public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [C PATCH] qualifiers of pointers to arrays in C2X [PR 98397]
@ 2021-05-24  6:05 Uecker, Martin
  2021-06-11 19:25 ` Uecker, Martin
  2021-08-12 16:58 ` Joseph Myers
  0 siblings, 2 replies; 9+ messages in thread
From: Uecker, Martin @ 2021-05-24  6:05 UTC (permalink / raw)
  To: gcc-patches; +Cc: joseph


Hi Joseph,

I found some time to update this patch. The only real change
of the patch is the qualifier in the conditional expression for
pointer to arrays in C2X. All the rest are the warnings,
which were wrong in the last version.

I hope I got this correct this time in combination with
-pedantic-errors and -Wc11-c2x-compat. 

Martin


2021-05-16  Martin Uecker  <muecker@gwdg.de>
    
gcc/c/
     PR c/98397
     * c-typeck.c (comp_target_types): Change pedwarn to pedwarn_c11
     for pointers to arrays with qualifiers.
     (build_conditional_expr): For C23 don't lose qualifiers for pointers
     to arrays when the other pointer is a void pointer. Update warnings.
     (convert_for_assignment): Update warnings for C2X when converting from
     void* with qualifiers to a pointer to array with the same qualifiers.

gcc/testsuite/
     PR c/98397
     * gcc.dg/c11-qual-1.c: New test.
     * gcc.dg/c2x-qual-1.c: New test.
     * gcc.dg/c2x-qual-2.c: New test.
     * gcc.dg/c2x-qual-3.c: New test.
     * gcc.dg/c2x-qual-4.c: New test.
     * gcc.dg/c2x-qual-5.c: New test.
     * gcc.dg/c2x-qual-6.c: New test.
     * gcc.dg/pointer-array-quals-1.c: Remove unnecessary flag.
     * gcc.dg/pointer-array-quals-2.c: Remove unnecessary flag.


diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index fc64ef96fb8..5b13656c090 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -1328,8 +1328,8 @@ comp_target_types (location_t location, tree ttl, tree ttr)
   val = comptypes_check_enum_int (mvl, mvr, &enum_and_int_p);
 
   if (val == 1 && val_ped != 1)
-    pedwarn (location, OPT_Wpedantic, "pointers to arrays with different qualifiers "
-                                      "are incompatible in ISO C");
+    pedwarn_c11 (location, OPT_Wpedantic, "invalid use of pointers to arrays with different qualifiers "
+					  "in ISO C before C2X");
 
   if (val == 2)
     pedwarn (location, OPT_Wpedantic, "types are not quite compatible");
@@ -5396,39 +5396,40 @@ build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp,
 		    "used in conditional expression");
 	  return error_mark_node;
 	}
-      else if (VOID_TYPE_P (TREE_TYPE (type1))
-	       && !TYPE_ATOMIC (TREE_TYPE (type1)))
-	{
-	  if ((TREE_CODE (TREE_TYPE (type2)) == ARRAY_TYPE)
-	      && (TYPE_QUALS (strip_array_types (TREE_TYPE (type2)))
-		  & ~TYPE_QUALS (TREE_TYPE (type1))))
-	    warning_at (colon_loc, OPT_Wdiscarded_array_qualifiers,
-			"pointer to array loses qualifier "
-			"in conditional expression");
-
-	  if (TREE_CODE (TREE_TYPE (type2)) == FUNCTION_TYPE)
+      else if ((VOID_TYPE_P (TREE_TYPE (type1))
+		&& !TYPE_ATOMIC (TREE_TYPE (type1)))
+	       || (VOID_TYPE_P (TREE_TYPE (type2))
+		   && !TYPE_ATOMIC (TREE_TYPE (type2))))
+	{
+	  tree t1 = TREE_TYPE (type1);
+	  tree t2 = TREE_TYPE (type2);
+	  if (!VOID_TYPE_P (t1))
+	   {
+	     /* roles are swapped */
+	     t1 = t2;
+	     t2 = TREE_TYPE (type1);
+	   }
+	  tree t2_stripped = strip_array_types (t2);
+	  if ((TREE_CODE (t2) == ARRAY_TYPE)
+	      && (TYPE_QUALS (t2_stripped) & ~TYPE_QUALS (t1)))
+	    {
+	      if (!flag_isoc2x)
+		warning_at (colon_loc, OPT_Wdiscarded_array_qualifiers,
+			    "pointer to array loses qualifier "
+			    "in conditional expression");
+	      else if (warn_c11_c2x_compat > 0)
+		warning_at (colon_loc, OPT_Wc11_c2x_compat,
+			    "pointer to array loses qualifier "
+			    "in conditional expression in ISO C before C2X");
+	    }
+	  if (TREE_CODE (t2) == FUNCTION_TYPE)
 	    pedwarn (colon_loc, OPT_Wpedantic,
 		     "ISO C forbids conditional expr between "
 		     "%<void *%> and function pointer");
-	  result_type = build_pointer_type (qualify_type (TREE_TYPE (type1),
-							  TREE_TYPE (type2)));
-	}
-      else if (VOID_TYPE_P (TREE_TYPE (type2))
-	       && !TYPE_ATOMIC (TREE_TYPE (type2)))
-	{
-	  if ((TREE_CODE (TREE_TYPE (type1)) == ARRAY_TYPE)
-	      && (TYPE_QUALS (strip_array_types (TREE_TYPE (type1)))
-		  & ~TYPE_QUALS (TREE_TYPE (type2))))
-	    warning_at (colon_loc, OPT_Wdiscarded_array_qualifiers,
-			"pointer to array loses qualifier "
-			"in conditional expression");
-
-	  if (TREE_CODE (TREE_TYPE (type1)) == FUNCTION_TYPE)
-	    pedwarn (colon_loc, OPT_Wpedantic,
-		     "ISO C forbids conditional expr between "
-		     "%<void *%> and function pointer");
-	  result_type = build_pointer_type (qualify_type (TREE_TYPE (type2),
-							  TREE_TYPE (type1)));
+	  /* for array, use qualifiers of element type */
+	  if (flag_isoc2x)
+	    t2 = t2_stripped;
+	  result_type = build_pointer_type (qualify_type (t1, t2));
 	}
       /* Objective-C pointer comparisons are a bit more lenient.  */
       else if (objc_have_common_type (type1, type2, -3, NULL_TREE))
@@ -6786,27 +6787,40 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
 
   /* This macro is used to emit diagnostics to ensure that all format
      strings are complete sentences, visible to gettext and checked at
-     compile time.  It is the same as PEDWARN_FOR_ASSIGNMENT but with an
-     extra parameter to enumerate qualifiers.  */
-#define PEDWARN_FOR_QUALIFIERS(LOCATION, PLOC, OPT, AR, AS, IN, RE, QUALS) \
+     compile time.  It can be called with 'pedwarn' or 'warning_at'.  */
+#define WARNING_FOR_QUALIFIERS(PEDWARN, LOCATION, PLOC, OPT, AR, AS, IN, RE, QUALS) \
   do {                                                                   \
     switch (errtype)                                                     \
       {                                                                  \
       case ic_argpass:                                                   \
-	{								\
-	auto_diagnostic_group d;						\
-	if (pedwarn (PLOC, OPT, AR, parmnum, rname, QUALS))		\
-	  inform_for_arg (fundecl, (PLOC), parmnum, type, rhstype);	\
-	}								\
+	{								 \
+	  auto_diagnostic_group d;					 \
+	  if (PEDWARN) {						 \
+	    if (pedwarn (PLOC, OPT, AR, parmnum, rname, QUALS))          \
+	      inform_for_arg (fundecl, (PLOC), parmnum, type, rhstype);  \
+	  } else {							 \
+	    if (warning_at (PLOC, OPT, AR, parmnum, rname, QUALS))	 \
+	      inform_for_arg (fundecl, (PLOC), parmnum, type, rhstype);  \
+	  }								 \
+	}								 \
         break;                                                           \
       case ic_assign:                                                    \
-        pedwarn (LOCATION, OPT, AS, QUALS);				 \
+	if (PEDWARN)							 \
+	  pedwarn (LOCATION, OPT, AS, QUALS);                            \
+	else								 \
+	  warning_at (LOCATION, OPT, AS, QUALS);                         \
         break;                                                           \
       case ic_init:                                                      \
-        pedwarn (LOCATION, OPT, IN, QUALS);				 \
+	if (PEDWARN)							 \
+	  pedwarn (LOCATION, OPT, IN, QUALS);                            \
+	else								 \
+	  warning_at (LOCATION, OPT, IN, QUALS);                         \
         break;                                                           \
       case ic_return:                                                    \
-        pedwarn (LOCATION, OPT, RE, QUALS);				 \
+	if (PEDWARN)							 \
+	  pedwarn (LOCATION, OPT, RE, QUALS);                            \
+	else								 \
+	  warning_at (LOCATION, OPT, RE, QUALS);                         \
         break;                                                           \
       default:                                                           \
         gcc_unreachable ();                                              \
@@ -6815,32 +6829,11 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
 
   /* This macro is used to emit diagnostics to ensure that all format
      strings are complete sentences, visible to gettext and checked at
-     compile time.  It is the same as PEDWARN_FOR_QUALIFIERS but uses
-     warning_at instead of pedwarn.  */
-#define WARNING_FOR_QUALIFIERS(LOCATION, PLOC, OPT, AR, AS, IN, RE, QUALS) \
-  do {                                                                   \
-    switch (errtype)                                                     \
-      {                                                                  \
-      case ic_argpass:                                                   \
-	{								\
-	  auto_diagnostic_group d;						\
-	  if (warning_at (PLOC, OPT, AR, parmnum, rname, QUALS))	\
-	    inform_for_arg (fundecl, (PLOC), parmnum, type, rhstype); \
-	}								\
-        break;                                                           \
-      case ic_assign:                                                    \
-        warning_at (LOCATION, OPT, AS, QUALS);                           \
-        break;                                                           \
-      case ic_init:                                                      \
-        warning_at (LOCATION, OPT, IN, QUALS);                           \
-        break;                                                           \
-      case ic_return:                                                    \
-        warning_at (LOCATION, OPT, RE, QUALS);                           \
-        break;                                                           \
-      default:                                                           \
-        gcc_unreachable ();                                              \
-      }                                                                  \
-  } while (0)
+     compile time.  It is the same as PEDWARN_FOR_ASSIGNMENT but with an
+     extra parameter to enumerate qualifiers.  */
+#define PEDWARN_FOR_QUALIFIERS(LOCATION, PLOC, OPT, AR, AS, IN, RE, QUALS) \
+   WARNING_FOR_QUALIFIERS (true, LOCATION, PLOC, OPT, AR, AS, IN, RE, QUALS)
+
 
   if (TREE_CODE (rhs) == EXCESS_PRECISION_EXPR)
     rhs = TREE_OPERAND (rhs, 0);
@@ -7348,17 +7341,18 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
 
 	      if (TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttr)
 		  & ~TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttl))
-		WARNING_FOR_QUALIFIERS (location, expr_loc,
-				        OPT_Wdiscarded_array_qualifiers,
-				        G_("passing argument %d of %qE discards "
+		WARNING_FOR_QUALIFIERS (flag_isoc2x,
+					location, expr_loc,
+					OPT_Wdiscarded_array_qualifiers,
+					G_("passing argument %d of %qE discards "
 					   "%qv qualifier from pointer target type"),
-				        G_("assignment discards %qv qualifier "
+					G_("assignment discards %qv qualifier "
 					   "from pointer target type"),
-				        G_("initialization discards %qv qualifier "
+					G_("initialization discards %qv qualifier "
 					   "from pointer target type"),
-				        G_("return discards %qv qualifier from "
+					G_("return discards %qv qualifier from "
 					   "pointer target type"),
-                                        TYPE_QUALS (ttr) & ~TYPE_QUALS (ttl));
+					TYPE_QUALS (ttr) & ~TYPE_QUALS (ttl));
             }
           else if (pedantic
 	      && ((VOID_TYPE_P (ttl) && TREE_CODE (ttr) == FUNCTION_TYPE)
@@ -7381,28 +7375,31 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
 	  else if (TREE_CODE (ttr) != FUNCTION_TYPE
 		   && TREE_CODE (ttl) != FUNCTION_TYPE)
 	    {
+	       /* Assignments between atomic and non-atomic objects are OK.  */
+	       bool warn_quals_ped = TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttr)
+				     & ~TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttl);
+	       bool warn_quals = TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttr)
+				 & ~TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (strip_array_types (ttl));
+
 	      /* Don't warn about loss of qualifier for conversions from
 		 qualified void* to pointers to arrays with corresponding
-		 qualifier on the element type. */
-	      if (!pedantic)
-	        ttl = strip_array_types (ttl);
+		 qualifier on the element type (except for pedantic before C23). */
+	      if (warn_quals || (warn_quals_ped && pedantic && !flag_isoc2x))
+		PEDWARN_FOR_QUALIFIERS (location, expr_loc,
+					OPT_Wdiscarded_qualifiers,
+					G_("passing argument %d of %qE discards "
+					   "%qv qualifier from pointer target type"),
+					G_("assignment discards %qv qualifier "
+					   "from pointer target type"),
+					G_("initialization discards %qv qualifier "
+					   "from pointer target type"),
+					G_("return discards %qv qualifier from "
+					   "pointer target type"),
+					TYPE_QUALS (ttr) & ~TYPE_QUALS (ttl));
+	      else if (warn_quals_ped)
+		pedwarn_c11 (location, OPT_Wc11_c2x_compat,
+			     "array with qualifier on the element is not qualified before C2X");
 
-	      /* Assignments between atomic and non-atomic objects are OK.  */
-	      if (TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttr)
-		  & ~TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttl))
-		{
-		  PEDWARN_FOR_QUALIFIERS (location, expr_loc,
-				          OPT_Wdiscarded_qualifiers,
-				          G_("passing argument %d of %qE discards "
-					     "%qv qualifier from pointer target type"),
-				          G_("assignment discards %qv qualifier "
-					     "from pointer target type"),
-				          G_("initialization discards %qv qualifier "
-					     "from pointer target type"),
-				          G_("return discards %qv qualifier from "
-					     "pointer target type"),
-				          TYPE_QUALS (ttr) & ~TYPE_QUALS (ttl));
-		}
 	      /* If this is not a case of ignoring a mismatch in signedness,
 		 no warning.  */
 	      else if (VOID_TYPE_P (ttl) || VOID_TYPE_P (ttr)
diff --git a/gcc/testsuite/gcc.dg/c11-qual-1.c b/gcc/testsuite/gcc.dg/c11-qual-1.c
new file mode 100644
index 00000000000..f731e068830
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c11-qual-1.c
@@ -0,0 +1,11 @@
+/* Test that qualifiers are lost in tertiary operator for pointers to arrays before C2X, PR98397 */
+/* { dg-do compile } */
+/* { dg-options "-std=c11 -pedantic-errors -Wno-discarded-array-qualifiers" } */
+
+void foo(void)
+{
+	const int (*u)[1];
+	void *v;
+	_Static_assert(_Generic(1 ? u : v, const void*: 0, void*: 1), "qualifier not lost");
+	_Static_assert(_Generic(1 ? v : u, const void*: 0, void*: 1), "qualifier not lost");
+}
diff --git a/gcc/testsuite/gcc.dg/c2x-qual-1.c b/gcc/testsuite/gcc.dg/c2x-qual-1.c
new file mode 100644
index 00000000000..4d33db1907d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-qual-1.c
@@ -0,0 +1,30 @@
+/* Tests related to qualifiers and pointers to arrays in C2X, PR98397 */
+/* { dg-do compile } */
+/* { dg-options "-std=c2x -pedantic-errors" } */
+
+/* test that qualifiers are preserved in tertiary operator for pointers to arrays in C2X */
+
+void f(void)
+{
+	const int (*u)[1];
+	void *v;
+	_Static_assert(_Generic(1 ? u : v, const void*: 1, void*: 0), "lost qualifier");
+	_Static_assert(_Generic(1 ? v : u, const void*: 1, void*: 0), "lost qualifier");
+}
+
+/* test that assignment of unqualified to qualified pointers works as expected */
+
+void g(void)
+{
+	int (*x)[3];
+	const int (*p)[3] = x;
+}
+
+/* test that assignment of qualified void pointers works as expected */
+
+void h(void)
+{
+	const void* x;
+	const int (*p)[3] = x;
+}
+
diff --git a/gcc/testsuite/gcc.dg/c2x-qual-2.c b/gcc/testsuite/gcc.dg/c2x-qual-2.c
new file mode 100644
index 00000000000..f60a5b18faa
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-qual-2.c
@@ -0,0 +1,30 @@
+/* Tests related to qualifiers and pointers to arrays in C2X, PR98397 */
+/* { dg-do compile } */
+/* { dg-options "-std=c2x -Wc11-c2x-compat" } */
+
+/* test that qualifiers are preserved in tertiary operator for pointers to arrays in C2X */
+
+void f(void)
+{
+	const int (*u)[1];
+	void *v;
+	_Static_assert(_Generic(1 ? u : v, const void*: 1, void*: 0), "lost qualifier");	/* { dg-warning "pointer to array loses qualifier in conditional" } */
+	_Static_assert(_Generic(1 ? v : u, const void*: 1, void*: 0), "lost qualifier");	/* { dg-warning "pointer to array loses qualifier in conditional" } */
+}
+
+/* test that assignment of unqualified to qualified pointers works as expected */
+
+void g(void)
+{
+	int (*x)[3];
+	const int (*p)[3] = x; /* { dg-warning "arrays with different qualifiers"  } */
+}
+
+/* test that assignment of qualified void pointers works as expected */
+
+void h(void)
+{
+	const void* x;
+	const int (*p)[3] = x; /* { dg-warning "array with qualifier on the element is not qualified before C2X" } */
+}
+
diff --git a/gcc/testsuite/gcc.dg/c2x-qual-3.c b/gcc/testsuite/gcc.dg/c2x-qual-3.c
new file mode 100644
index 00000000000..31896fcb1a1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-qual-3.c
@@ -0,0 +1,30 @@
+/* Tests related to qualifiers and pointers to arrays in C2X, PR98397 */
+/* { dg-do compile } */
+/* { dg-options "-std=c2x -Wc11-c2x-compat -pedantic-errors" } */
+
+/* test that qualifiers are preserved in tertiary operator for pointers to arrays in C2X */
+
+void f(void)
+{
+	const int (*u)[1];
+	void *v;
+	_Static_assert(_Generic(1 ? u : v, const void*: 1, void*: 0), "lost qualifier");	/* { dg-warning "pointer to array loses qualifier in conditional" } */
+	_Static_assert(_Generic(1 ? v : u, const void*: 1, void*: 0), "lost qualifier");	/* { dg-warning "pointer to array loses qualifier in conditional" } */
+}
+
+/* test that assignment of unqualified to qualified pointers works as expected */
+
+void g(void)
+{
+	int (*x)[3];
+	const int (*p)[3] = x; /* { dg-warning "arrays with different qualifiers"  } */
+}
+
+/* test that assignment of qualified void pointers works as expected */
+
+void h(void)
+{
+	const void* x;
+	const int (*p)[3] = x; /* { dg-warning "array with qualifier on the element is not qualified before C2X" } */
+}
+
diff --git a/gcc/testsuite/gcc.dg/c2x-qual-4.c b/gcc/testsuite/gcc.dg/c2x-qual-4.c
new file mode 100644
index 00000000000..93b4723dcd6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-qual-4.c
@@ -0,0 +1,105 @@
+/* { dg-do compile } */
+/* { dg-options "-std=c2x" } */
+void tvoid(void* x);
+void transpose0(double* out, const double* in) { }
+void transpose1(double out[2][2], const double in[2][2]) { }
+void transpose2(double out[2][2][2], const double in[2][2][2]) { }
+// return
+int (*y2(const int x[3][3]))[3] { return x; } /* { dg-warning "return discards 'const' qualifier from pointer target type" } */
+const int (*y3(int x[3][3]))[3] { return x; }
+void test(void)
+{
+	double x0[2];
+	double y0[2];
+	const double z0[4];
+	double x1[2][2];
+	double y1[2][2];
+	double o1[2][3];
+	const double z1[2][2];
+	double x2[2][2][2];
+	double y2[2][2][2];
+	double o2[2][2][3];
+	const double z2[2][2][2];
+	// void pointers
+	tvoid(x0);
+	tvoid(x1);
+	tvoid(x2);
+	tvoid(z0); /* { dg-warning "passing argument 1 of 'tvoid' discards 'const' qualifier from pointer target type" } */
+	tvoid(z1); /* { dg-warning "passing argument 1 of 'tvoid' discards 'const' qualifier from pointer target type" } */
+	tvoid(z2); /* { dg-warning "passing argument 1 of 'tvoid' discards 'const' qualifier from pointer target type" } */
+	void* p;
+	const void* pc;
+	p = x0;
+	p = x1;
+	p = x2;
+	p = z0; /* { dg-warning "assignment discards 'const' qualifier from pointer target type" } */
+	p = z1; /* { dg-warning "assignment discards 'const' qualifier from pointer target type" } */
+	p = z2; /* { dg-warning "assignment discards 'const' qualifier from pointer target type" } */
+	pc = x0;
+	pc = x1;
+	pc = x2;
+	pc = z0;
+	pc = z1;
+	pc = z2;
+	transpose0(pc, p); /* { dg-warning "passing argument 1 of 'transpose0' discards 'const' qualifier from pointer target type" } */
+	transpose1(pc, p); /* { dg-warning "passing argument 1 of 'transpose1' discards 'const' qualifier from pointer target type" } */
+	transpose2(pc, p); /* { dg-warning "passing argument 1 of 'transpose2' discards 'const' qualifier from pointer target type" } */
+	transpose0(p, pc);
+	transpose1(p, pc);
+	transpose2(p, pc);
+	// passing as arguments
+	transpose0(y0, x0);
+	transpose1(y1, x1);
+	transpose2(y2, x2);
+	// initialization
+	const double (*u0p) = x0;
+	const double (*u1p)[2] = x1;
+	const double (*u2p)[2][2] = x2;
+	double (*v0p) = z0; /* { dg-warning "initialization discards 'const' qualifier from pointer target type" } */
+	double (*v1p)[2] = z1; /* { dg-warning "initialization discards 'const' qualifier from pointer target type" } */
+	double (*v2p)[2][2] = z2; /* { dg-warning "initialization discards 'const' qualifier from pointer target type" } */
+	// subtraction
+	&(x0[1]) - &(z0[0]);
+	&(x1[1]) - &(z1[0]);
+	&(x2[1]) - &(z2[0]);
+	// comparison
+	x0 == z0;
+	x1 == z1;
+	x2 == z2;
+	x0 < z0; 
+	x1 < z1; 
+	x2 < z2; 
+	x0 > z0;
+	x1 > z1;
+	x2 > z2;
+	// assignment
+	u0p = x0;
+	u1p = x1;
+	u2p = x2;
+	v0p = z0; /* { dg-warning "assignment discards 'const' qualifier from pointer target type" } */
+	v1p = z1; /* { dg-warning "assignment discards 'const' qualifier from pointer target type" } */
+	v2p = z2; /* { dg-warning "assignment discards 'const' qualifier from pointer target type" } */
+	// conditional expressions
+	(void)(1 ? x0 : z0);
+	(void)(1 ? x1 : z1);
+	(void)(1 ? x2 : z2);
+	(void)(1 ? x0 : x1); /* { dg-warning "pointer type mismatch in conditional expression" } */
+	(void)(1 ? x1 : x2); /* { dg-warning "pointer type mismatch in conditional expression" } */
+	(void)(1 ? x2 : x0); /* { dg-warning "pointer type mismatch in conditional expression" } */
+	v0p = (1 ? z0 : v0p); /* { dg-warning "assignment discards 'const' qualifier from pointer target type" } */
+	v1p = (1 ? z1 : v1p); /* { dg-warning "assignment discards 'const' qualifier from pointer target type" } */
+	v2p = (1 ? z2 : v2p); /* { dg-warning "assignment discards 'const' qualifier from pointer target type" } */
+	v0p = (1 ? x0 : u0p); /* { dg-warning "assignment discards 'const' qualifier from pointer target type" } */
+	v1p = (1 ? x1 : u1p); /* { dg-warning "assignment discards 'const' qualifier from pointer target type" } */
+	v2p = (1 ? x2 : u2p); /* { dg-warning "assignment discards 'const' qualifier from pointer target type" } */
+	(1 ? x0 : z0)[0] = 1; /* { dg-error "assignment of read-only location" } */
+	(1 ? x1 : z1)[0][0] = 1; /* { dg-error "assignment of read-only location" } */
+	(1 ? x2 : z2)[0][0][0] = 1; /* { dg-error "assignment of read-only location" } */
+	v0p = (1 ? p : z0); /* { dg-warning "assignment discards 'const' qualifier from pointer target type" } */
+	v1p = (1 ? p : z1); /* { dg-warning "assignment discards 'const' qualifier from pointer target type" } */
+	v2p = (1 ? p : z2); /* { dg-warning "assignment discards 'const' qualifier from pointer target type" } */
+	v0p = (1 ? pc : x0); /* { dg-warning "assignment discards 'const' qualifier from pointer target type" } */
+	v1p = (1 ? pc : x1); /* { dg-warning "assignment discards 'const' qualifier from pointer target type" } */
+	v2p = (1 ? pc : x2); /* { dg-warning "assignment discards 'const' qualifier from pointer target type" } */
+}
+
diff --git a/gcc/testsuite/gcc.dg/c2x-qual-5.c b/gcc/testsuite/gcc.dg/c2x-qual-5.c
new file mode 100644
index 00000000000..0801fa0eed5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-qual-5.c
@@ -0,0 +1,101 @@
+/* { dg-do compile } */
+/* { dg-options "-std=c2x -pedantic-errors" } */
+void tvoid(void* x);
+void transpose0(double* out, const double* in) { }
+void transpose1(double out[2][2], const double in[2][2]) { }
+void transpose2(double out[2][2][2], const double in[2][2][2]) { }
+// return
+int (*x2(const int x[3][3]))[3] { return x; } /* { dg-error "return discards" } */
+const int (*x3(int x[3][3]))[3] { return x; }
+void test(void)
+{
+	double x0[2];
+	double y0[2];
+	const double z0[4];
+	double x1[2][2];
+	double y1[2][2];
+	double o1[2][3];
+	const double z1[2][2];
+	double x2[2][2][2];
+	double y2[2][2][2];
+	double o2[2][2][3];
+	const double z2[2][2][2];
+	// void pointers
+	tvoid(z0); /* { dg-error "passing argument 1 of 'tvoid' discards 'const' qualifier from pointer target type" } */
+	tvoid(z1); /* { dg-error "passing argument 1 of 'tvoid' discards 'const' qualifier from pointer target type" } */
+	tvoid(z2); /* { dg-error "passing argument 1 of 'tvoid' discards 'const' qualifier from pointer target type" } */
+	void* p;
+	const void* pc;
+	p = x0;
+	p = x1;
+	p = x2;
+	p = z0; /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
+	p = z1; /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
+	p = z2; /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
+	pc = x0;
+	pc = x1;
+	pc = x2;
+	pc = z0;
+	pc = z1;
+	pc = z2;
+	transpose0(pc, p); /* { dg-error "passing argument 1 of 'transpose0' discards 'const' qualifier from pointer target type" } */
+	transpose1(pc, p); /* { dg-error "passing argument 1 of 'transpose1' discards 'const' qualifier from pointer target type" } */
+	transpose2(pc, p); /* { dg-error "passing argument 1 of 'transpose2' discards 'const' qualifier from pointer target type" } */
+	transpose0(p, pc);
+	transpose1(p, pc);
+	transpose2(p, pc);
+	// passing as arguments
+	transpose0(y0, x0);
+	transpose1(y1, o1); /* { dg-error "passing argument 2 of 'transpose1' from incompatible pointer type" } */
+	transpose1(y1, x1);
+	transpose2(y2, o2); /* { dg-error "passing argument 2 of 'transpose2' from incompatible pointer type" } */
+	transpose2(y2, x2);
+	// initialization
+	const double (*x0p) = x0;
+	const double (*x1p)[2] = x1;
+	const double (*x2p)[2][2] = x2;
+	double (*v0p) = z0; /* { dg-error "initialization discards 'const' qualifier from pointer target type" } */
+	double (*v1p)[2] = z1; /* { dg-error "initialization discards" } */
+	double (*v2p)[2][2] = z2; /* { dg-error "initialization discards" } */
+	// assignment
+	x0p = x0;
+	x1p = x1;
+	x2p = x2;
+	// subtraction
+	&(x0[1]) - &(z0[0]);
+	&(x1[1]) - &(z1[0]);
+	&(x2[1]) - &(z2[0]);
+	// comparison
+	x0 == z0;
+	x1 == z1;
+	x2 == z2;
+	x0 < z0;
+	x1 < z1;
+	x2 < z2;
+	x0 > z0;
+	x1 > z1;
+	x2 > z2;
+	// conditional expressions
+	(void)(1 ? x0 : z0);
+	(void)(1 ? x1 : z1);
+	(void)(1 ? x2 : z2);
+	(void)(1 ? x0 : x1); /* { dg-error "pointer type mismatch in conditional expression" } */
+	(void)(1 ? x1 : x2); /* { dg-error "pointer type mismatch in conditional expression" } */
+	(void)(1 ? x2 : x0); /* { dg-error "pointer type mismatch in conditional expression" } */
+	v0p = (1 ? z0 : v0p); /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
+	v1p = (1 ? z1 : v1p); /* { dg-error "assignment discards" } */
+	v2p = (1 ? z2 : v2p); /* { dg-error "assignment discards" } */
+	v0p = (1 ? x0 : x0p); /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
+	v1p = (1 ? x1 : x1p); /* { dg-error "assignment discards" } */
+	v2p = (1 ? x2 : x2p); /* { dg-error "assignment discards" } */
+	(1 ? x0 : z0)[0] = 1; /* { dg-error "assignment of read-only location" } */
+	(1 ? x1 : z1)[0][0] = 1; /* { dg-error "assignment of read-only location" } */
+	(1 ? x2 : z2)[0][0][0] = 1; /* { dg-error "assignment of read-only location" } */
+	v0p = (1 ? p : z0); /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
+	v1p = (1 ? p : z1); /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
+	v2p = (1 ? p : z2); /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
+	v0p = (1 ? pc : x0); /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
+	v1p = (1 ? pc : x1); /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
+	v2p = (1 ? pc : x2); /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
+}
+
diff --git a/gcc/testsuite/gcc.dg/c2x-qual-6.c b/gcc/testsuite/gcc.dg/c2x-qual-6.c
new file mode 100644
index 00000000000..9c91e206e30
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-qual-6.c
@@ -0,0 +1,114 @@
+/* { dg-do compile } */
+/* { dg-options "-std=c2x -Wc11-c2x-compat -pedantic-errors" } */
+void tvoid(void* x);
+void transpose0(double* out, const double* in) { }
+void transpose1(double out[2][2], const double in[2][2]) { }
+void transpose2(double out[2][2][2], const double in[2][2][2]) { }
+// return
+int (*x2(const int x[3][3]))[3] { return x; } /* { dg-warning "before C2X" } */
+						/* { dg-error "return discards" "" { target *-*-* } .-1 } */
+const int (*x3(int x[3][3]))[3] { return x; }	/* { dg-warning "before C2X" } */
+void test(void)
+{
+	double x0[2];
+	double y0[2];
+	const double z0[4];
+	double x1[2][2];
+	double y1[2][2];
+	double o1[2][3];
+	const double z1[2][2];
+	double x2[2][2][2];
+	double y2[2][2][2];
+	double o2[2][2][3];
+	const double z2[2][2][2];
+	// void pointers
+	tvoid(z0); /* { dg-error "passing argument 1 of 'tvoid' discards 'const' qualifier from pointer target type" } */
+	tvoid(z1); /* { dg-error "passing argument 1 of 'tvoid' discards 'const' qualifier from pointer target type" } */
+	tvoid(z2); /* { dg-error "passing argument 1 of 'tvoid' discards 'const' qualifier from pointer target type" } */
+	void* p;
+	const void* pc;
+	p = x0;
+	p = x1;
+	p = x2;
+	p = z0; /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
+	p = z1; /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
+	p = z2; /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
+	pc = x0;
+	pc = x1;
+	pc = x2;
+	pc = z0;
+	pc = z1;
+	pc = z2;
+	transpose0(pc, p); /* { dg-error "passing argument 1 of 'transpose0' discards 'const' qualifier from pointer target type" } */
+	transpose1(pc, p); /* { dg-error "passing argument 1 of 'transpose1' discards 'const' qualifier from pointer target type" } */
+	transpose2(pc, p); /* { dg-error "passing argument 1 of 'transpose2' discards 'const' qualifier from pointer target type" } */
+	transpose0(p, pc); 
+	transpose1(p, pc); /* { dg-warning "before C2X" } */
+	transpose2(p, pc); /* { dg-warning "before C2X" } */
+	// passing as arguments
+	transpose0(y0, x0);
+	transpose1(y1, o1); /* { dg-error "passing argument 2 of 'transpose1' from incompatible pointer type" } */
+	transpose1(y1, x1); /* { dg-warning "before C2X" } */
+	transpose2(y2, o2); /* { dg-error "passing argument 2 of 'transpose2' from incompatible pointer type" } */
+	transpose2(y2, x2); /* { dg-warning "before C2X" } */
+	// initialization
+	const double (*x0p) = x0;
+	const double (*x1p)[2] = x1; /* { dg-warning "before C2X" } */
+	const double (*x2p)[2][2] = x2; /* { dg-warning "before C2X" } */
+	double (*v0p) = z0; /* { dg-error "initialization discards 'const' qualifier from pointer target type" } */
+	double (*v1p)[2] = z1; /* { dg-warning "before C2X" } */
+				/* { dg-error "initialization discards" "" { target *-*-* } .-1 } */
+	double (*v2p)[2][2] = z2; /* { dg-warning "before C2X" } */
+				/* { dg-error "initialization discards" "" { target *-*-* } .-1 } */
+				
+	// assignment
+	x0p = x0;
+	x1p = x1; /* { dg-warning "before C2X" } */
+	x2p = x2; /* { dg-warning "before C2X" } */
+
+	// subtraction
+	&(x0[1]) - &(z0[0]);
+	&(x1[1]) - &(z1[0]); /* { dg-warning "before C2X" } */
+	&(x2[1]) - &(z2[0]); /* { dg-warning "before C2X" } */
+	// comparison
+	x0 == z0;
+	x1 == z1; /* { dg-warning "before C2X" } */
+	x2 == z2; /* { dg-warning "before C2X" } */
+	x0 < z0;
+	x1 < z1; /* { dg-warning "before C2X" } */
+	x2 < z2; /* { dg-warning "before C2X" } */
+	x0 > z0;
+	x1 > z1; /* { dg-warning "before C2X" } */
+	x2 > z2; /* { dg-warning "before C2X" } */
+	// conditional expressions
+	(void)(1 ? x0 : z0);
+	(void)(1 ? x1 : z1); /* { dg-warning "before C2X" } */
+	(void)(1 ? x2 : z2); /* { dg-warning "before C2X" } */
+	(void)(1 ? x0 : x1); /* { dg-error "pointer type mismatch in conditional expression" } */
+	(void)(1 ? x1 : x2); /* { dg-error "pointer type mismatch in conditional expression" } */
+	(void)(1 ? x2 : x0); /* { dg-error "pointer type mismatch in conditional expression" } */
+	v0p = (1 ? z0 : v0p); /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
+	v1p = (1 ? z1 : v1p); /* { dg-warning "before C2X" } */
+				/* { dg-error "assignment discards" "" { target *-*-* } .-1 } */
+	v2p = (1 ? z2 : v2p); /* { dg-warning "before C2X" } */
+				/* { dg-error "assignment discards" "" { target *-*-* } .-1 } */
+	v0p = (1 ? x0 : x0p); /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
+	v1p = (1 ? x1 : x1p); /* { dg-error "assignment discards" } */
+				/* { dg-warning "before C2X" "" { target *-*-* } .-1 } */
+	v2p = (1 ? x2 : x2p); /* { dg-error "assignment discards" } */
+				/* { dg-warning "before C2X" "" { target *-*-* } .-1 } */
+	(1 ? x0 : z0)[0] = 1; /* { dg-error "assignment of read-only location" } */
+	(1 ? x1 : z1)[0][0] = 1; /* { dg-error "assignment of read-only location" } */
+				/* { dg-warning "before C2X" "" { target *-*-* } .-1 } */
+	(1 ? x2 : z2)[0][0][0] = 1; /* { dg-error "assignment of read-only location" } */
+				/* { dg-warning "before C2X" "" { target *-*-* } .-1 } */
+	v0p = (1 ? p : z0); /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
+	v1p = (1 ? p : z1); /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
+				/* { dg-warning "before C2X" "" { target *-*-* } .-1 } */
+	v2p = (1 ? p : z2); /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
+				/* { dg-warning "before C2X" "" { target *-*-* } .-1 } */
+	v0p = (1 ? pc : x0); /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
+	v1p = (1 ? pc : x1); /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
+	v2p = (1 ? pc : x2); /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
+}
+
diff --git a/gcc/testsuite/gcc.dg/pointer-array-quals-1.c b/gcc/testsuite/gcc.dg/pointer-array-quals-1.c
index 921a37e9e0d..498ab223162 100644
--- a/gcc/testsuite/gcc.dg/pointer-array-quals-1.c
+++ b/gcc/testsuite/gcc.dg/pointer-array-quals-1.c
@@ -1,6 +1,6 @@
 /* { dg-do compile } */
 /* Origin: Martin Uecker <uecker@eecs.berkeley.edu> */
-/* { dg-options "-Wdiscarded-array-qualifiers" } */
+/* { dg-options "" } */
 void tvoid(void* x);
 void transpose0(double* out, const double* in) { }
 void transpose1(double out[2][2], const double in[2][2]) { }
diff --git a/gcc/testsuite/gcc.dg/pointer-array-quals-2.c b/gcc/testsuite/gcc.dg/pointer-array-quals-2.c
index 30689c7312d..4c95d8a3a78 100644
--- a/gcc/testsuite/gcc.dg/pointer-array-quals-2.c
+++ b/gcc/testsuite/gcc.dg/pointer-array-quals-2.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-Wdiscarded-array-qualifiers -pedantic-errors" } */
+/* { dg-options "-pedantic-errors" } */
 /* Origin: Martin Uecker <uecker@eecs.berkeley.edu> */
 void tvoid(void* x);
 void transpose0(double* out, const double* in) { }

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [C PATCH] qualifiers of pointers to arrays in C2X [PR 98397]
  2021-05-24  6:05 [C PATCH] qualifiers of pointers to arrays in C2X [PR 98397] Uecker, Martin
@ 2021-06-11 19:25 ` Uecker, Martin
  2021-08-01  7:20   ` PING^2 " Uecker, Martin
  2021-08-12 16:58 ` Joseph Myers
  1 sibling, 1 reply; 9+ messages in thread
From: Uecker, Martin @ 2021-06-11 19:25 UTC (permalink / raw)
  To: gcc-patches; +Cc: joseph


(PING. In case you missed this. Sorry, forgot to CC you.)

Am Montag, den 24.05.2021, 08:05 +0200 schrieb Martin Uecker:
> Hi Joseph,
> 
> I found some time to update this patch. The only real change
> of the patch is the qualifier in the conditional expression for
> pointer to arrays in C2X. All the rest are the warnings,
> which were wrong in the last version.
> 
> I hope I got this correct this time in combination with
> -pedantic-errors and -Wc11-c2x-compat. 
> 
> Martin
> 
> 
> 2021-05-16  Martin Uecker  <muecker@gwdg.de>
>     
> gcc/c/
>      PR c/98397
>      * c-typeck.c (comp_target_types): Change pedwarn to pedwarn_c11
>      for pointers to arrays with qualifiers.
>      (build_conditional_expr): For C23 don't lose qualifiers for pointers
>      to arrays when the other pointer is a void pointer. Update warnings.
>      (convert_for_assignment): Update warnings for C2X when converting from
>      void* with qualifiers to a pointer to array with the same qualifiers.
> 
> gcc/testsuite/
>      PR c/98397
>      * gcc.dg/c11-qual-1.c: New test.
>      * gcc.dg/c2x-qual-1.c: New test.
>      * gcc.dg/c2x-qual-2.c: New test.
>      * gcc.dg/c2x-qual-3.c: New test.
>      * gcc.dg/c2x-qual-4.c: New test.
>      * gcc.dg/c2x-qual-5.c: New test.
>      * gcc.dg/c2x-qual-6.c: New test.
>      * gcc.dg/pointer-array-quals-1.c: Remove unnecessary flag.
>      * gcc.dg/pointer-array-quals-2.c: Remove unnecessary flag.
> 
> 
> diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
> index fc64ef96fb8..5b13656c090 100644
> --- a/gcc/c/c-typeck.c
> +++ b/gcc/c/c-typeck.c
> @@ -1328,8 +1328,8 @@ comp_target_types (location_t location, tree ttl, tree ttr)
>    val = comptypes_check_enum_int (mvl, mvr, &enum_and_int_p);
>  
>    if (val == 1 && val_ped != 1)
> -    pedwarn (location, OPT_Wpedantic, "pointers to arrays with different qualifiers "
> -                                      "are incompatible in ISO C");
> +    pedwarn_c11 (location, OPT_Wpedantic, "invalid use of pointers to arrays with different
> qualifiers "
> +					  "in ISO C before C2X");
>  
>    if (val == 2)
>      pedwarn (location, OPT_Wpedantic, "types are not quite compatible");
> @@ -5396,39 +5396,40 @@ build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp,
>  		    "used in conditional expression");
>  	  return error_mark_node;
>  	}
> -      else if (VOID_TYPE_P (TREE_TYPE (type1))
> -	       && !TYPE_ATOMIC (TREE_TYPE (type1)))
> -	{
> -	  if ((TREE_CODE (TREE_TYPE (type2)) == ARRAY_TYPE)
> -	      && (TYPE_QUALS (strip_array_types (TREE_TYPE (type2)))
> -		  & ~TYPE_QUALS (TREE_TYPE (type1))))
> -	    warning_at (colon_loc, OPT_Wdiscarded_array_qualifiers,
> -			"pointer to array loses qualifier "
> -			"in conditional expression");
> -
> -	  if (TREE_CODE (TREE_TYPE (type2)) == FUNCTION_TYPE)
> +      else if ((VOID_TYPE_P (TREE_TYPE (type1))
> +		&& !TYPE_ATOMIC (TREE_TYPE (type1)))
> +	       || (VOID_TYPE_P (TREE_TYPE (type2))
> +		   && !TYPE_ATOMIC (TREE_TYPE (type2))))
> +	{
> +	  tree t1 = TREE_TYPE (type1);
> +	  tree t2 = TREE_TYPE (type2);
> +	  if (!VOID_TYPE_P (t1))
> +	   {
> +	     /* roles are swapped */
> +	     t1 = t2;
> +	     t2 = TREE_TYPE (type1);
> +	   }
> +	  tree t2_stripped = strip_array_types (t2);
> +	  if ((TREE_CODE (t2) == ARRAY_TYPE)
> +	      && (TYPE_QUALS (t2_stripped) & ~TYPE_QUALS (t1)))
> +	    {
> +	      if (!flag_isoc2x)
> +		warning_at (colon_loc, OPT_Wdiscarded_array_qualifiers,
> +			    "pointer to array loses qualifier "
> +			    "in conditional expression");
> +	      else if (warn_c11_c2x_compat > 0)
> +		warning_at (colon_loc, OPT_Wc11_c2x_compat,
> +			    "pointer to array loses qualifier "
> +			    "in conditional expression in ISO C before C2X");
> +	    }
> +	  if (TREE_CODE (t2) == FUNCTION_TYPE)
>  	    pedwarn (colon_loc, OPT_Wpedantic,
>  		     "ISO C forbids conditional expr between "
>  		     "%<void *%> and function pointer");
> -	  result_type = build_pointer_type (qualify_type (TREE_TYPE (type1),
> -							  TREE_TYPE (type2)));
> -	}
> -      else if (VOID_TYPE_P (TREE_TYPE (type2))
> -	       && !TYPE_ATOMIC (TREE_TYPE (type2)))
> -	{
> -	  if ((TREE_CODE (TREE_TYPE (type1)) == ARRAY_TYPE)
> -	      && (TYPE_QUALS (strip_array_types (TREE_TYPE (type1)))
> -		  & ~TYPE_QUALS (TREE_TYPE (type2))))
> -	    warning_at (colon_loc, OPT_Wdiscarded_array_qualifiers,
> -			"pointer to array loses qualifier "
> -			"in conditional expression");
> -
> -	  if (TREE_CODE (TREE_TYPE (type1)) == FUNCTION_TYPE)
> -	    pedwarn (colon_loc, OPT_Wpedantic,
> -		     "ISO C forbids conditional expr between "
> -		     "%<void *%> and function pointer");
> -	  result_type = build_pointer_type (qualify_type (TREE_TYPE (type2),
> -							  TREE_TYPE (type1)));
> +	  /* for array, use qualifiers of element type */
> +	  if (flag_isoc2x)
> +	    t2 = t2_stripped;
> +	  result_type = build_pointer_type (qualify_type (t1, t2));
>  	}
>        /* Objective-C pointer comparisons are a bit more lenient.  */
>        else if (objc_have_common_type (type1, type2, -3, NULL_TREE))
> @@ -6786,27 +6787,40 @@ convert_for_assignment (location_t location, location_t expr_loc, tree
> type,
>  
>    /* This macro is used to emit diagnostics to ensure that all format
>       strings are complete sentences, visible to gettext and checked at
> -     compile time.  It is the same as PEDWARN_FOR_ASSIGNMENT but with an
> -     extra parameter to enumerate qualifiers.  */
> -#define PEDWARN_FOR_QUALIFIERS(LOCATION, PLOC, OPT, AR, AS, IN, RE, QUALS) \
> +     compile time.  It can be called with 'pedwarn' or 'warning_at'.  */
> +#define WARNING_FOR_QUALIFIERS(PEDWARN, LOCATION, PLOC, OPT, AR, AS, IN, RE, QUALS) \
>    do {                                                                   \
>      switch (errtype)                                                     \
>        {                                                                  \
>        case ic_argpass:                                                   \
> -	{								\
> -	auto_diagnostic_group d;						\
> -	if (pedwarn (PLOC, OPT, AR, parmnum, rname, QUALS))		\
> -	  inform_for_arg (fundecl, (PLOC), parmnum, type, rhstype);	\
> -	}								\
> +	{								 \
> +	  auto_diagnostic_group d;					 \
> +	  if (PEDWARN) {						 \
> +	    if (pedwarn (PLOC, OPT, AR, parmnum, rname, QUALS))          \
> +	      inform_for_arg (fundecl, (PLOC), parmnum, type, rhstype);  \
> +	  } else {							 \
> +	    if (warning_at (PLOC, OPT, AR, parmnum, rname, QUALS))	 \
> +	      inform_for_arg (fundecl, (PLOC), parmnum, type, rhstype);  \
> +	  }								 \
> +	}								 \
>          break;                                                           \
>        case ic_assign:                                                    \
> -        pedwarn (LOCATION, OPT, AS, QUALS);				 \
> +	if (PEDWARN)							 \
> +	  pedwarn (LOCATION, OPT, AS, QUALS);                            \
> +	else								 \
> +	  warning_at (LOCATION, OPT, AS, QUALS);                         \
>          break;                                                           \
>        case ic_init:                                                      \
> -        pedwarn (LOCATION, OPT, IN, QUALS);				 \
> +	if (PEDWARN)							 \
> +	  pedwarn (LOCATION, OPT, IN, QUALS);                            \
> +	else								 \
> +	  warning_at (LOCATION, OPT, IN, QUALS);                         \
>          break;                                                           \
>        case ic_return:                                                    \
> -        pedwarn (LOCATION, OPT, RE, QUALS);				 \
> +	if (PEDWARN)							 \
> +	  pedwarn (LOCATION, OPT, RE, QUALS);                            \
> +	else								 \
> +	  warning_at (LOCATION, OPT, RE, QUALS);                         \
>          break;                                                           \
>        default:                                                           \
>          gcc_unreachable ();                                              \
> @@ -6815,32 +6829,11 @@ convert_for_assignment (location_t location, location_t expr_loc, tree
> type,
>  
>    /* This macro is used to emit diagnostics to ensure that all format
>       strings are complete sentences, visible to gettext and checked at
> -     compile time.  It is the same as PEDWARN_FOR_QUALIFIERS but uses
> -     warning_at instead of pedwarn.  */
> -#define WARNING_FOR_QUALIFIERS(LOCATION, PLOC, OPT, AR, AS, IN, RE, QUALS) \
> -  do {                                                                   \
> -    switch (errtype)                                                     \
> -      {                                                                  \
> -      case ic_argpass:                                                   \
> -	{								\
> -	  auto_diagnostic_group d;						\
> -	  if (warning_at (PLOC, OPT, AR, parmnum, rname, QUALS))	\
> -	    inform_for_arg (fundecl, (PLOC), parmnum, type, rhstype); \
> -	}								\
> -        break;                                                           \
> -      case ic_assign:                                                    \
> -        warning_at (LOCATION, OPT, AS, QUALS);                           \
> -        break;                                                           \
> -      case ic_init:                                                      \
> -        warning_at (LOCATION, OPT, IN, QUALS);                           \
> -        break;                                                           \
> -      case ic_return:                                                    \
> -        warning_at (LOCATION, OPT, RE, QUALS);                           \
> -        break;                                                           \
> -      default:                                                           \
> -        gcc_unreachable ();                                              \
> -      }                                                                  \
> -  } while (0)
> +     compile time.  It is the same as PEDWARN_FOR_ASSIGNMENT but with an
> +     extra parameter to enumerate qualifiers.  */
> +#define PEDWARN_FOR_QUALIFIERS(LOCATION, PLOC, OPT, AR, AS, IN, RE, QUALS) \
> +   WARNING_FOR_QUALIFIERS (true, LOCATION, PLOC, OPT, AR, AS, IN, RE, QUALS)
> +
>  
>    if (TREE_CODE (rhs) == EXCESS_PRECISION_EXPR)
>      rhs = TREE_OPERAND (rhs, 0);
> @@ -7348,17 +7341,18 @@ convert_for_assignment (location_t location, location_t expr_loc, tree
> type,
>  
>  	      if (TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttr)
>  		  & ~TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttl))
> -		WARNING_FOR_QUALIFIERS (location, expr_loc,
> -				        OPT_Wdiscarded_array_qualifiers,
> -				        G_("passing argument %d of %qE discards "
> +		WARNING_FOR_QUALIFIERS (flag_isoc2x,
> +					location, expr_loc,
> +					OPT_Wdiscarded_array_qualifiers,
> +					G_("passing argument %d of %qE discards "
>  					   "%qv qualifier from pointer target type"),
> -				        G_("assignment discards %qv qualifier "
> +					G_("assignment discards %qv qualifier "
>  					   "from pointer target type"),
> -				        G_("initialization discards %qv qualifier "
> +					G_("initialization discards %qv qualifier "
>  					   "from pointer target type"),
> -				        G_("return discards %qv qualifier from "
> +					G_("return discards %qv qualifier from "
>  					   "pointer target type"),
> -                                        TYPE_QUALS (ttr) & ~TYPE_QUALS (ttl));
> +					TYPE_QUALS (ttr) & ~TYPE_QUALS (ttl));
>              }
>            else if (pedantic
>  	      && ((VOID_TYPE_P (ttl) && TREE_CODE (ttr) == FUNCTION_TYPE)
> @@ -7381,28 +7375,31 @@ convert_for_assignment (location_t location, location_t expr_loc, tree
> type,
>  	  else if (TREE_CODE (ttr) != FUNCTION_TYPE
>  		   && TREE_CODE (ttl) != FUNCTION_TYPE)
>  	    {
> +	       /* Assignments between atomic and non-atomic objects are OK.  */
> +	       bool warn_quals_ped = TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttr)
> +				     & ~TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttl);
> +	       bool warn_quals = TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttr)
> +				 & ~TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (strip_array_types (ttl));
> +
>  	      /* Don't warn about loss of qualifier for conversions from
>  		 qualified void* to pointers to arrays with corresponding
> -		 qualifier on the element type. */
> -	      if (!pedantic)
> -	        ttl = strip_array_types (ttl);
> +		 qualifier on the element type (except for pedantic before C23). */
> +	      if (warn_quals || (warn_quals_ped && pedantic && !flag_isoc2x))
> +		PEDWARN_FOR_QUALIFIERS (location, expr_loc,
> +					OPT_Wdiscarded_qualifiers,
> +					G_("passing argument %d of %qE discards "
> +					   "%qv qualifier from pointer target type"),
> +					G_("assignment discards %qv qualifier "
> +					   "from pointer target type"),
> +					G_("initialization discards %qv qualifier "
> +					   "from pointer target type"),
> +					G_("return discards %qv qualifier from "
> +					   "pointer target type"),
> +					TYPE_QUALS (ttr) & ~TYPE_QUALS (ttl));
> +	      else if (warn_quals_ped)
> +		pedwarn_c11 (location, OPT_Wc11_c2x_compat,
> +			     "array with qualifier on the element is not qualified before C2X");
>  
> -	      /* Assignments between atomic and non-atomic objects are OK.  */
> -	      if (TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttr)
> -		  & ~TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttl))
> -		{
> -		  PEDWARN_FOR_QUALIFIERS (location, expr_loc,
> -				          OPT_Wdiscarded_qualifiers,
> -				          G_("passing argument %d of %qE discards "
> -					     "%qv qualifier from pointer target type"),
> -				          G_("assignment discards %qv qualifier "
> -					     "from pointer target type"),
> -				          G_("initialization discards %qv qualifier "
> -					     "from pointer target type"),
> -				          G_("return discards %qv qualifier from "
> -					     "pointer target type"),
> -				          TYPE_QUALS (ttr) & ~TYPE_QUALS (ttl));
> -		}
>  	      /* If this is not a case of ignoring a mismatch in signedness,
>  		 no warning.  */
>  	      else if (VOID_TYPE_P (ttl) || VOID_TYPE_P (ttr)
> diff --git a/gcc/testsuite/gcc.dg/c11-qual-1.c b/gcc/testsuite/gcc.dg/c11-qual-1.c
> new file mode 100644
> index 00000000000..f731e068830
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c11-qual-1.c
> @@ -0,0 +1,11 @@
> +/* Test that qualifiers are lost in tertiary operator for pointers to arrays before C2X, PR98397
> */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c11 -pedantic-errors -Wno-discarded-array-qualifiers" } */
> +
> +void foo(void)
> +{
> +	const int (*u)[1];
> +	void *v;
> +	_Static_assert(_Generic(1 ? u : v, const void*: 0, void*: 1), "qualifier not lost");
> +	_Static_assert(_Generic(1 ? v : u, const void*: 0, void*: 1), "qualifier not lost");
> +}
> diff --git a/gcc/testsuite/gcc.dg/c2x-qual-1.c b/gcc/testsuite/gcc.dg/c2x-qual-1.c
> new file mode 100644
> index 00000000000..4d33db1907d
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c2x-qual-1.c
> @@ -0,0 +1,30 @@
> +/* Tests related to qualifiers and pointers to arrays in C2X, PR98397 */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c2x -pedantic-errors" } */
> +
> +/* test that qualifiers are preserved in tertiary operator for pointers to arrays in C2X */
> +
> +void f(void)
> +{
> +	const int (*u)[1];
> +	void *v;
> +	_Static_assert(_Generic(1 ? u : v, const void*: 1, void*: 0), "lost qualifier");
> +	_Static_assert(_Generic(1 ? v : u, const void*: 1, void*: 0), "lost qualifier");
> +}
> +
> +/* test that assignment of unqualified to qualified pointers works as expected */
> +
> +void g(void)
> +{
> +	int (*x)[3];
> +	const int (*p)[3] = x;
> +}
> +
> +/* test that assignment of qualified void pointers works as expected */
> +
> +void h(void)
> +{
> +	const void* x;
> +	const int (*p)[3] = x;
> +}
> +
> diff --git a/gcc/testsuite/gcc.dg/c2x-qual-2.c b/gcc/testsuite/gcc.dg/c2x-qual-2.c
> new file mode 100644
> index 00000000000..f60a5b18faa
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c2x-qual-2.c
> @@ -0,0 +1,30 @@
> +/* Tests related to qualifiers and pointers to arrays in C2X, PR98397 */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c2x -Wc11-c2x-compat" } */
> +
> +/* test that qualifiers are preserved in tertiary operator for pointers to arrays in C2X */
> +
> +void f(void)
> +{
> +	const int (*u)[1];
> +	void *v;
> +	_Static_assert(_Generic(1 ? u : v, const void*: 1, void*: 0), "lost qualifier");	/* { dg-
> warning "pointer to array loses qualifier in conditional" } */
> +	_Static_assert(_Generic(1 ? v : u, const void*: 1, void*: 0), "lost qualifier");	/* { dg-
> warning "pointer to array loses qualifier in conditional" } */
> +}
> +
> +/* test that assignment of unqualified to qualified pointers works as expected */
> +
> +void g(void)
> +{
> +	int (*x)[3];
> +	const int (*p)[3] = x; /* { dg-warning "arrays with different qualifiers"  } */
> +}
> +
> +/* test that assignment of qualified void pointers works as expected */
> +
> +void h(void)
> +{
> +	const void* x;
> +	const int (*p)[3] = x; /* { dg-warning "array with qualifier on the element is not qualified
> before C2X" } */
> +}
> +
> diff --git a/gcc/testsuite/gcc.dg/c2x-qual-3.c b/gcc/testsuite/gcc.dg/c2x-qual-3.c
> new file mode 100644
> index 00000000000..31896fcb1a1
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c2x-qual-3.c
> @@ -0,0 +1,30 @@
> +/* Tests related to qualifiers and pointers to arrays in C2X, PR98397 */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c2x -Wc11-c2x-compat -pedantic-errors" } */
> +
> +/* test that qualifiers are preserved in tertiary operator for pointers to arrays in C2X */
> +
> +void f(void)
> +{
> +	const int (*u)[1];
> +	void *v;
> +	_Static_assert(_Generic(1 ? u : v, const void*: 1, void*: 0), "lost qualifier");	/* { dg-
> warning "pointer to array loses qualifier in conditional" } */
> +	_Static_assert(_Generic(1 ? v : u, const void*: 1, void*: 0), "lost qualifier");	/* { dg-
> warning "pointer to array loses qualifier in conditional" } */
> +}
> +
> +/* test that assignment of unqualified to qualified pointers works as expected */
> +
> +void g(void)
> +{
> +	int (*x)[3];
> +	const int (*p)[3] = x; /* { dg-warning "arrays with different qualifiers"  } */
> +}
> +
> +/* test that assignment of qualified void pointers works as expected */
> +
> +void h(void)
> +{
> +	const void* x;
> +	const int (*p)[3] = x; /* { dg-warning "array with qualifier on the element is not qualified
> before C2X" } */
> +}
> +
> diff --git a/gcc/testsuite/gcc.dg/c2x-qual-4.c b/gcc/testsuite/gcc.dg/c2x-qual-4.c
> new file mode 100644
> index 00000000000..93b4723dcd6
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c2x-qual-4.c
> @@ -0,0 +1,105 @@
> +/* { dg-do compile } */
> +/* { dg-options "-std=c2x" } */
> +void tvoid(void* x);
> +void transpose0(double* out, const double* in) { }
> +void transpose1(double out[2][2], const double in[2][2]) { }
> +void transpose2(double out[2][2][2], const double in[2][2][2]) { }
> +// return
> +int (*y2(const int x[3][3]))[3] { return x; } /* { dg-warning "return discards 'const' qualifier
> from pointer target type" } */
> +const int (*y3(int x[3][3]))[3] { return x; }
> +void test(void)
> +{
> +	double x0[2];
> +	double y0[2];
> +	const double z0[4];
> +	double x1[2][2];
> +	double y1[2][2];
> +	double o1[2][3];
> +	const double z1[2][2];
> +	double x2[2][2][2];
> +	double y2[2][2][2];
> +	double o2[2][2][3];
> +	const double z2[2][2][2];
> +	// void pointers
> +	tvoid(x0);
> +	tvoid(x1);
> +	tvoid(x2);
> +	tvoid(z0); /* { dg-warning "passing argument 1 of 'tvoid' discards 'const' qualifier from
> pointer target type" } */
> +	tvoid(z1); /* { dg-warning "passing argument 1 of 'tvoid' discards 'const' qualifier from
> pointer target type" } */
> +	tvoid(z2); /* { dg-warning "passing argument 1 of 'tvoid' discards 'const' qualifier from
> pointer target type" } */
> +	void* p;
> +	const void* pc;
> +	p = x0;
> +	p = x1;
> +	p = x2;
> +	p = z0; /* { dg-warning "assignment discards 'const' qualifier from pointer target type" }
> */
> +	p = z1; /* { dg-warning "assignment discards 'const' qualifier from pointer target type" }
> */
> +	p = z2; /* { dg-warning "assignment discards 'const' qualifier from pointer target type" }
> */
> +	pc = x0;
> +	pc = x1;
> +	pc = x2;
> +	pc = z0;
> +	pc = z1;
> +	pc = z2;
> +	transpose0(pc, p); /* { dg-warning "passing argument 1 of 'transpose0' discards 'const'
> qualifier from pointer target type" } */
> +	transpose1(pc, p); /* { dg-warning "passing argument 1 of 'transpose1' discards 'const'
> qualifier from pointer target type" } */
> +	transpose2(pc, p); /* { dg-warning "passing argument 1 of 'transpose2' discards 'const'
> qualifier from pointer target type" } */
> +	transpose0(p, pc);
> +	transpose1(p, pc);
> +	transpose2(p, pc);
> +	// passing as arguments
> +	transpose0(y0, x0);
> +	transpose1(y1, x1);
> +	transpose2(y2, x2);
> +	// initialization
> +	const double (*u0p) = x0;
> +	const double (*u1p)[2] = x1;
> +	const double (*u2p)[2][2] = x2;
> +	double (*v0p) = z0; /* { dg-warning "initialization discards 'const' qualifier from pointer
> target type" } */
> +	double (*v1p)[2] = z1; /* { dg-warning "initialization discards 'const' qualifier from
> pointer target type" } */
> +	double (*v2p)[2][2] = z2; /* { dg-warning "initialization discards 'const' qualifier from
> pointer target type" } */
> +	// subtraction
> +	&(x0[1]) - &(z0[0]);
> +	&(x1[1]) - &(z1[0]);
> +	&(x2[1]) - &(z2[0]);
> +	// comparison
> +	x0 == z0;
> +	x1 == z1;
> +	x2 == z2;
> +	x0 < z0; 
> +	x1 < z1; 
> +	x2 < z2; 
> +	x0 > z0;
> +	x1 > z1;
> +	x2 > z2;
> +	// assignment
> +	u0p = x0;
> +	u1p = x1;
> +	u2p = x2;
> +	v0p = z0; /* { dg-warning "assignment discards 'const' qualifier from pointer target type" }
> */
> +	v1p = z1; /* { dg-warning "assignment discards 'const' qualifier from pointer target type" }
> */
> +	v2p = z2; /* { dg-warning "assignment discards 'const' qualifier from pointer target type" }
> */
> +	// conditional expressions
> +	(void)(1 ? x0 : z0);
> +	(void)(1 ? x1 : z1);
> +	(void)(1 ? x2 : z2);
> +	(void)(1 ? x0 : x1); /* { dg-warning "pointer type mismatch in conditional expression" } */
> +	(void)(1 ? x1 : x2); /* { dg-warning "pointer type mismatch in conditional expression" } */
> +	(void)(1 ? x2 : x0); /* { dg-warning "pointer type mismatch in conditional expression" } */
> +	v0p = (1 ? z0 : v0p); /* { dg-warning "assignment discards 'const' qualifier from pointer
> target type" } */
> +	v1p = (1 ? z1 : v1p); /* { dg-warning "assignment discards 'const' qualifier from pointer
> target type" } */
> +	v2p = (1 ? z2 : v2p); /* { dg-warning "assignment discards 'const' qualifier from pointer
> target type" } */
> +	v0p = (1 ? x0 : u0p); /* { dg-warning "assignment discards 'const' qualifier from pointer
> target type" } */
> +	v1p = (1 ? x1 : u1p); /* { dg-warning "assignment discards 'const' qualifier from pointer
> target type" } */
> +	v2p = (1 ? x2 : u2p); /* { dg-warning "assignment discards 'const' qualifier from pointer
> target type" } */
> +	(1 ? x0 : z0)[0] = 1; /* { dg-error "assignment of read-only location" } */
> +	(1 ? x1 : z1)[0][0] = 1; /* { dg-error "assignment of read-only location" } */
> +	(1 ? x2 : z2)[0][0][0] = 1; /* { dg-error "assignment of read-only location" } */
> +	v0p = (1 ? p : z0); /* { dg-warning "assignment discards 'const' qualifier from pointer
> target type" } */
> +	v1p = (1 ? p : z1); /* { dg-warning "assignment discards 'const' qualifier from pointer
> target type" } */
> +	v2p = (1 ? p : z2); /* { dg-warning "assignment discards 'const' qualifier from pointer
> target type" } */
> +	v0p = (1 ? pc : x0); /* { dg-warning "assignment discards 'const' qualifier from pointer
> target type" } */
> +	v1p = (1 ? pc : x1); /* { dg-warning "assignment discards 'const' qualifier from pointer
> target type" } */
> +	v2p = (1 ? pc : x2); /* { dg-warning "assignment discards 'const' qualifier from pointer
> target type" } */
> +}
> +
> diff --git a/gcc/testsuite/gcc.dg/c2x-qual-5.c b/gcc/testsuite/gcc.dg/c2x-qual-5.c
> new file mode 100644
> index 00000000000..0801fa0eed5
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c2x-qual-5.c
> @@ -0,0 +1,101 @@
> +/* { dg-do compile } */
> +/* { dg-options "-std=c2x -pedantic-errors" } */
> +void tvoid(void* x);
> +void transpose0(double* out, const double* in) { }
> +void transpose1(double out[2][2], const double in[2][2]) { }
> +void transpose2(double out[2][2][2], const double in[2][2][2]) { }
> +// return
> +int (*x2(const int x[3][3]))[3] { return x; } /* { dg-error "return discards" } */
> +const int (*x3(int x[3][3]))[3] { return x; }
> +void test(void)
> +{
> +	double x0[2];
> +	double y0[2];
> +	const double z0[4];
> +	double x1[2][2];
> +	double y1[2][2];
> +	double o1[2][3];
> +	const double z1[2][2];
> +	double x2[2][2][2];
> +	double y2[2][2][2];
> +	double o2[2][2][3];
> +	const double z2[2][2][2];
> +	// void pointers
> +	tvoid(z0); /* { dg-error "passing argument 1 of 'tvoid' discards 'const' qualifier from
> pointer target type" } */
> +	tvoid(z1); /* { dg-error "passing argument 1 of 'tvoid' discards 'const' qualifier from
> pointer target type" } */
> +	tvoid(z2); /* { dg-error "passing argument 1 of 'tvoid' discards 'const' qualifier from
> pointer target type" } */
> +	void* p;
> +	const void* pc;
> +	p = x0;
> +	p = x1;
> +	p = x2;
> +	p = z0; /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
> +	p = z1; /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
> +	p = z2; /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
> +	pc = x0;
> +	pc = x1;
> +	pc = x2;
> +	pc = z0;
> +	pc = z1;
> +	pc = z2;
> +	transpose0(pc, p); /* { dg-error "passing argument 1 of 'transpose0' discards 'const'
> qualifier from pointer target type" } */
> +	transpose1(pc, p); /* { dg-error "passing argument 1 of 'transpose1' discards 'const'
> qualifier from pointer target type" } */
> +	transpose2(pc, p); /* { dg-error "passing argument 1 of 'transpose2' discards 'const'
> qualifier from pointer target type" } */
> +	transpose0(p, pc);
> +	transpose1(p, pc);
> +	transpose2(p, pc);
> +	// passing as arguments
> +	transpose0(y0, x0);
> +	transpose1(y1, o1); /* { dg-error "passing argument 2 of 'transpose1' from incompatible
> pointer type" } */
> +	transpose1(y1, x1);
> +	transpose2(y2, o2); /* { dg-error "passing argument 2 of 'transpose2' from incompatible
> pointer type" } */
> +	transpose2(y2, x2);
> +	// initialization
> +	const double (*x0p) = x0;
> +	const double (*x1p)[2] = x1;
> +	const double (*x2p)[2][2] = x2;
> +	double (*v0p) = z0; /* { dg-error "initialization discards 'const' qualifier from pointer
> target type" } */
> +	double (*v1p)[2] = z1; /* { dg-error "initialization discards" } */
> +	double (*v2p)[2][2] = z2; /* { dg-error "initialization discards" } */
> +	// assignment
> +	x0p = x0;
> +	x1p = x1;
> +	x2p = x2;
> +	// subtraction
> +	&(x0[1]) - &(z0[0]);
> +	&(x1[1]) - &(z1[0]);
> +	&(x2[1]) - &(z2[0]);
> +	// comparison
> +	x0 == z0;
> +	x1 == z1;
> +	x2 == z2;
> +	x0 < z0;
> +	x1 < z1;
> +	x2 < z2;
> +	x0 > z0;
> +	x1 > z1;
> +	x2 > z2;
> +	// conditional expressions
> +	(void)(1 ? x0 : z0);
> +	(void)(1 ? x1 : z1);
> +	(void)(1 ? x2 : z2);
> +	(void)(1 ? x0 : x1); /* { dg-error "pointer type mismatch in conditional expression" } */
> +	(void)(1 ? x1 : x2); /* { dg-error "pointer type mismatch in conditional expression" } */
> +	(void)(1 ? x2 : x0); /* { dg-error "pointer type mismatch in conditional expression" } */
> +	v0p = (1 ? z0 : v0p); /* { dg-error "assignment discards 'const' qualifier from pointer
> target type" } */
> +	v1p = (1 ? z1 : v1p); /* { dg-error "assignment discards" } */
> +	v2p = (1 ? z2 : v2p); /* { dg-error "assignment discards" } */
> +	v0p = (1 ? x0 : x0p); /* { dg-error "assignment discards 'const' qualifier from pointer
> target type" } */
> +	v1p = (1 ? x1 : x1p); /* { dg-error "assignment discards" } */
> +	v2p = (1 ? x2 : x2p); /* { dg-error "assignment discards" } */
> +	(1 ? x0 : z0)[0] = 1; /* { dg-error "assignment of read-only location" } */
> +	(1 ? x1 : z1)[0][0] = 1; /* { dg-error "assignment of read-only location" } */
> +	(1 ? x2 : z2)[0][0][0] = 1; /* { dg-error "assignment of read-only location" } */
> +	v0p = (1 ? p : z0); /* { dg-error "assignment discards 'const' qualifier from pointer target
> type" } */
> +	v1p = (1 ? p : z1); /* { dg-error "assignment discards 'const' qualifier from pointer target
> type" } */
> +	v2p = (1 ? p : z2); /* { dg-error "assignment discards 'const' qualifier from pointer target
> type" } */
> +	v0p = (1 ? pc : x0); /* { dg-error "assignment discards 'const' qualifier from pointer
> target type" } */
> +	v1p = (1 ? pc : x1); /* { dg-error "assignment discards 'const' qualifier from pointer
> target type" } */
> +	v2p = (1 ? pc : x2); /* { dg-error "assignment discards 'const' qualifier from pointer
> target type" } */
> +}
> +
> diff --git a/gcc/testsuite/gcc.dg/c2x-qual-6.c b/gcc/testsuite/gcc.dg/c2x-qual-6.c
> new file mode 100644
> index 00000000000..9c91e206e30
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c2x-qual-6.c
> @@ -0,0 +1,114 @@
> +/* { dg-do compile } */
> +/* { dg-options "-std=c2x -Wc11-c2x-compat -pedantic-errors" } */
> +void tvoid(void* x);
> +void transpose0(double* out, const double* in) { }
> +void transpose1(double out[2][2], const double in[2][2]) { }
> +void transpose2(double out[2][2][2], const double in[2][2][2]) { }
> +// return
> +int (*x2(const int x[3][3]))[3] { return x; } /* { dg-warning "before C2X" } */
> +						/* { dg-error "return discards" "" { target *-*-* }
> .-1 } */
> +const int (*x3(int x[3][3]))[3] { return x; }	/* { dg-warning "before C2X" } */
> +void test(void)
> +{
> +	double x0[2];
> +	double y0[2];
> +	const double z0[4];
> +	double x1[2][2];
> +	double y1[2][2];
> +	double o1[2][3];
> +	const double z1[2][2];
> +	double x2[2][2][2];
> +	double y2[2][2][2];
> +	double o2[2][2][3];
> +	const double z2[2][2][2];
> +	// void pointers
> +	tvoid(z0); /* { dg-error "passing argument 1 of 'tvoid' discards 'const' qualifier from
> pointer target type" } */
> +	tvoid(z1); /* { dg-error "passing argument 1 of 'tvoid' discards 'const' qualifier from
> pointer target type" } */
> +	tvoid(z2); /* { dg-error "passing argument 1 of 'tvoid' discards 'const' qualifier from
> pointer target type" } */
> +	void* p;
> +	const void* pc;
> +	p = x0;
> +	p = x1;
> +	p = x2;
> +	p = z0; /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
> +	p = z1; /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
> +	p = z2; /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
> +	pc = x0;
> +	pc = x1;
> +	pc = x2;
> +	pc = z0;
> +	pc = z1;
> +	pc = z2;
> +	transpose0(pc, p); /* { dg-error "passing argument 1 of 'transpose0' discards 'const'
> qualifier from pointer target type" } */
> +	transpose1(pc, p); /* { dg-error "passing argument 1 of 'transpose1' discards 'const'
> qualifier from pointer target type" } */
> +	transpose2(pc, p); /* { dg-error "passing argument 1 of 'transpose2' discards 'const'
> qualifier from pointer target type" } */
> +	transpose0(p, pc); 
> +	transpose1(p, pc); /* { dg-warning "before C2X" } */
> +	transpose2(p, pc); /* { dg-warning "before C2X" } */
> +	// passing as arguments
> +	transpose0(y0, x0);
> +	transpose1(y1, o1); /* { dg-error "passing argument 2 of 'transpose1' from incompatible
> pointer type" } */
> +	transpose1(y1, x1); /* { dg-warning "before C2X" } */
> +	transpose2(y2, o2); /* { dg-error "passing argument 2 of 'transpose2' from incompatible
> pointer type" } */
> +	transpose2(y2, x2); /* { dg-warning "before C2X" } */
> +	// initialization
> +	const double (*x0p) = x0;
> +	const double (*x1p)[2] = x1; /* { dg-warning "before C2X" } */
> +	const double (*x2p)[2][2] = x2; /* { dg-warning "before C2X" } */
> +	double (*v0p) = z0; /* { dg-error "initialization discards 'const' qualifier from pointer
> target type" } */
> +	double (*v1p)[2] = z1; /* { dg-warning "before C2X" } */
> +				/* { dg-error "initialization discards" "" { target *-*-* } .-1 } */
> +	double (*v2p)[2][2] = z2; /* { dg-warning "before C2X" } */
> +				/* { dg-error "initialization discards" "" { target *-*-* } .-1 } */
> +				
> +	// assignment
> +	x0p = x0;
> +	x1p = x1; /* { dg-warning "before C2X" } */
> +	x2p = x2; /* { dg-warning "before C2X" } */
> +
> +	// subtraction
> +	&(x0[1]) - &(z0[0]);
> +	&(x1[1]) - &(z1[0]); /* { dg-warning "before C2X" } */
> +	&(x2[1]) - &(z2[0]); /* { dg-warning "before C2X" } */
> +	// comparison
> +	x0 == z0;
> +	x1 == z1; /* { dg-warning "before C2X" } */
> +	x2 == z2; /* { dg-warning "before C2X" } */
> +	x0 < z0;
> +	x1 < z1; /* { dg-warning "before C2X" } */
> +	x2 < z2; /* { dg-warning "before C2X" } */
> +	x0 > z0;
> +	x1 > z1; /* { dg-warning "before C2X" } */
> +	x2 > z2; /* { dg-warning "before C2X" } */
> +	// conditional expressions
> +	(void)(1 ? x0 : z0);
> +	(void)(1 ? x1 : z1); /* { dg-warning "before C2X" } */
> +	(void)(1 ? x2 : z2); /* { dg-warning "before C2X" } */
> +	(void)(1 ? x0 : x1); /* { dg-error "pointer type mismatch in conditional expression" } */
> +	(void)(1 ? x1 : x2); /* { dg-error "pointer type mismatch in conditional expression" } */
> +	(void)(1 ? x2 : x0); /* { dg-error "pointer type mismatch in conditional expression" } */
> +	v0p = (1 ? z0 : v0p); /* { dg-error "assignment discards 'const' qualifier from pointer
> target type" } */
> +	v1p = (1 ? z1 : v1p); /* { dg-warning "before C2X" } */
> +				/* { dg-error "assignment discards" "" { target *-*-* } .-1 } */
> +	v2p = (1 ? z2 : v2p); /* { dg-warning "before C2X" } */
> +				/* { dg-error "assignment discards" "" { target *-*-* } .-1 } */
> +	v0p = (1 ? x0 : x0p); /* { dg-error "assignment discards 'const' qualifier from pointer
> target type" } */
> +	v1p = (1 ? x1 : x1p); /* { dg-error "assignment discards" } */
> +				/* { dg-warning "before C2X" "" { target *-*-* } .-1 } */
> +	v2p = (1 ? x2 : x2p); /* { dg-error "assignment discards" } */
> +				/* { dg-warning "before C2X" "" { target *-*-* } .-1 } */
> +	(1 ? x0 : z0)[0] = 1; /* { dg-error "assignment of read-only location" } */
> +	(1 ? x1 : z1)[0][0] = 1; /* { dg-error "assignment of read-only location" } */
> +				/* { dg-warning "before C2X" "" { target *-*-* } .-1 } */
> +	(1 ? x2 : z2)[0][0][0] = 1; /* { dg-error "assignment of read-only location" } */
> +				/* { dg-warning "before C2X" "" { target *-*-* } .-1 } */
> +	v0p = (1 ? p : z0); /* { dg-error "assignment discards 'const' qualifier from pointer target
> type" } */
> +	v1p = (1 ? p : z1); /* { dg-error "assignment discards 'const' qualifier from pointer target
> type" } */
> +				/* { dg-warning "before C2X" "" { target *-*-* } .-1 } */
> +	v2p = (1 ? p : z2); /* { dg-error "assignment discards 'const' qualifier from pointer target
> type" } */
> +				/* { dg-warning "before C2X" "" { target *-*-* } .-1 } */
> +	v0p = (1 ? pc : x0); /* { dg-error "assignment discards 'const' qualifier from pointer
> target type" } */
> +	v1p = (1 ? pc : x1); /* { dg-error "assignment discards 'const' qualifier from pointer
> target type" } */
> +	v2p = (1 ? pc : x2); /* { dg-error "assignment discards 'const' qualifier from pointer
> target type" } */
> +}
> +
> diff --git a/gcc/testsuite/gcc.dg/pointer-array-quals-1.c b/gcc/testsuite/gcc.dg/pointer-array-
> quals-1.c
> index 921a37e9e0d..498ab223162 100644
> --- a/gcc/testsuite/gcc.dg/pointer-array-quals-1.c
> +++ b/gcc/testsuite/gcc.dg/pointer-array-quals-1.c
> @@ -1,6 +1,6 @@
>  /* { dg-do compile } */
>  /* Origin: Martin Uecker <uecker@eecs.berkeley.edu> */
> -/* { dg-options "-Wdiscarded-array-qualifiers" } */
> +/* { dg-options "" } */
>  void tvoid(void* x);
>  void transpose0(double* out, const double* in) { }
>  void transpose1(double out[2][2], const double in[2][2]) { }
> diff --git a/gcc/testsuite/gcc.dg/pointer-array-quals-2.c b/gcc/testsuite/gcc.dg/pointer-array-
> quals-2.c
> index 30689c7312d..4c95d8a3a78 100644
> --- a/gcc/testsuite/gcc.dg/pointer-array-quals-2.c
> +++ b/gcc/testsuite/gcc.dg/pointer-array-quals-2.c
> @@ -1,5 +1,5 @@
>  /* { dg-do compile } */
> -/* { dg-options "-Wdiscarded-array-qualifiers -pedantic-errors" } */
> +/* { dg-options "-pedantic-errors" } */
>  /* Origin: Martin Uecker <uecker@eecs.berkeley.edu> */
>  void tvoid(void* x);
>  void transpose0(double* out, const double* in) { }

^ permalink raw reply	[flat|nested] 9+ messages in thread

* PING^2 [C PATCH] qualifiers of pointers to arrays in C2X [PR 98397]
  2021-06-11 19:25 ` Uecker, Martin
@ 2021-08-01  7:20   ` Uecker, Martin
  0 siblings, 0 replies; 9+ messages in thread
From: Uecker, Martin @ 2021-08-01  7:20 UTC (permalink / raw)
  To: gcc-patches; +Cc: joseph

Am Freitag, den 11.06.2021, 21:25 +0200 schrieb Martin Uecker:
> (PING. In case you missed this. Sorry, forgot to CC you.)
> 
> Am Montag, den 24.05.2021, 08:05 +0200 schrieb Martin Uecker:
> > Hi Joseph,
> > 
> > I found some time to update this patch. The only real change
> > of the patch is the qualifier in the conditional expression for
> > pointer to arrays in C2X. All the rest are the warnings,
> > which were wrong in the last version.
> > 
> > I hope I got this correct this time in combination with
> > -pedantic-errors and -Wc11-c2x-compat. 
> > 
> > Martin
> > 
> > 
> > 2021-05-16  Martin Uecker  <muecker@gwdg.de>
> >     
> > gcc/c/
> >      PR c/98397
> >      * c-typeck.c (comp_target_types): Change pedwarn to pedwarn_c11
> >      for pointers to arrays with qualifiers.
> >      (build_conditional_expr): For C23 don't lose qualifiers for pointers
> >      to arrays when the other pointer is a void pointer. Update warnings.
> >      (convert_for_assignment): Update warnings for C2X when converting from
> >      void* with qualifiers to a pointer to array with the same qualifiers.
> > 
> > gcc/testsuite/
> >      PR c/98397
> >      * gcc.dg/c11-qual-1.c: New test.
> >      * gcc.dg/c2x-qual-1.c: New test.
> >      * gcc.dg/c2x-qual-2.c: New test.
> >      * gcc.dg/c2x-qual-3.c: New test.
> >      * gcc.dg/c2x-qual-4.c: New test.
> >      * gcc.dg/c2x-qual-5.c: New test.
> >      * gcc.dg/c2x-qual-6.c: New test.
> >      * gcc.dg/pointer-array-quals-1.c: Remove unnecessary flag.
> >      * gcc.dg/pointer-array-quals-2.c: Remove unnecessary flag.
> > 
> > 
> > diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
> > index fc64ef96fb8..5b13656c090 100644
> > --- a/gcc/c/c-typeck.c
> > +++ b/gcc/c/c-typeck.c
> > @@ -1328,8 +1328,8 @@ comp_target_types (location_t location, tree ttl, tree ttr)
> >    val = comptypes_check_enum_int (mvl, mvr, &enum_and_int_p);
> >  
> >    if (val == 1 && val_ped != 1)
> > -    pedwarn (location, OPT_Wpedantic, "pointers to arrays with different qualifiers "
> > -                                      "are incompatible in ISO C");
> > +    pedwarn_c11 (location, OPT_Wpedantic, "invalid use of pointers to arrays with different
> > qualifiers "
> > +					  "in ISO C before C2X");
> >  
> >    if (val == 2)
> >      pedwarn (location, OPT_Wpedantic, "types are not quite compatible");
> > @@ -5396,39 +5396,40 @@ build_conditional_expr (location_t colon_loc, tree ifexp, bool
> > ifexp_bcp,
> >  		    "used in conditional expression");
> >  	  return error_mark_node;
> >  	}
> > -      else if (VOID_TYPE_P (TREE_TYPE (type1))
> > -	       && !TYPE_ATOMIC (TREE_TYPE (type1)))
> > -	{
> > -	  if ((TREE_CODE (TREE_TYPE (type2)) == ARRAY_TYPE)
> > -	      && (TYPE_QUALS (strip_array_types (TREE_TYPE (type2)))
> > -		  & ~TYPE_QUALS (TREE_TYPE (type1))))
> > -	    warning_at (colon_loc, OPT_Wdiscarded_array_qualifiers,
> > -			"pointer to array loses qualifier "
> > -			"in conditional expression");
> > -
> > -	  if (TREE_CODE (TREE_TYPE (type2)) == FUNCTION_TYPE)
> > +      else if ((VOID_TYPE_P (TREE_TYPE (type1))
> > +		&& !TYPE_ATOMIC (TREE_TYPE (type1)))
> > +	       || (VOID_TYPE_P (TREE_TYPE (type2))
> > +		   && !TYPE_ATOMIC (TREE_TYPE (type2))))
> > +	{
> > +	  tree t1 = TREE_TYPE (type1);
> > +	  tree t2 = TREE_TYPE (type2);
> > +	  if (!VOID_TYPE_P (t1))
> > +	   {
> > +	     /* roles are swapped */
> > +	     t1 = t2;
> > +	     t2 = TREE_TYPE (type1);
> > +	   }
> > +	  tree t2_stripped = strip_array_types (t2);
> > +	  if ((TREE_CODE (t2) == ARRAY_TYPE)
> > +	      && (TYPE_QUALS (t2_stripped) & ~TYPE_QUALS (t1)))
> > +	    {
> > +	      if (!flag_isoc2x)
> > +		warning_at (colon_loc, OPT_Wdiscarded_array_qualifiers,
> > +			    "pointer to array loses qualifier "
> > +			    "in conditional expression");
> > +	      else if (warn_c11_c2x_compat > 0)
> > +		warning_at (colon_loc, OPT_Wc11_c2x_compat,
> > +			    "pointer to array loses qualifier "
> > +			    "in conditional expression in ISO C before C2X");
> > +	    }
> > +	  if (TREE_CODE (t2) == FUNCTION_TYPE)
> >  	    pedwarn (colon_loc, OPT_Wpedantic,
> >  		     "ISO C forbids conditional expr between "
> >  		     "%<void *%> and function pointer");
> > -	  result_type = build_pointer_type (qualify_type (TREE_TYPE (type1),
> > -							  TREE_TYPE (type2)));
> > -	}
> > -      else if (VOID_TYPE_P (TREE_TYPE (type2))
> > -	       && !TYPE_ATOMIC (TREE_TYPE (type2)))
> > -	{
> > -	  if ((TREE_CODE (TREE_TYPE (type1)) == ARRAY_TYPE)
> > -	      && (TYPE_QUALS (strip_array_types (TREE_TYPE (type1)))
> > -		  & ~TYPE_QUALS (TREE_TYPE (type2))))
> > -	    warning_at (colon_loc, OPT_Wdiscarded_array_qualifiers,
> > -			"pointer to array loses qualifier "
> > -			"in conditional expression");
> > -
> > -	  if (TREE_CODE (TREE_TYPE (type1)) == FUNCTION_TYPE)
> > -	    pedwarn (colon_loc, OPT_Wpedantic,
> > -		     "ISO C forbids conditional expr between "
> > -		     "%<void *%> and function pointer");
> > -	  result_type = build_pointer_type (qualify_type (TREE_TYPE (type2),
> > -							  TREE_TYPE (type1)));
> > +	  /* for array, use qualifiers of element type */
> > +	  if (flag_isoc2x)
> > +	    t2 = t2_stripped;
> > +	  result_type = build_pointer_type (qualify_type (t1, t2));
> >  	}
> >        /* Objective-C pointer comparisons are a bit more lenient.  */
> >        else if (objc_have_common_type (type1, type2, -3, NULL_TREE))
> > @@ -6786,27 +6787,40 @@ convert_for_assignment (location_t location, location_t expr_loc, tree
> > type,
> >  
> >    /* This macro is used to emit diagnostics to ensure that all format
> >       strings are complete sentences, visible to gettext and checked at
> > -     compile time.  It is the same as PEDWARN_FOR_ASSIGNMENT but with an
> > -     extra parameter to enumerate qualifiers.  */
> > -#define PEDWARN_FOR_QUALIFIERS(LOCATION, PLOC, OPT, AR, AS, IN, RE, QUALS) \
> > +     compile time.  It can be called with 'pedwarn' or 'warning_at'.  */
> > +#define WARNING_FOR_QUALIFIERS(PEDWARN, LOCATION, PLOC, OPT, AR, AS, IN, RE, QUALS) \
> >    do {                                                                   \
> >      switch (errtype)                                                     \
> >        {                                                                  \
> >        case ic_argpass:                                                   \
> > -	{								\
> > -	auto_diagnostic_group d;						\
> > -	if (pedwarn (PLOC, OPT, AR, parmnum, rname, QUALS))		\
> > -	  inform_for_arg (fundecl, (PLOC), parmnum, type, rhstype);	\
> > -	}								\
> > +	{								 \
> > +	  auto_diagnostic_group d;					 \
> > +	  if (PEDWARN) {						 \
> > +	    if (pedwarn (PLOC, OPT, AR, parmnum, rname, QUALS))          \
> > +	      inform_for_arg (fundecl, (PLOC), parmnum, type, rhstype);  \
> > +	  } else {							 \
> > +	    if (warning_at (PLOC, OPT, AR, parmnum, rname, QUALS))	 \
> > +	      inform_for_arg (fundecl, (PLOC), parmnum, type, rhstype);  \
> > +	  }								 \
> > +	}								 \
> >          break;                                                           \
> >        case ic_assign:                                                    \
> > -        pedwarn (LOCATION, OPT, AS, QUALS);				 \
> > +	if (PEDWARN)							 \
> > +	  pedwarn (LOCATION, OPT, AS, QUALS);                            \
> > +	else								 \
> > +	  warning_at (LOCATION, OPT, AS, QUALS);                         \
> >          break;                                                           \
> >        case ic_init:                                                      \
> > -        pedwarn (LOCATION, OPT, IN, QUALS);				 \
> > +	if (PEDWARN)							 \
> > +	  pedwarn (LOCATION, OPT, IN, QUALS);                            \
> > +	else								 \
> > +	  warning_at (LOCATION, OPT, IN, QUALS);                         \
> >          break;                                                           \
> >        case ic_return:                                                    \
> > -        pedwarn (LOCATION, OPT, RE, QUALS);				 \
> > +	if (PEDWARN)							 \
> > +	  pedwarn (LOCATION, OPT, RE, QUALS);                            \
> > +	else								 \
> > +	  warning_at (LOCATION, OPT, RE, QUALS);                         \
> >          break;                                                           \
> >        default:                                                           \
> >          gcc_unreachable ();                                              \
> > @@ -6815,32 +6829,11 @@ convert_for_assignment (location_t location, location_t expr_loc, tree
> > type,
> >  
> >    /* This macro is used to emit diagnostics to ensure that all format
> >       strings are complete sentences, visible to gettext and checked at
> > -     compile time.  It is the same as PEDWARN_FOR_QUALIFIERS but uses
> > -     warning_at instead of pedwarn.  */
> > -#define WARNING_FOR_QUALIFIERS(LOCATION, PLOC, OPT, AR, AS, IN, RE, QUALS) \
> > -  do {                                                                   \
> > -    switch (errtype)                                                     \
> > -      {                                                                  \
> > -      case ic_argpass:                                                   \
> > -	{								\
> > -	  auto_diagnostic_group d;						\
> > -	  if (warning_at (PLOC, OPT, AR, parmnum, rname, QUALS))	\
> > -	    inform_for_arg (fundecl, (PLOC), parmnum, type, rhstype); \
> > -	}								\
> > -        break;                                                           \
> > -      case ic_assign:                                                    \
> > -        warning_at (LOCATION, OPT, AS, QUALS);                           \
> > -        break;                                                           \
> > -      case ic_init:                                                      \
> > -        warning_at (LOCATION, OPT, IN, QUALS);                           \
> > -        break;                                                           \
> > -      case ic_return:                                                    \
> > -        warning_at (LOCATION, OPT, RE, QUALS);                           \
> > -        break;                                                           \
> > -      default:                                                           \
> > -        gcc_unreachable ();                                              \
> > -      }                                                                  \
> > -  } while (0)
> > +     compile time.  It is the same as PEDWARN_FOR_ASSIGNMENT but with an
> > +     extra parameter to enumerate qualifiers.  */
> > +#define PEDWARN_FOR_QUALIFIERS(LOCATION, PLOC, OPT, AR, AS, IN, RE, QUALS) \
> > +   WARNING_FOR_QUALIFIERS (true, LOCATION, PLOC, OPT, AR, AS, IN, RE, QUALS)
> > +
> >  
> >    if (TREE_CODE (rhs) == EXCESS_PRECISION_EXPR)
> >      rhs = TREE_OPERAND (rhs, 0);
> > @@ -7348,17 +7341,18 @@ convert_for_assignment (location_t location, location_t expr_loc, tree
> > type,
> >  
> >  	      if (TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttr)
> >  		  & ~TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttl))
> > -		WARNING_FOR_QUALIFIERS (location, expr_loc,
> > -				        OPT_Wdiscarded_array_qualifiers,
> > -				        G_("passing argument %d of %qE discards "
> > +		WARNING_FOR_QUALIFIERS (flag_isoc2x,
> > +					location, expr_loc,
> > +					OPT_Wdiscarded_array_qualifiers,
> > +					G_("passing argument %d of %qE discards "
> >  					   "%qv qualifier from pointer target type"),
> > -				        G_("assignment discards %qv qualifier "
> > +					G_("assignment discards %qv qualifier "
> >  					   "from pointer target type"),
> > -				        G_("initialization discards %qv qualifier "
> > +					G_("initialization discards %qv qualifier "
> >  					   "from pointer target type"),
> > -				        G_("return discards %qv qualifier from "
> > +					G_("return discards %qv qualifier from "
> >  					   "pointer target type"),
> > -                                        TYPE_QUALS (ttr) & ~TYPE_QUALS (ttl));
> > +					TYPE_QUALS (ttr) & ~TYPE_QUALS (ttl));
> >              }
> >            else if (pedantic
> >  	      && ((VOID_TYPE_P (ttl) && TREE_CODE (ttr) == FUNCTION_TYPE)
> > @@ -7381,28 +7375,31 @@ convert_for_assignment (location_t location, location_t expr_loc, tree
> > type,
> >  	  else if (TREE_CODE (ttr) != FUNCTION_TYPE
> >  		   && TREE_CODE (ttl) != FUNCTION_TYPE)
> >  	    {
> > +	       /* Assignments between atomic and non-atomic objects are OK.  */
> > +	       bool warn_quals_ped = TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttr)
> > +				     & ~TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttl);
> > +	       bool warn_quals = TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttr)
> > +				 & ~TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (strip_array_types (ttl));
> > +
> >  	      /* Don't warn about loss of qualifier for conversions from
> >  		 qualified void* to pointers to arrays with corresponding
> > -		 qualifier on the element type. */
> > -	      if (!pedantic)
> > -	        ttl = strip_array_types (ttl);
> > +		 qualifier on the element type (except for pedantic before C23). */
> > +	      if (warn_quals || (warn_quals_ped && pedantic && !flag_isoc2x))
> > +		PEDWARN_FOR_QUALIFIERS (location, expr_loc,
> > +					OPT_Wdiscarded_qualifiers,
> > +					G_("passing argument %d of %qE discards "
> > +					   "%qv qualifier from pointer target type"),
> > +					G_("assignment discards %qv qualifier "
> > +					   "from pointer target type"),
> > +					G_("initialization discards %qv qualifier "
> > +					   "from pointer target type"),
> > +					G_("return discards %qv qualifier from "
> > +					   "pointer target type"),
> > +					TYPE_QUALS (ttr) & ~TYPE_QUALS (ttl));
> > +	      else if (warn_quals_ped)
> > +		pedwarn_c11 (location, OPT_Wc11_c2x_compat,
> > +			     "array with qualifier on the element is not qualified before C2X");
> >  
> > -	      /* Assignments between atomic and non-atomic objects are OK.  */
> > -	      if (TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttr)
> > -		  & ~TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttl))
> > -		{
> > -		  PEDWARN_FOR_QUALIFIERS (location, expr_loc,
> > -				          OPT_Wdiscarded_qualifiers,
> > -				          G_("passing argument %d of %qE discards "
> > -					     "%qv qualifier from pointer target type"),
> > -				          G_("assignment discards %qv qualifier "
> > -					     "from pointer target type"),
> > -				          G_("initialization discards %qv qualifier "
> > -					     "from pointer target type"),
> > -				          G_("return discards %qv qualifier from "
> > -					     "pointer target type"),
> > -				          TYPE_QUALS (ttr) & ~TYPE_QUALS (ttl));
> > -		}
> >  	      /* If this is not a case of ignoring a mismatch in signedness,
> >  		 no warning.  */
> >  	      else if (VOID_TYPE_P (ttl) || VOID_TYPE_P (ttr)
> > diff --git a/gcc/testsuite/gcc.dg/c11-qual-1.c b/gcc/testsuite/gcc.dg/c11-qual-1.c
> > new file mode 100644
> > index 00000000000..f731e068830
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/c11-qual-1.c
> > @@ -0,0 +1,11 @@
> > +/* Test that qualifiers are lost in tertiary operator for pointers to arrays before C2X,
> > PR98397
> > */
> > +/* { dg-do compile } */
> > +/* { dg-options "-std=c11 -pedantic-errors -Wno-discarded-array-qualifiers" } */
> > +
> > +void foo(void)
> > +{
> > +	const int (*u)[1];
> > +	void *v;
> > +	_Static_assert(_Generic(1 ? u : v, const void*: 0, void*: 1), "qualifier not lost");
> > +	_Static_assert(_Generic(1 ? v : u, const void*: 0, void*: 1), "qualifier not lost");
> > +}
> > diff --git a/gcc/testsuite/gcc.dg/c2x-qual-1.c b/gcc/testsuite/gcc.dg/c2x-qual-1.c
> > new file mode 100644
> > index 00000000000..4d33db1907d
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/c2x-qual-1.c
> > @@ -0,0 +1,30 @@
> > +/* Tests related to qualifiers and pointers to arrays in C2X, PR98397 */
> > +/* { dg-do compile } */
> > +/* { dg-options "-std=c2x -pedantic-errors" } */
> > +
> > +/* test that qualifiers are preserved in tertiary operator for pointers to arrays in C2X */
> > +
> > +void f(void)
> > +{
> > +	const int (*u)[1];
> > +	void *v;
> > +	_Static_assert(_Generic(1 ? u : v, const void*: 1, void*: 0), "lost qualifier");
> > +	_Static_assert(_Generic(1 ? v : u, const void*: 1, void*: 0), "lost qualifier");
> > +}
> > +
> > +/* test that assignment of unqualified to qualified pointers works as expected */
> > +
> > +void g(void)
> > +{
> > +	int (*x)[3];
> > +	const int (*p)[3] = x;
> > +}
> > +
> > +/* test that assignment of qualified void pointers works as expected */
> > +
> > +void h(void)
> > +{
> > +	const void* x;
> > +	const int (*p)[3] = x;
> > +}
> > +
> > diff --git a/gcc/testsuite/gcc.dg/c2x-qual-2.c b/gcc/testsuite/gcc.dg/c2x-qual-2.c
> > new file mode 100644
> > index 00000000000..f60a5b18faa
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/c2x-qual-2.c
> > @@ -0,0 +1,30 @@
> > +/* Tests related to qualifiers and pointers to arrays in C2X, PR98397 */
> > +/* { dg-do compile } */
> > +/* { dg-options "-std=c2x -Wc11-c2x-compat" } */
> > +
> > +/* test that qualifiers are preserved in tertiary operator for pointers to arrays in C2X */
> > +
> > +void f(void)
> > +{
> > +	const int (*u)[1];
> > +	void *v;
> > +	_Static_assert(_Generic(1 ? u : v, const void*: 1, void*: 0), "lost qualifier");	/* { dg-
> > warning "pointer to array loses qualifier in conditional" } */
> > +	_Static_assert(_Generic(1 ? v : u, const void*: 1, void*: 0), "lost qualifier");	/* { dg-
> > warning "pointer to array loses qualifier in conditional" } */
> > +}
> > +
> > +/* test that assignment of unqualified to qualified pointers works as expected */
> > +
> > +void g(void)
> > +{
> > +	int (*x)[3];
> > +	const int (*p)[3] = x; /* { dg-warning "arrays with different qualifiers"  } */
> > +}
> > +
> > +/* test that assignment of qualified void pointers works as expected */
> > +
> > +void h(void)
> > +{
> > +	const void* x;
> > +	const int (*p)[3] = x; /* { dg-warning "array with qualifier on the element is not qualified
> > before C2X" } */
> > +}
> > +
> > diff --git a/gcc/testsuite/gcc.dg/c2x-qual-3.c b/gcc/testsuite/gcc.dg/c2x-qual-3.c
> > new file mode 100644
> > index 00000000000..31896fcb1a1
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/c2x-qual-3.c
> > @@ -0,0 +1,30 @@
> > +/* Tests related to qualifiers and pointers to arrays in C2X, PR98397 */
> > +/* { dg-do compile } */
> > +/* { dg-options "-std=c2x -Wc11-c2x-compat -pedantic-errors" } */
> > +
> > +/* test that qualifiers are preserved in tertiary operator for pointers to arrays in C2X */
> > +
> > +void f(void)
> > +{
> > +	const int (*u)[1];
> > +	void *v;
> > +	_Static_assert(_Generic(1 ? u : v, const void*: 1, void*: 0), "lost qualifier");	/* { dg-
> > warning "pointer to array loses qualifier in conditional" } */
> > +	_Static_assert(_Generic(1 ? v : u, const void*: 1, void*: 0), "lost qualifier");	/* { dg-
> > warning "pointer to array loses qualifier in conditional" } */
> > +}
> > +
> > +/* test that assignment of unqualified to qualified pointers works as expected */
> > +
> > +void g(void)
> > +{
> > +	int (*x)[3];
> > +	const int (*p)[3] = x; /* { dg-warning "arrays with different qualifiers"  } */
> > +}
> > +
> > +/* test that assignment of qualified void pointers works as expected */
> > +
> > +void h(void)
> > +{
> > +	const void* x;
> > +	const int (*p)[3] = x; /* { dg-warning "array with qualifier on the element is not qualified
> > before C2X" } */
> > +}
> > +
> > diff --git a/gcc/testsuite/gcc.dg/c2x-qual-4.c b/gcc/testsuite/gcc.dg/c2x-qual-4.c
> > new file mode 100644
> > index 00000000000..93b4723dcd6
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/c2x-qual-4.c
> > @@ -0,0 +1,105 @@
> > +/* { dg-do compile } */
> > +/* { dg-options "-std=c2x" } */
> > +void tvoid(void* x);
> > +void transpose0(double* out, const double* in) { }
> > +void transpose1(double out[2][2], const double in[2][2]) { }
> > +void transpose2(double out[2][2][2], const double in[2][2][2]) { }
> > +// return
> > +int (*y2(const int x[3][3]))[3] { return x; } /* { dg-warning "return discards 'const'
> > qualifier
> > from pointer target type" } */
> > +const int (*y3(int x[3][3]))[3] { return x; }
> > +void test(void)
> > +{
> > +	double x0[2];
> > +	double y0[2];
> > +	const double z0[4];
> > +	double x1[2][2];
> > +	double y1[2][2];
> > +	double o1[2][3];
> > +	const double z1[2][2];
> > +	double x2[2][2][2];
> > +	double y2[2][2][2];
> > +	double o2[2][2][3];
> > +	const double z2[2][2][2];
> > +	// void pointers
> > +	tvoid(x0);
> > +	tvoid(x1);
> > +	tvoid(x2);
> > +	tvoid(z0); /* { dg-warning "passing argument 1 of 'tvoid' discards 'const' qualifier from
> > pointer target type" } */
> > +	tvoid(z1); /* { dg-warning "passing argument 1 of 'tvoid' discards 'const' qualifier from
> > pointer target type" } */
> > +	tvoid(z2); /* { dg-warning "passing argument 1 of 'tvoid' discards 'const' qualifier from
> > pointer target type" } */
> > +	void* p;
> > +	const void* pc;
> > +	p = x0;
> > +	p = x1;
> > +	p = x2;
> > +	p = z0; /* { dg-warning "assignment discards 'const' qualifier from pointer target type" }
> > */
> > +	p = z1; /* { dg-warning "assignment discards 'const' qualifier from pointer target type" }
> > */
> > +	p = z2; /* { dg-warning "assignment discards 'const' qualifier from pointer target type" }
> > */
> > +	pc = x0;
> > +	pc = x1;
> > +	pc = x2;
> > +	pc = z0;
> > +	pc = z1;
> > +	pc = z2;
> > +	transpose0(pc, p); /* { dg-warning "passing argument 1 of 'transpose0' discards 'const'
> > qualifier from pointer target type" } */
> > +	transpose1(pc, p); /* { dg-warning "passing argument 1 of 'transpose1' discards 'const'
> > qualifier from pointer target type" } */
> > +	transpose2(pc, p); /* { dg-warning "passing argument 1 of 'transpose2' discards 'const'
> > qualifier from pointer target type" } */
> > +	transpose0(p, pc);
> > +	transpose1(p, pc);
> > +	transpose2(p, pc);
> > +	// passing as arguments
> > +	transpose0(y0, x0);
> > +	transpose1(y1, x1);
> > +	transpose2(y2, x2);
> > +	// initialization
> > +	const double (*u0p) = x0;
> > +	const double (*u1p)[2] = x1;
> > +	const double (*u2p)[2][2] = x2;
> > +	double (*v0p) = z0; /* { dg-warning "initialization discards 'const' qualifier from pointer
> > target type" } */
> > +	double (*v1p)[2] = z1; /* { dg-warning "initialization discards 'const' qualifier from
> > pointer target type" } */
> > +	double (*v2p)[2][2] = z2; /* { dg-warning "initialization discards 'const' qualifier from
> > pointer target type" } */
> > +	// subtraction
> > +	&(x0[1]) - &(z0[0]);
> > +	&(x1[1]) - &(z1[0]);
> > +	&(x2[1]) - &(z2[0]);
> > +	// comparison
> > +	x0 == z0;
> > +	x1 == z1;
> > +	x2 == z2;
> > +	x0 < z0; 
> > +	x1 < z1; 
> > +	x2 < z2; 
> > +	x0 > z0;
> > +	x1 > z1;
> > +	x2 > z2;
> > +	// assignment
> > +	u0p = x0;
> > +	u1p = x1;
> > +	u2p = x2;
> > +	v0p = z0; /* { dg-warning "assignment discards 'const' qualifier from pointer target type" }
> > */
> > +	v1p = z1; /* { dg-warning "assignment discards 'const' qualifier from pointer target type" }
> > */
> > +	v2p = z2; /* { dg-warning "assignment discards 'const' qualifier from pointer target type" }
> > */
> > +	// conditional expressions
> > +	(void)(1 ? x0 : z0);
> > +	(void)(1 ? x1 : z1);
> > +	(void)(1 ? x2 : z2);
> > +	(void)(1 ? x0 : x1); /* { dg-warning "pointer type mismatch in conditional expression" } */
> > +	(void)(1 ? x1 : x2); /* { dg-warning "pointer type mismatch in conditional expression" } */
> > +	(void)(1 ? x2 : x0); /* { dg-warning "pointer type mismatch in conditional expression" } */
> > +	v0p = (1 ? z0 : v0p); /* { dg-warning "assignment discards 'const' qualifier from pointer
> > target type" } */
> > +	v1p = (1 ? z1 : v1p); /* { dg-warning "assignment discards 'const' qualifier from pointer
> > target type" } */
> > +	v2p = (1 ? z2 : v2p); /* { dg-warning "assignment discards 'const' qualifier from pointer
> > target type" } */
> > +	v0p = (1 ? x0 : u0p); /* { dg-warning "assignment discards 'const' qualifier from pointer
> > target type" } */
> > +	v1p = (1 ? x1 : u1p); /* { dg-warning "assignment discards 'const' qualifier from pointer
> > target type" } */
> > +	v2p = (1 ? x2 : u2p); /* { dg-warning "assignment discards 'const' qualifier from pointer
> > target type" } */
> > +	(1 ? x0 : z0)[0] = 1; /* { dg-error "assignment of read-only location" } */
> > +	(1 ? x1 : z1)[0][0] = 1; /* { dg-error "assignment of read-only location" } */
> > +	(1 ? x2 : z2)[0][0][0] = 1; /* { dg-error "assignment of read-only location" } */
> > +	v0p = (1 ? p : z0); /* { dg-warning "assignment discards 'const' qualifier from pointer
> > target type" } */
> > +	v1p = (1 ? p : z1); /* { dg-warning "assignment discards 'const' qualifier from pointer
> > target type" } */
> > +	v2p = (1 ? p : z2); /* { dg-warning "assignment discards 'const' qualifier from pointer
> > target type" } */
> > +	v0p = (1 ? pc : x0); /* { dg-warning "assignment discards 'const' qualifier from pointer
> > target type" } */
> > +	v1p = (1 ? pc : x1); /* { dg-warning "assignment discards 'const' qualifier from pointer
> > target type" } */
> > +	v2p = (1 ? pc : x2); /* { dg-warning "assignment discards 'const' qualifier from pointer
> > target type" } */
> > +}
> > +
> > diff --git a/gcc/testsuite/gcc.dg/c2x-qual-5.c b/gcc/testsuite/gcc.dg/c2x-qual-5.c
> > new file mode 100644
> > index 00000000000..0801fa0eed5
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/c2x-qual-5.c
> > @@ -0,0 +1,101 @@
> > +/* { dg-do compile } */
> > +/* { dg-options "-std=c2x -pedantic-errors" } */
> > +void tvoid(void* x);
> > +void transpose0(double* out, const double* in) { }
> > +void transpose1(double out[2][2], const double in[2][2]) { }
> > +void transpose2(double out[2][2][2], const double in[2][2][2]) { }
> > +// return
> > +int (*x2(const int x[3][3]))[3] { return x; } /* { dg-error "return discards" } */
> > +const int (*x3(int x[3][3]))[3] { return x; }
> > +void test(void)
> > +{
> > +	double x0[2];
> > +	double y0[2];
> > +	const double z0[4];
> > +	double x1[2][2];
> > +	double y1[2][2];
> > +	double o1[2][3];
> > +	const double z1[2][2];
> > +	double x2[2][2][2];
> > +	double y2[2][2][2];
> > +	double o2[2][2][3];
> > +	const double z2[2][2][2];
> > +	// void pointers
> > +	tvoid(z0); /* { dg-error "passing argument 1 of 'tvoid' discards 'const' qualifier from
> > pointer target type" } */
> > +	tvoid(z1); /* { dg-error "passing argument 1 of 'tvoid' discards 'const' qualifier from
> > pointer target type" } */
> > +	tvoid(z2); /* { dg-error "passing argument 1 of 'tvoid' discards 'const' qualifier from
> > pointer target type" } */
> > +	void* p;
> > +	const void* pc;
> > +	p = x0;
> > +	p = x1;
> > +	p = x2;
> > +	p = z0; /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
> > +	p = z1; /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
> > +	p = z2; /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
> > +	pc = x0;
> > +	pc = x1;
> > +	pc = x2;
> > +	pc = z0;
> > +	pc = z1;
> > +	pc = z2;
> > +	transpose0(pc, p); /* { dg-error "passing argument 1 of 'transpose0' discards 'const'
> > qualifier from pointer target type" } */
> > +	transpose1(pc, p); /* { dg-error "passing argument 1 of 'transpose1' discards 'const'
> > qualifier from pointer target type" } */
> > +	transpose2(pc, p); /* { dg-error "passing argument 1 of 'transpose2' discards 'const'
> > qualifier from pointer target type" } */
> > +	transpose0(p, pc);
> > +	transpose1(p, pc);
> > +	transpose2(p, pc);
> > +	// passing as arguments
> > +	transpose0(y0, x0);
> > +	transpose1(y1, o1); /* { dg-error "passing argument 2 of 'transpose1' from incompatible
> > pointer type" } */
> > +	transpose1(y1, x1);
> > +	transpose2(y2, o2); /* { dg-error "passing argument 2 of 'transpose2' from incompatible
> > pointer type" } */
> > +	transpose2(y2, x2);
> > +	// initialization
> > +	const double (*x0p) = x0;
> > +	const double (*x1p)[2] = x1;
> > +	const double (*x2p)[2][2] = x2;
> > +	double (*v0p) = z0; /* { dg-error "initialization discards 'const' qualifier from pointer
> > target type" } */
> > +	double (*v1p)[2] = z1; /* { dg-error "initialization discards" } */
> > +	double (*v2p)[2][2] = z2; /* { dg-error "initialization discards" } */
> > +	// assignment
> > +	x0p = x0;
> > +	x1p = x1;
> > +	x2p = x2;
> > +	// subtraction
> > +	&(x0[1]) - &(z0[0]);
> > +	&(x1[1]) - &(z1[0]);
> > +	&(x2[1]) - &(z2[0]);
> > +	// comparison
> > +	x0 == z0;
> > +	x1 == z1;
> > +	x2 == z2;
> > +	x0 < z0;
> > +	x1 < z1;
> > +	x2 < z2;
> > +	x0 > z0;
> > +	x1 > z1;
> > +	x2 > z2;
> > +	// conditional expressions
> > +	(void)(1 ? x0 : z0);
> > +	(void)(1 ? x1 : z1);
> > +	(void)(1 ? x2 : z2);
> > +	(void)(1 ? x0 : x1); /* { dg-error "pointer type mismatch in conditional expression" } */
> > +	(void)(1 ? x1 : x2); /* { dg-error "pointer type mismatch in conditional expression" } */
> > +	(void)(1 ? x2 : x0); /* { dg-error "pointer type mismatch in conditional expression" } */
> > +	v0p = (1 ? z0 : v0p); /* { dg-error "assignment discards 'const' qualifier from pointer
> > target type" } */
> > +	v1p = (1 ? z1 : v1p); /* { dg-error "assignment discards" } */
> > +	v2p = (1 ? z2 : v2p); /* { dg-error "assignment discards" } */
> > +	v0p = (1 ? x0 : x0p); /* { dg-error "assignment discards 'const' qualifier from pointer
> > target type" } */
> > +	v1p = (1 ? x1 : x1p); /* { dg-error "assignment discards" } */
> > +	v2p = (1 ? x2 : x2p); /* { dg-error "assignment discards" } */
> > +	(1 ? x0 : z0)[0] = 1; /* { dg-error "assignment of read-only location" } */
> > +	(1 ? x1 : z1)[0][0] = 1; /* { dg-error "assignment of read-only location" } */
> > +	(1 ? x2 : z2)[0][0][0] = 1; /* { dg-error "assignment of read-only location" } */
> > +	v0p = (1 ? p : z0); /* { dg-error "assignment discards 'const' qualifier from pointer target
> > type" } */
> > +	v1p = (1 ? p : z1); /* { dg-error "assignment discards 'const' qualifier from pointer target
> > type" } */
> > +	v2p = (1 ? p : z2); /* { dg-error "assignment discards 'const' qualifier from pointer target
> > type" } */
> > +	v0p = (1 ? pc : x0); /* { dg-error "assignment discards 'const' qualifier from pointer
> > target type" } */
> > +	v1p = (1 ? pc : x1); /* { dg-error "assignment discards 'const' qualifier from pointer
> > target type" } */
> > +	v2p = (1 ? pc : x2); /* { dg-error "assignment discards 'const' qualifier from pointer
> > target type" } */
> > +}
> > +
> > diff --git a/gcc/testsuite/gcc.dg/c2x-qual-6.c b/gcc/testsuite/gcc.dg/c2x-qual-6.c
> > new file mode 100644
> > index 00000000000..9c91e206e30
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/c2x-qual-6.c
> > @@ -0,0 +1,114 @@
> > +/* { dg-do compile } */
> > +/* { dg-options "-std=c2x -Wc11-c2x-compat -pedantic-errors" } */
> > +void tvoid(void* x);
> > +void transpose0(double* out, const double* in) { }
> > +void transpose1(double out[2][2], const double in[2][2]) { }
> > +void transpose2(double out[2][2][2], const double in[2][2][2]) { }
> > +// return
> > +int (*x2(const int x[3][3]))[3] { return x; } /* { dg-warning "before C2X" } */
> > +						/* { dg-error "return discards" "" { target *-*-* }
> > .-1 } */
> > +const int (*x3(int x[3][3]))[3] { return x; }	/* { dg-warning "before C2X" } */
> > +void test(void)
> > +{
> > +	double x0[2];
> > +	double y0[2];
> > +	const double z0[4];
> > +	double x1[2][2];
> > +	double y1[2][2];
> > +	double o1[2][3];
> > +	const double z1[2][2];
> > +	double x2[2][2][2];
> > +	double y2[2][2][2];
> > +	double o2[2][2][3];
> > +	const double z2[2][2][2];
> > +	// void pointers
> > +	tvoid(z0); /* { dg-error "passing argument 1 of 'tvoid' discards 'const' qualifier from
> > pointer target type" } */
> > +	tvoid(z1); /* { dg-error "passing argument 1 of 'tvoid' discards 'const' qualifier from
> > pointer target type" } */
> > +	tvoid(z2); /* { dg-error "passing argument 1 of 'tvoid' discards 'const' qualifier from
> > pointer target type" } */
> > +	void* p;
> > +	const void* pc;
> > +	p = x0;
> > +	p = x1;
> > +	p = x2;
> > +	p = z0; /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
> > +	p = z1; /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
> > +	p = z2; /* { dg-error "assignment discards 'const' qualifier from pointer target type" } */
> > +	pc = x0;
> > +	pc = x1;
> > +	pc = x2;
> > +	pc = z0;
> > +	pc = z1;
> > +	pc = z2;
> > +	transpose0(pc, p); /* { dg-error "passing argument 1 of 'transpose0' discards 'const'
> > qualifier from pointer target type" } */
> > +	transpose1(pc, p); /* { dg-error "passing argument 1 of 'transpose1' discards 'const'
> > qualifier from pointer target type" } */
> > +	transpose2(pc, p); /* { dg-error "passing argument 1 of 'transpose2' discards 'const'
> > qualifier from pointer target type" } */
> > +	transpose0(p, pc); 
> > +	transpose1(p, pc); /* { dg-warning "before C2X" } */
> > +	transpose2(p, pc); /* { dg-warning "before C2X" } */
> > +	// passing as arguments
> > +	transpose0(y0, x0);
> > +	transpose1(y1, o1); /* { dg-error "passing argument 2 of 'transpose1' from incompatible
> > pointer type" } */
> > +	transpose1(y1, x1); /* { dg-warning "before C2X" } */
> > +	transpose2(y2, o2); /* { dg-error "passing argument 2 of 'transpose2' from incompatible
> > pointer type" } */
> > +	transpose2(y2, x2); /* { dg-warning "before C2X" } */
> > +	// initialization
> > +	const double (*x0p) = x0;
> > +	const double (*x1p)[2] = x1; /* { dg-warning "before C2X" } */
> > +	const double (*x2p)[2][2] = x2; /* { dg-warning "before C2X" } */
> > +	double (*v0p) = z0; /* { dg-error "initialization discards 'const' qualifier from pointer
> > target type" } */
> > +	double (*v1p)[2] = z1; /* { dg-warning "before C2X" } */
> > +				/* { dg-error "initialization discards" "" { target *-*-* } .-1 } */
> > +	double (*v2p)[2][2] = z2; /* { dg-warning "before C2X" } */
> > +				/* { dg-error "initialization discards" "" { target *-*-* } .-1 } */
> > +				
> > +	// assignment
> > +	x0p = x0;
> > +	x1p = x1; /* { dg-warning "before C2X" } */
> > +	x2p = x2; /* { dg-warning "before C2X" } */
> > +
> > +	// subtraction
> > +	&(x0[1]) - &(z0[0]);
> > +	&(x1[1]) - &(z1[0]); /* { dg-warning "before C2X" } */
> > +	&(x2[1]) - &(z2[0]); /* { dg-warning "before C2X" } */
> > +	// comparison
> > +	x0 == z0;
> > +	x1 == z1; /* { dg-warning "before C2X" } */
> > +	x2 == z2; /* { dg-warning "before C2X" } */
> > +	x0 < z0;
> > +	x1 < z1; /* { dg-warning "before C2X" } */
> > +	x2 < z2; /* { dg-warning "before C2X" } */
> > +	x0 > z0;
> > +	x1 > z1; /* { dg-warning "before C2X" } */
> > +	x2 > z2; /* { dg-warning "before C2X" } */
> > +	// conditional expressions
> > +	(void)(1 ? x0 : z0);
> > +	(void)(1 ? x1 : z1); /* { dg-warning "before C2X" } */
> > +	(void)(1 ? x2 : z2); /* { dg-warning "before C2X" } */
> > +	(void)(1 ? x0 : x1); /* { dg-error "pointer type mismatch in conditional expression" } */
> > +	(void)(1 ? x1 : x2); /* { dg-error "pointer type mismatch in conditional expression" } */
> > +	(void)(1 ? x2 : x0); /* { dg-error "pointer type mismatch in conditional expression" } */
> > +	v0p = (1 ? z0 : v0p); /* { dg-error "assignment discards 'const' qualifier from pointer
> > target type" } */
> > +	v1p = (1 ? z1 : v1p); /* { dg-warning "before C2X" } */
> > +				/* { dg-error "assignment discards" "" { target *-*-* } .-1 } */
> > +	v2p = (1 ? z2 : v2p); /* { dg-warning "before C2X" } */
> > +				/* { dg-error "assignment discards" "" { target *-*-* } .-1 } */
> > +	v0p = (1 ? x0 : x0p); /* { dg-error "assignment discards 'const' qualifier from pointer
> > target type" } */
> > +	v1p = (1 ? x1 : x1p); /* { dg-error "assignment discards" } */
> > +				/* { dg-warning "before C2X" "" { target *-*-* } .-1 } */
> > +	v2p = (1 ? x2 : x2p); /* { dg-error "assignment discards" } */
> > +				/* { dg-warning "before C2X" "" { target *-*-* } .-1 } */
> > +	(1 ? x0 : z0)[0] = 1; /* { dg-error "assignment of read-only location" } */
> > +	(1 ? x1 : z1)[0][0] = 1; /* { dg-error "assignment of read-only location" } */
> > +				/* { dg-warning "before C2X" "" { target *-*-* } .-1 } */
> > +	(1 ? x2 : z2)[0][0][0] = 1; /* { dg-error "assignment of read-only location" } */
> > +				/* { dg-warning "before C2X" "" { target *-*-* } .-1 } */
> > +	v0p = (1 ? p : z0); /* { dg-error "assignment discards 'const' qualifier from pointer target
> > type" } */
> > +	v1p = (1 ? p : z1); /* { dg-error "assignment discards 'const' qualifier from pointer target
> > type" } */
> > +				/* { dg-warning "before C2X" "" { target *-*-* } .-1 } */
> > +	v2p = (1 ? p : z2); /* { dg-error "assignment discards 'const' qualifier from pointer target
> > type" } */
> > +				/* { dg-warning "before C2X" "" { target *-*-* } .-1 } */
> > +	v0p = (1 ? pc : x0); /* { dg-error "assignment discards 'const' qualifier from pointer
> > target type" } */
> > +	v1p = (1 ? pc : x1); /* { dg-error "assignment discards 'const' qualifier from pointer
> > target type" } */
> > +	v2p = (1 ? pc : x2); /* { dg-error "assignment discards 'const' qualifier from pointer
> > target type" } */
> > +}
> > +
> > diff --git a/gcc/testsuite/gcc.dg/pointer-array-quals-1.c b/gcc/testsuite/gcc.dg/pointer-array-
> > quals-1.c
> > index 921a37e9e0d..498ab223162 100644
> > --- a/gcc/testsuite/gcc.dg/pointer-array-quals-1.c
> > +++ b/gcc/testsuite/gcc.dg/pointer-array-quals-1.c
> > @@ -1,6 +1,6 @@
> >  /* { dg-do compile } */
> >  /* Origin: Martin Uecker <uecker@eecs.berkeley.edu> */
> > -/* { dg-options "-Wdiscarded-array-qualifiers" } */
> > +/* { dg-options "" } */
> >  void tvoid(void* x);
> >  void transpose0(double* out, const double* in) { }
> >  void transpose1(double out[2][2], const double in[2][2]) { }
> > diff --git a/gcc/testsuite/gcc.dg/pointer-array-quals-2.c b/gcc/testsuite/gcc.dg/pointer-array-
> > quals-2.c
> > index 30689c7312d..4c95d8a3a78 100644
> > --- a/gcc/testsuite/gcc.dg/pointer-array-quals-2.c
> > +++ b/gcc/testsuite/gcc.dg/pointer-array-quals-2.c
> > @@ -1,5 +1,5 @@
> >  /* { dg-do compile } */
> > -/* { dg-options "-Wdiscarded-array-qualifiers -pedantic-errors" } */
> > +/* { dg-options "-pedantic-errors" } */
> >  /* Origin: Martin Uecker <uecker@eecs.berkeley.edu> */
> >  void tvoid(void* x);
> >  void transpose0(double* out, const double* in) { }

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [C PATCH] qualifiers of pointers to arrays in C2X [PR 98397]
  2021-05-24  6:05 [C PATCH] qualifiers of pointers to arrays in C2X [PR 98397] Uecker, Martin
  2021-06-11 19:25 ` Uecker, Martin
@ 2021-08-12 16:58 ` Joseph Myers
  2021-08-22 22:20   ` Uecker, Martin
  1 sibling, 1 reply; 9+ messages in thread
From: Joseph Myers @ 2021-08-12 16:58 UTC (permalink / raw)
  To: Uecker, Martin; +Cc: gcc-patches

On Mon, 24 May 2021, Uecker, Martin wrote:

> -      else if (VOID_TYPE_P (TREE_TYPE (type1))
> -	       && !TYPE_ATOMIC (TREE_TYPE (type1)))
> -	{
> -	  if ((TREE_CODE (TREE_TYPE (type2)) == ARRAY_TYPE)
> -	      && (TYPE_QUALS (strip_array_types (TREE_TYPE (type2)))
> -		  & ~TYPE_QUALS (TREE_TYPE (type1))))
> -	    warning_at (colon_loc, OPT_Wdiscarded_array_qualifiers,
> -			"pointer to array loses qualifier "
> -			"in conditional expression");
> -
> -	  if (TREE_CODE (TREE_TYPE (type2)) == FUNCTION_TYPE)
> +      else if ((VOID_TYPE_P (TREE_TYPE (type1))
> +		&& !TYPE_ATOMIC (TREE_TYPE (type1)))
> +	       || (VOID_TYPE_P (TREE_TYPE (type2))
> +		   && !TYPE_ATOMIC (TREE_TYPE (type2))))

Here you're unifying the two cases where one argument is (not a null 
pointer constant and) a pointer to qualified or unqualified void (and the 
other argument is not a pointer to qualified or unqualified void).  The 
!TYPE_ATOMIC checks are because of the general rule that _Atomic is a type 
qualifier only syntactically, so _Atomic void doesn't count as qualified 
void for this purpose.

> +	{
> +	  tree t1 = TREE_TYPE (type1);
> +	  tree t2 = TREE_TYPE (type2);
> +	  if (!VOID_TYPE_P (t1))
> +	   {
> +	     /* roles are swapped */
> +	     t1 = t2;
> +	     t2 = TREE_TYPE (type1);
> +	   }

But here you don't have a TYPE_ATOMIC check before swapping.  So if t1 is 
_Atomic void and t2 is void, the types don't get swapped.

> +	  /* for array, use qualifiers of element type */
> +	  if (flag_isoc2x)
> +	    t2 = t2_stripped;
> +	  result_type = build_pointer_type (qualify_type (t1, t2));

And then it looks to me like this will end up with _Atomic void * as the 
result type, when a conditional expression between _Atomic void * and 
void * should actually have type void *.

If that's indeed the case, I think the swapping needs to occur whenever t1 
is not *non-atomic* void, so that the condition for swapping matches the 
condition checked in the outer if.  (And of course there should be a 
testcase for that.)

I didn't see any other issues in this version of the patch.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [C PATCH] qualifiers of pointers to arrays in C2X [PR 98397]
  2021-08-12 16:58 ` Joseph Myers
@ 2021-08-22 22:20   ` Uecker, Martin
  0 siblings, 0 replies; 9+ messages in thread
From: Uecker, Martin @ 2021-08-22 22:20 UTC (permalink / raw)
  To: joseph; +Cc: gcc-patches

Am Donnerstag, den 12.08.2021, 16:58 +0000 schrieb Joseph Myers:
> On Mon, 24 May 2021, Uecker, Martin wrote:
> 
> > -      else if (VOID_TYPE_P (TREE_TYPE (type1))
> > -	       && !TYPE_ATOMIC (TREE_TYPE (type1)))
> > -	{
> > -	  if ((TREE_CODE (TREE_TYPE (type2)) == ARRAY_TYPE)
> > -	      && (TYPE_QUALS (strip_array_types (TREE_TYPE (type2)))
> > -		  & ~TYPE_QUALS (TREE_TYPE (type1))))
> > -	    warning_at (colon_loc, OPT_Wdiscarded_array_qualifiers,
> > -			"pointer to array loses qualifier "
> > -			"in conditional expression");
> > -
> > -	  if (TREE_CODE (TREE_TYPE (type2)) == FUNCTION_TYPE)
> > +      else if ((VOID_TYPE_P (TREE_TYPE (type1))
> > +		&& !TYPE_ATOMIC (TREE_TYPE (type1)))
> > +	       || (VOID_TYPE_P (TREE_TYPE (type2))
> > +		   && !TYPE_ATOMIC (TREE_TYPE (type2))))
> 
> Here you're unifying the two cases where one argument is (not a null 
> pointer constant and) a pointer to qualified or unqualified void (and the 
> other argument is not a pointer to qualified or unqualified void).  The 
> !TYPE_ATOMIC checks are because of the general rule that _Atomic is a type 
> qualifier only syntactically, so _Atomic void doesn't count as qualified 
> void for this purpose.
> 
> > +	{
> > +	  tree t1 = TREE_TYPE (type1);
> > +	  tree t2 = TREE_TYPE (type2);
> > +	  if (!VOID_TYPE_P (t1))
> > +	   {
> > +	     /* roles are swapped */
> > +	     t1 = t2;
> > +	     t2 = TREE_TYPE (type1);
> > +	   }
> 
> But here you don't have a TYPE_ATOMIC check before swapping.  So if t1 is 
> _Atomic void and t2 is void, the types don't get swapped.
> 
> > +	  /* for array, use qualifiers of element type */
> > +	  if (flag_isoc2x)
> > +	    t2 = t2_stripped;
> > +	  result_type = build_pointer_type (qualify_type (t1, t2));
> 
> And then it looks to me like this will end up with _Atomic void * as the 
> result type, when a conditional expression between _Atomic void * and 
> void * should actually have type void *.
> 
> If that's indeed the case, I think the swapping needs to occur whenever t1 
> is not *non-atomic* void, so that the condition for swapping matches the 
> condition checked in the outer if.  (And of course there should be a 
> testcase for that.)
> 
> I didn't see any other issues in this version of the patch.

Committed with this change and the additional test.

Martin

> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [C PATCH] qualifiers of pointers to arrays in C2X [PR 98397]
  2021-01-17 20:18   ` Uecker, Martin
@ 2021-01-18 21:48     ` Joseph Myers
  0 siblings, 0 replies; 9+ messages in thread
From: Joseph Myers @ 2021-01-18 21:48 UTC (permalink / raw)
  To: Uecker, Martin; +Cc: gcc-patches

On Sun, 17 Jan 2021, Uecker, Martin wrote:

> I did not add tests for c11 for warnings because we already
> had warnings before and the tests for these exist. (I removed 
> -Wdiscarded-array-qualifiers from the old tests as this flag
> is not needed.) Or should there be additional warnings
> with -Wc11-c2x-compat for c11? But warning twice about
> the same issue does not seem ideal...

If something is already warned about by default in C11 mode, 
-Wc11-c2x-compat doesn't need to produce extra warnings.

> +	int (*x)[3];
> +	const int (*p)[3] = x; /* { dg-warning "pointers to arrays with different qualifiers are
> incompatible in ISO C before C2X"  } */

"incompatible" doesn't seem the right wording for the diagnostic.  The 
types are incompatible (i.e. not compatible types) regardless of standard 
version; the issue in this case is the rules for assignment.

> diff --git a/gcc/testsuite/gcc.dg/c2x-qual-6.c b/gcc/testsuite/gcc.dg/c2x-qual-6.c
> new file mode 100644
> index 00000000000..dca50ac014f
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c2x-qual-6.c
> @@ -0,0 +1,9 @@
> +/* Test related to qualifiers and pointers to arrays in C2X, PR98397 */
> +/* { dg-do compile } */
> +/* { dg-options "-std=c2x -pedantic-errors -Wc11-c2x-compat" } */
> +
> +void f(void)
> +{
> +	const void* x;
> +	const int (*p)[3] = x; /* { dg-error "array with qualifier on the element is not qualified
> before C2X" } */

This is showing a bug in the compiler implementation.  In C2X mode, this 
message should be a warning not a pedwarn because the code is not a 
constraint violation.  -Wc11-c2x-compat should produce a warning 
(independent of -pedantic), but -pedantic-errors should not turn it into 
an error.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [C PATCH] qualifiers of pointers to arrays in C2X [PR 98397]
  2021-01-01  0:01 ` Joseph Myers
@ 2021-01-17 20:18   ` Uecker, Martin
  2021-01-18 21:48     ` Joseph Myers
  0 siblings, 1 reply; 9+ messages in thread
From: Uecker, Martin @ 2021-01-17 20:18 UTC (permalink / raw)
  To: joseph; +Cc: gcc-patches

Am Freitag, den 01.01.2021, 00:01 +0000 schrieb Joseph Myers:
> 
> 
> I'd expect c2x-* tests to use -std=c2x not -std=gnu2x.  Tests needing 
> -std=gnu2x can be gnu2x-* tests, but you should be able to test the types 
> using _Generic without needing any gnu2x features.  c2x-* tests should 
> also use -pedantic or -pedantic-errors unless they are specifically 
> testing something that doesn't work with those options.
> 
> There should also be tests for cases where code is valid before C2x but 
> invalid in C2x (assignment storing a pointer-to-qualified-array in void *, 
> for example).
> 
> All the tests should have both c2x-* and c11-* variants so the testsuite 
> verifies that the code is properly handled in C11 mode (warnings with 
> -pedantic, errors with -pedantic-errors, in the cases that are invalid for 
> C11 but valid for C2x).  There should also be -Wc11-c2x-compat tests with 
> -std=c2x where appropriate.


Here is a revised version which adds some missing warnings 
and also fixed some error regarding which warnings got emitted.

I added tests for C2X that test with std=c2x and -pedantic-errors
with and without -Wc11-c2x-compat. In the conditional operator
I directly test for the type using _Generic and _Static_assert.
This test also exists for c11 testing for the old behavior.

I did not add tests for c11 for warnings because we already
had warnings before and the tests for these exist. (I removed 
-Wdiscarded-array-qualifiers from the old tests as this flag
is not needed.) Or should there be additional warnings
with -Wc11-c2x-compat for c11? But warning twice about
the same issue does not seem ideal...

More comments?

Best,
Martin


diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index 67c0080a5ef..243790e7abf 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -1318,8 +1318,8 @@ comp_target_types (location_t location, tree ttl, tree ttr)
   val = comptypes_check_enum_int (mvl, mvr, &enum_and_int_p);
 
   if (val == 1 && val_ped != 1)
-    pedwarn (location, OPT_Wpedantic, "pointers to arrays with different qualifiers "
-                                      "are incompatible in ISO C");
+    pedwarn_c11 (location, OPT_Wpedantic, "pointers to arrays with different qualifiers "
+					  "are incompatible in ISO C before C2X");
 
   if (val == 2)
     pedwarn (location, OPT_Wpedantic, "types are not quite compatible");
@@ -5331,39 +5331,39 @@ build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp,
 		    "used in conditional expression");
 	  return error_mark_node;
 	}
-      else if (VOID_TYPE_P (TREE_TYPE (type1))
-	       && !TYPE_ATOMIC (TREE_TYPE (type1)))
-	{
-	  if ((TREE_CODE (TREE_TYPE (type2)) == ARRAY_TYPE)
-	      && (TYPE_QUALS (strip_array_types (TREE_TYPE (type2)))
-		  & ~TYPE_QUALS (TREE_TYPE (type1))))
-	    warning_at (colon_loc, OPT_Wdiscarded_array_qualifiers,
-			"pointer to array loses qualifier "
-			"in conditional expression");
-
-	  if (TREE_CODE (TREE_TYPE (type2)) == FUNCTION_TYPE)
+      else if ((VOID_TYPE_P (TREE_TYPE (type1))
+		&& !TYPE_ATOMIC (TREE_TYPE (type1)))
+	       || (VOID_TYPE_P (TREE_TYPE (type2))
+		   && !TYPE_ATOMIC (TREE_TYPE (type2))))
+	{
+	  tree t1 = TREE_TYPE (type1);
+	  tree t2 = TREE_TYPE (type2);
+	  if (!VOID_TYPE_P (t1))
+	   {
+	     /* roles are swapped */
+	     t1 = t2;
+	     t2 = TREE_TYPE (type1);
+	   }
+	  tree t2_stripped = strip_array_types (t2);
+	  if ((TREE_CODE (t2) == ARRAY_TYPE)
+	      && (TYPE_QUALS (t2_stripped) & ~TYPE_QUALS (t1)))
+	    {
+	      if (!flag_isoc2x)
+		warning_at (colon_loc, OPT_Wdiscarded_array_qualifiers,
+			    "pointer to array loses qualifier "
+			    "in conditional expression");
+	      else if (warn_c11_c2x_compat > 0)
+		warning_at (colon_loc, OPT_Wc11_c2x_compat,
+			    "pointer to array does not lose qualifier in C2X "
+			    "in conditional expression");
+	    }
+	  if (TREE_CODE (t2) == FUNCTION_TYPE)
 	    pedwarn (colon_loc, OPT_Wpedantic,
 		     "ISO C forbids conditional expr between "
 		     "%<void *%> and function pointer");
-	  result_type = build_pointer_type (qualify_type (TREE_TYPE (type1),
-							  TREE_TYPE (type2)));
-	}
-      else if (VOID_TYPE_P (TREE_TYPE (type2))
-	       && !TYPE_ATOMIC (TREE_TYPE (type2)))
-	{
-	  if ((TREE_CODE (TREE_TYPE (type1)) == ARRAY_TYPE)
-	      && (TYPE_QUALS (strip_array_types (TREE_TYPE (type1)))
-		  & ~TYPE_QUALS (TREE_TYPE (type2))))
-	    warning_at (colon_loc, OPT_Wdiscarded_array_qualifiers,
-			"pointer to array loses qualifier "
-			"in conditional expression");
-
-	  if (TREE_CODE (TREE_TYPE (type1)) == FUNCTION_TYPE)
-	    pedwarn (colon_loc, OPT_Wpedantic,
-		     "ISO C forbids conditional expr between "
-		     "%<void *%> and function pointer");
-	  result_type = build_pointer_type (qualify_type (TREE_TYPE (type2),
-							  TREE_TYPE (type1)));
+	  if (flag_isoc2x)
+	    t2 = t2_stripped;
+	  result_type = build_pointer_type (qualify_type (t1, t2));
 	}
       /* Objective-C pointer comparisons are a bit more lenient.  */
       else if (objc_have_common_type (type1, type2, -3, NULL_TREE))
@@ -7316,15 +7316,16 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
 	  else if (TREE_CODE (ttr) != FUNCTION_TYPE
 		   && TREE_CODE (ttl) != FUNCTION_TYPE)
 	    {
+	       /* Assignments between atomic and non-atomic objects are OK.  */
+	       bool warn_quals_ped = TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttr)
+				     & ~TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttl);
+	       bool warn_quals = TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttr)
+				 & ~TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (strip_array_types (ttl));
+
 	      /* Don't warn about loss of qualifier for conversions from
 		 qualified void* to pointers to arrays with corresponding
 		 qualifier on the element type. */
-	      if (!pedantic)
-	        ttl = strip_array_types (ttl);
-
-	      /* Assignments between atomic and non-atomic objects are OK.  */
-	      if (TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttr)
-		  & ~TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (ttl))
+	      if (warn_quals || (warn_quals_ped && pedantic && !flag_isoc2x))
 		{
 		  PEDWARN_FOR_QUALIFIERS (location, expr_loc,
 				          OPT_Wdiscarded_qualifiers,
@@ -7338,6 +7339,10 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
 					     "pointer target type"),
 				          TYPE_QUALS (ttr) & ~TYPE_QUALS (ttl));
 		}
+	      else if (warn_quals_ped && pedantic && flag_isoc2x && (warn_c11_c2x_compat > 0))
+		pedwarn (expr_loc, OPT_Wc11_c2x_compat,
+				   "array with qualifier on the element is not qualified before
C2X");
+
 	      /* If this is not a case of ignoring a mismatch in signedness,
 		 no warning.  */
 	      else if (VOID_TYPE_P (ttl) || VOID_TYPE_P (ttr)
diff --git a/gcc/testsuite/gcc.dg/c11-qual-1.c b/gcc/testsuite/gcc.dg/c11-qual-1.c
new file mode 100644
index 00000000000..f731e068830
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c11-qual-1.c
@@ -0,0 +1,11 @@
+/* Test that qualifiers are lost in tertiary operator for pointers to arrays before C2X, PR98397 */
+/* { dg-do compile } */
+/* { dg-options "-std=c11 -pedantic-errors -Wno-discarded-array-qualifiers" } */
+
+void foo(void)
+{
+	const int (*u)[1];
+	void *v;
+	_Static_assert(_Generic(1 ? u : v, const void*: 0, void*: 1), "qualifier not lost");
+	_Static_assert(_Generic(1 ? v : u, const void*: 0, void*: 1), "qualifier not lost");
+}
diff --git a/gcc/testsuite/gcc.dg/c2x-qual-1.c b/gcc/testsuite/gcc.dg/c2x-qual-1.c
new file mode 100644
index 00000000000..d53dc214f91
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-qual-1.c
@@ -0,0 +1,11 @@
+/* Test related to qualifiers and pointers to arrays in C2X, PR98397 */
+/* { dg-do compile } */
+/* { dg-options "-std=c2x -pedantic-errors" } */
+
+void foo(void)
+{
+	const int (*u)[1];
+	void *v;
+	_Static_assert(_Generic(1 ? u : v, const void*: 1, void*: 0), "lost qualifier");
+	_Static_assert(_Generic(1 ? v : u, const void*: 1, void*: 0), "lost qualifier");
+}
diff --git a/gcc/testsuite/gcc.dg/c2x-qual-2.c b/gcc/testsuite/gcc.dg/c2x-qual-2.c
new file mode 100644
index 00000000000..3304410bb96
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-qual-2.c
@@ -0,0 +1,12 @@
+/* Test related to qualifiers and pointers to arrays in C2X, PR98397 */
+/* { dg-do compile } */
+/* { dg-options "-std=c2x -pedantic-errors -Wc11-c2x-compat" } */
+
+void foo(void)
+{
+	const int (*u)[1];
+	void *v;
+	_Static_assert(_Generic(1 ? u : v, const void*: 1, void*: 0), "lost qualifier");	/*
{ dg-warning "pointer to array does not lose qualifier in C2X" } */
+	_Static_assert(_Generic(1 ? v : u, const void*: 1, void*: 0), "lost qualifier");	/*
{ dg-warning "pointer to array does not lose qualifier in C2X" } */
+
+}
diff --git a/gcc/testsuite/gcc.dg/c2x-qual-3.c b/gcc/testsuite/gcc.dg/c2x-qual-3.c
new file mode 100644
index 00000000000..58fe16fed94
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-qual-3.c
@@ -0,0 +1,9 @@
+/* Test related to qualifiers and pointers to arrays in C2X, PR98397 */
+/* { dg-do compile } */
+/* { dg-options "-std=c2x -pedantic-errors" } */
+
+void f(void)
+{
+	int (*x)[3];
+	const int (*p)[3] = x;
+}
diff --git a/gcc/testsuite/gcc.dg/c2x-qual-4.c b/gcc/testsuite/gcc.dg/c2x-qual-4.c
new file mode 100644
index 00000000000..e04668eb752
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-qual-4.c
@@ -0,0 +1,9 @@
+/* Test related to qualifiers and pointers to arrays in C2X, PR98397 */
+/* { dg-do compile } */
+/* { dg-options "-std=c2x -pedantic-errors -Wc11-c2x-compat" } */
+
+void f(void)
+{
+	int (*x)[3];
+	const int (*p)[3] = x; /* { dg-warning "pointers to arrays with different qualifiers are
incompatible in ISO C before C2X"  } */
+}
diff --git a/gcc/testsuite/gcc.dg/c2x-qual-5.c b/gcc/testsuite/gcc.dg/c2x-qual-5.c
new file mode 100644
index 00000000000..4647d0b0dfe
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-qual-5.c
@@ -0,0 +1,9 @@
+/* Test related to qualifiers and pointers to arrays in C2X, PR98397 */
+/* { dg-do compile } */
+/* { dg-options "-std=c2x -pedantic-errors" } */
+
+void f(void)
+{
+	const void* x;
+	const int (*p)[3] = x;
+}
diff --git a/gcc/testsuite/gcc.dg/c2x-qual-6.c b/gcc/testsuite/gcc.dg/c2x-qual-6.c
new file mode 100644
index 00000000000..dca50ac014f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-qual-6.c
@@ -0,0 +1,9 @@
+/* Test related to qualifiers and pointers to arrays in C2X, PR98397 */
+/* { dg-do compile } */
+/* { dg-options "-std=c2x -pedantic-errors -Wc11-c2x-compat" } */
+
+void f(void)
+{
+	const void* x;
+	const int (*p)[3] = x; /* { dg-error "array with qualifier on the element is not qualified
before C2X" } */
+}
diff --git a/gcc/testsuite/gcc.dg/pointer-array-quals-1.c b/gcc/testsuite/gcc.dg/pointer-array-
quals-1.c
index 921a37e9e0d..498ab223162 100644
--- a/gcc/testsuite/gcc.dg/pointer-array-quals-1.c
+++ b/gcc/testsuite/gcc.dg/pointer-array-quals-1.c
@@ -1,6 +1,6 @@
 /* { dg-do compile } */
 /* Origin: Martin Uecker <uecker@eecs.berkeley.edu> */
-/* { dg-options "-Wdiscarded-array-qualifiers" } */
+/* { dg-options "" } */
 void tvoid(void* x);
 void transpose0(double* out, const double* in) { }
 void transpose1(double out[2][2], const double in[2][2]) { }
diff --git a/gcc/testsuite/gcc.dg/pointer-array-quals-2.c b/gcc/testsuite/gcc.dg/pointer-array-
quals-2.c
index 30689c7312d..4c95d8a3a78 100644
--- a/gcc/testsuite/gcc.dg/pointer-array-quals-2.c
+++ b/gcc/testsuite/gcc.dg/pointer-array-quals-2.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-Wdiscarded-array-qualifiers -pedantic-errors" } */
+/* { dg-options "-pedantic-errors" } */
 /* Origin: Martin Uecker <uecker@eecs.berkeley.edu> */
 void tvoid(void* x);
 void transpose0(double* out, const double* in) { }

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [C PATCH] qualifiers of pointers to arrays in C2X [PR 98397]
  2020-12-21 10:28 Uecker, Martin
@ 2021-01-01  0:01 ` Joseph Myers
  2021-01-17 20:18   ` Uecker, Martin
  0 siblings, 1 reply; 9+ messages in thread
From: Joseph Myers @ 2021-01-01  0:01 UTC (permalink / raw)
  To: Uecker, Martin; +Cc: gcc-patches

On Mon, 21 Dec 2020, Uecker, Martin wrote:

> diff --git a/gcc/testsuite/gcc.dg/c2x-qual-1.c b/gcc/testsuite/gcc.dg/c2x-qual-1.c
> new file mode 100644
> index 00000000000..058a840e04c
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/c2x-qual-1.c
> @@ -0,0 +1,12 @@
> +/* Test that qualifiers are not lost in tertiary operator for pointers to arrays, PR98397 */
> +/* { dg-do compile } */
> +/* { dg-options "-std=gnu2x" } */
> +
> +void foo(void)
> +{
> +	const int (*u)[1];
> +	void *v;
> +	extern const void *vc;
> +	extern typeof(1 ? u : v) vc;
> +	extern typeof(1 ? v : u) vc;
> +}

I'd expect c2x-* tests to use -std=c2x not -std=gnu2x.  Tests needing 
-std=gnu2x can be gnu2x-* tests, but you should be able to test the types 
using _Generic without needing any gnu2x features.  c2x-* tests should 
also use -pedantic or -pedantic-errors unless they are specifically 
testing something that doesn't work with those options.

There should also be tests for cases where code is valid before C2x but 
invalid in C2x (assignment storing a pointer-to-qualified-array in void *, 
for example).

All the tests should have both c2x-* and c11-* variants so the testsuite 
verifies that the code is properly handled in C11 mode (warnings with 
-pedantic, errors with -pedantic-errors, in the cases that are invalid for 
C11 but valid for C2x).  There should also be -Wc11-c2x-compat tests with 
-std=c2x where appropriate.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [C PATCH] qualifiers of pointers to arrays in C2X [PR 98397]
@ 2020-12-21 10:28 Uecker, Martin
  2021-01-01  0:01 ` Joseph Myers
  0 siblings, 1 reply; 9+ messages in thread
From: Uecker, Martin @ 2020-12-21 10:28 UTC (permalink / raw)
  To: gcc-patches; +Cc: joseph


Here is a patch that adds the minor corrections needed for
qualifiers of pointers to arrays in C23.

-- Martin


C: Correct qualifiers for pointers to arrays according to C2X [PR98397]
    
2020-12-12  Martin Uecker  <muecker@gwdg.de>
    
gcc/c/
     PR c/98397
     * c-typeck.c (comp_target_types): Change pedwarn to pedwarn_c11
     for pointers to arrays with qualifiers.      
     (build_conditional_expr): For C23 don't lose qualifiers for pointers
     to arrays when the other pointer is a void pointer.
     (convert_for_assignment): For C23 don't pedwarn when converting from
     void* with qualifiers to a pointer to array with the same qualifiers.

gcc/testsuite/
     PR c/98397
     * gcc.dg/c2x-qual-1.c: New test.   
     * gcc.dg/c2x-qual-2.c: New test.


diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index f68cb01529b..46a66e96db5 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -1318,8 +1318,8 @@ comp_target_types (location_t location, tree ttl, tree ttr)
   val = comptypes_check_enum_int (mvl, mvr, &enum_and_int_p);
 
   if (val == 1 && val_ped != 1)
-    pedwarn (location, OPT_Wpedantic, "pointers to arrays with different qualifiers "
-                                      "are incompatible in ISO C");
+    pedwarn_c11 (location, OPT_Wpedantic, "pointers to arrays with different qualifiers "
+					  "are incompatible in ISO C before C2X");
 
   if (val == 2)
     pedwarn (location, OPT_Wpedantic, "types are not quite compatible");
@@ -5331,39 +5331,32 @@ build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp,
 		    "used in conditional expression");
 	  return error_mark_node;
 	}
-      else if (VOID_TYPE_P (TREE_TYPE (type1))
-	       && !TYPE_ATOMIC (TREE_TYPE (type1)))
-	{
-	  if ((TREE_CODE (TREE_TYPE (type2)) == ARRAY_TYPE)
-	      && (TYPE_QUALS (strip_array_types (TREE_TYPE (type2)))
-		  & ~TYPE_QUALS (TREE_TYPE (type1))))
+      else if ((VOID_TYPE_P (TREE_TYPE (type1))
+		&& !TYPE_ATOMIC (TREE_TYPE (type1)))
+	       || (VOID_TYPE_P (TREE_TYPE (type2))
+		   && !TYPE_ATOMIC (TREE_TYPE (type2))))
+	{
+	  tree t1 = TREE_TYPE (type1);
+	  tree t2 = TREE_TYPE (type2);
+	  if (!VOID_TYPE_P (t1))
+	   {
+	     /* roles are swapped */
+	     t1 = t2;
+	     t2 = TREE_TYPE (type1);
+	   }
+	  tree t2_stripped = strip_array_types (t2);
+	  if (flag_isoc2x)
+	    t2 = t2_stripped;
+	  else if ((TREE_CODE (t2) == ARRAY_TYPE)
+		   && (TYPE_QUALS (t2_stripped) & ~TYPE_QUALS (t1)))
 	    warning_at (colon_loc, OPT_Wdiscarded_array_qualifiers,
 			"pointer to array loses qualifier "
 			"in conditional expression");
-
-	  if (TREE_CODE (TREE_TYPE (type2)) == FUNCTION_TYPE)
-	    pedwarn (colon_loc, OPT_Wpedantic,
-		     "ISO C forbids conditional expr between "
-		     "%<void *%> and function pointer");
-	  result_type = build_pointer_type (qualify_type (TREE_TYPE (type1),
-							  TREE_TYPE (type2)));
-	}
-      else if (VOID_TYPE_P (TREE_TYPE (type2))
-	       && !TYPE_ATOMIC (TREE_TYPE (type2)))
-	{
-	  if ((TREE_CODE (TREE_TYPE (type1)) == ARRAY_TYPE)
-	      && (TYPE_QUALS (strip_array_types (TREE_TYPE (type1)))
-		  & ~TYPE_QUALS (TREE_TYPE (type2))))
-	    warning_at (colon_loc, OPT_Wdiscarded_array_qualifiers,
-			"pointer to array loses qualifier "
-			"in conditional expression");
-
-	  if (TREE_CODE (TREE_TYPE (type1)) == FUNCTION_TYPE)
+	  if (TREE_CODE (t2) == FUNCTION_TYPE)
 	    pedwarn (colon_loc, OPT_Wpedantic,
 		     "ISO C forbids conditional expr between "
 		     "%<void *%> and function pointer");
-	  result_type = build_pointer_type (qualify_type (TREE_TYPE (type2),
-							  TREE_TYPE (type1)));
+	  result_type = build_pointer_type (qualify_type (t1, t2));
 	}
       /* Objective-C pointer comparisons are a bit more lenient.  */
       else if (objc_have_common_type (type1, type2, -3, NULL_TREE))
@@ -7319,7 +7312,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
 	      /* Don't warn about loss of qualifier for conversions from
 		 qualified void* to pointers to arrays with corresponding
 		 qualifier on the element type. */
-	      if (!pedantic)
+	      if (flag_isoc2x || !pedantic)
 	        ttl = strip_array_types (ttl);
 
 	      /* Assignments between atomic and non-atomic objects are OK.  */
diff --git a/gcc/testsuite/gcc.dg/c2x-qual-1.c b/gcc/testsuite/gcc.dg/c2x-qual-1.c
new file mode 100644
index 00000000000..058a840e04c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-qual-1.c
@@ -0,0 +1,12 @@
+/* Test that qualifiers are not lost in tertiary operator for pointers to arrays, PR98397 */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu2x" } */
+
+void foo(void)
+{
+	const int (*u)[1];
+	void *v;
+	extern const void *vc;
+	extern typeof(1 ? u : v) vc;
+	extern typeof(1 ? v : u) vc;
+}
diff --git a/gcc/testsuite/gcc.dg/c2x-qual-2.c b/gcc/testsuite/gcc.dg/c2x-qual-2.c
new file mode 100644
index 00000000000..68b897226d3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2x-qual-2.c
@@ -0,0 +1,12 @@
+/* Test that there are no warnings about lost qualifiers for pointers to arrays
+   with pedantic for C2X, PR98397 */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu2x -pedantic" } */
+
+void foo(void)
+{
+	const void *w;
+	const int (*u)[1] = w;
+	void *v;
+	(1 ? u : v);
+}

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2021-08-22 22:20 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-24  6:05 [C PATCH] qualifiers of pointers to arrays in C2X [PR 98397] Uecker, Martin
2021-06-11 19:25 ` Uecker, Martin
2021-08-01  7:20   ` PING^2 " Uecker, Martin
2021-08-12 16:58 ` Joseph Myers
2021-08-22 22:20   ` Uecker, Martin
  -- strict thread matches above, loose matches on Subject: below --
2020-12-21 10:28 Uecker, Martin
2021-01-01  0:01 ` Joseph Myers
2021-01-17 20:18   ` Uecker, Martin
2021-01-18 21:48     ` Joseph Myers

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).