diff mbox series

[C] qualifiers of pointers to arrays in C2X [PR 98397]

Message ID 5690541802513d86355283933c811c171410543e.camel@med.uni-goettingen.de
State New
Headers show
Series [C] qualifiers of pointers to arrays in C2X [PR 98397] | expand

Commit Message

Uecker, Martin May 24, 2021, 6:05 a.m. UTC
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.

Comments

Uecker, Martin June 11, 2021, 7:25 p.m. UTC | #1
(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) { }
Uecker, Martin Aug. 1, 2021, 7:20 a.m. UTC | #2
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) { }
Joseph Myers Aug. 12, 2021, 4:58 p.m. UTC | #3
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.
Uecker, Martin Aug. 22, 2021, 10:20 p.m. UTC | #4
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

>
diff mbox series

Patch

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) { }