diff mbox series

[C++] Fix constexpr handling of arrays with unknown bound (PR c++/83993)

Message ID 20180124231943.GJ2063@tucnak
State New
Headers show
Series [C++] Fix constexpr handling of arrays with unknown bound (PR c++/83993) | expand

Commit Message

Jakub Jelinek Jan. 24, 2018, 11:19 p.m. UTC
Hi!

In constexpr evaluation of array references for arrays with unknown bounds,
we need to diagnose out of bounds accesses, but really don't know the bounds
at compile time, right now GCC will see nelts as error_mark_node + 1 and
will not consider them a constant expression at all.
From the clang commit message it seems that CWG is leaning towards allowing
&array_with_unknown_bound[0] and array_with_unknown_bound, but disallowing
any other indexes (i.e. assume the array could have zero elements).
The following patch implements that.  Bootstrapped/regtested on x86_64-linux
and i686-linux, ok for trunk?

2018-01-24  Jakub Jelinek  <jakub@redhat.com>

	PR c++/83993
	* constexpr.c (diag_array_subscript): Emit different diagnostics
	if TYPE_DOMAIN (arraytype) is NULL.
	(cxx_eval_array_reference, cxx_eval_store_expression): For arrays
	with NULL TYPE_DOMAIN use size_zero_node as nelts.

	* g++.dg/init/pr83993-1.C: New test.
	* g++.dg/cpp0x/pr83993.C: New test.


	Jakub

Comments

Martin Sebor Jan. 25, 2018, 8:13 p.m. UTC | #1
On 01/24/2018 04:19 PM, Jakub Jelinek wrote:
> Hi!
>
> In constexpr evaluation of array references for arrays with unknown bounds,
> we need to diagnose out of bounds accesses, but really don't know the bounds
> at compile time, right now GCC will see nelts as error_mark_node + 1 and
> will not consider them a constant expression at all.
> From the clang commit message it seems that CWG is leaning towards allowing
> &array_with_unknown_bound[0] and array_with_unknown_bound, but disallowing
> any other indexes (i.e. assume the array could have zero elements).
> The following patch implements that.  Bootstrapped/regtested on x86_64-linux
> and i686-linux, ok for trunk?

Unless your goal is to have GCC 8 reject just some subset of
expressions in the reported test case (and fix the rest in
the future) then there still are other potentially invalid
expressions that GCC will continue to accept.  For example,
the initialization of p in f() is rejected with the patch but
the use of the same expression in g() is still accepted (Clang
silently accepts both).  It could be due to the same problem
as bug 82877.  Other similar issues are being tracked in bug
82876 and 82874.

   extern const int a[];

   constexpr int f ()
   {
     const int *p = &a[7], *q = &a[0];
     return p - q;
   }

   constexpr int g ()
   {
     return &a[7] - &a[0];
   }

   constexpr const int i = f ();
   constexpr const int j = g ();

Regarding the patch, just a couple of minor nits:

The consensus we have reached some time back was not to quote
integer constants so I would suggest to follow it in new code
and take the opportunity to remove the quoting from surrounding
code.

   https://gcc.gnu.org/wiki/DiagnosticsGuidelines#Quoting

In addition, I would suggest to phrase the error as "array
subscript value %E is used *with* array qD" as opposed to
"on array."  If we wanted to make it clearer that it's
the fact that the index is non-zero is the problem mentioning
it in the warning might help ("non-zero array subscript %E...")

Alternatively, since the bounds are unknown is evident from
the type of the array (e.g., "int[]") just: "array subscript
value %E may be outside the bounds of array %qD of type %qT"
might be good enough.

Martin

>
> 2018-01-24  Jakub Jelinek  <jakub@redhat.com>
>
> 	PR c++/83993
> 	* constexpr.c (diag_array_subscript): Emit different diagnostics
> 	if TYPE_DOMAIN (arraytype) is NULL.
> 	(cxx_eval_array_reference, cxx_eval_store_expression): For arrays
> 	with NULL TYPE_DOMAIN use size_zero_node as nelts.
>
> 	* g++.dg/init/pr83993-1.C: New test.
> 	* g++.dg/cpp0x/pr83993.C: New test.
>
> --- gcc/cp/constexpr.c.jj	2018-01-19 23:34:04.897278768 +0100
> +++ gcc/cp/constexpr.c	2018-01-24 13:38:40.572913190 +0100
> @@ -2270,13 +2270,20 @@ diag_array_subscript (const constexpr_ct
>        tree sidx = fold_convert (ssizetype, index);
>        if (DECL_P (array))
>  	{
> -	  error ("array subscript value %qE is outside the bounds "
> -		 "of array %qD of type %qT", sidx, array, arraytype);
> +	  if (TYPE_DOMAIN (arraytype))
> +	    error ("array subscript value %qE is outside the bounds "
> +	           "of array %qD of type %qT", sidx, array, arraytype);
> +	  else
> +	    error ("array subscript value %qE used on array %qD of "
> +		   "type %qT with unknown bounds", sidx, array, arraytype);
>  	  inform (DECL_SOURCE_LOCATION (array), "declared here");
>  	}
> -      else
> +      else if (TYPE_DOMAIN (arraytype))
>  	error ("array subscript value %qE is outside the bounds "
>  	       "of array type %qT", sidx, arraytype);
> +      else
> +	error ("array subscript value %qE used on array of type %qT "
> +	       "with unknown bounds", sidx, arraytype);
>      }
>  }
>
> @@ -2361,7 +2368,12 @@ cxx_eval_array_reference (const constexp
>
>    tree nelts;
>    if (TREE_CODE (TREE_TYPE (ary)) == ARRAY_TYPE)
> -    nelts = array_type_nelts_top (TREE_TYPE (ary));
> +    {
> +      if (TYPE_DOMAIN (TREE_TYPE (ary)))
> +	nelts = array_type_nelts_top (TREE_TYPE (ary));
> +      else
> +	nelts = size_zero_node;
> +    }
>    else if (VECTOR_TYPE_P (TREE_TYPE (ary)))
>      nelts = size_int (TYPE_VECTOR_SUBPARTS (TREE_TYPE (ary)));
>    else
> @@ -3439,7 +3451,12 @@ cxx_eval_store_expression (const constex
>  	  tree nelts, ary;
>  	  ary = TREE_OPERAND (probe, 0);
>  	  if (TREE_CODE (TREE_TYPE (ary)) == ARRAY_TYPE)
> -	    nelts = array_type_nelts_top (TREE_TYPE (ary));
> +	    {
> +	      if (TYPE_DOMAIN (TREE_TYPE (ary)))
> +		nelts = array_type_nelts_top (TREE_TYPE (ary));
> +	      else
> +		nelts = size_zero_node;
> +	    }
>  	  else if (VECTOR_TYPE_P (TREE_TYPE (ary)))
>  	    nelts = size_int (TYPE_VECTOR_SUBPARTS (TREE_TYPE (ary)));
>  	  else
> --- gcc/testsuite/g++.dg/init/pr83993-1.C.jj	2018-01-24 13:45:43.430864528 +0100
> +++ gcc/testsuite/g++.dg/init/pr83993-1.C	2018-01-24 13:44:59.352869530 +0100
> @@ -0,0 +1,11 @@
> +// PR c++/83993
> +// { dg-do compile }
> +
> +extern const int a[];
> +const int *const b = &a[0];
> +
> +int
> +foo ()
> +{
> +  return b[0];
> +}
> --- gcc/testsuite/g++.dg/cpp0x/pr83993.C.jj	2018-01-24 14:09:01.846716177 +0100
> +++ gcc/testsuite/g++.dg/cpp0x/pr83993.C	2018-01-24 14:08:41.246718212 +0100
> @@ -0,0 +1,49 @@
> +// PR c++/83993
> +// { dg-do compile { target c++11 } }
> +// { dg-options "" }
> +
> +extern const int a[];
> +const int b[5] = { 1, 2, 3, 4, 5 };
> +extern const int c[4];
> +constexpr const int *d = &a[0];
> +constexpr const int *d2 = a;
> +constexpr const int *e = &a[1];		// { dg-error "array subscript value '1' used on array 'a' of type 'const int \\\[\\\]' with unknown bounds" }
> +constexpr const int *f = &b[0];
> +constexpr const int *f2 = b;
> +constexpr const int *g = &b[5];
> +constexpr const int *h = &b[6];		// { dg-error "array subscript value '6' is outside the bounds of array 'b' of type 'const int \\\[5\\\]'" }
> +constexpr const int *i = &c[0];
> +constexpr const int *i2 = c;
> +constexpr const int *j = &c[4];
> +constexpr const int *k = &c[5];		// { dg-error "array subscript value '5' is outside the bounds of array 'c' of type 'const int \\\[4\\\]'" }
> +extern const int l[];
> +
> +void
> +foo ()
> +{
> +  extern const int l[3];
> +  constexpr const int *m = &l[0];
> +  constexpr const int *m2 = l;
> +  constexpr const int *n = &l[1];
> +  static_assert (m == m2, "");
> +}
> +
> +constexpr const int *m = &l[0];
> +constexpr const int *m2 = l;
> +constexpr const int *n = &l[1];		// { dg-error "array subscript value '1' used on array 'l' of type 'const int \\\[\\\]' with unknown bounds" }
> +static_assert (d == d2 && f == f2 && i == i2 && m == m2, "");
> +const int o[] = { 1, 2 };
> +constexpr const int *p = &o[0];
> +constexpr const int *p2 = o;
> +constexpr const int *q = &o[2];
> +constexpr const int *r = &o[3];		// { dg-error "array subscript value '3' is outside the bounds of array 'o' of type 'const int \\\[2\\\]'" }
> +struct S { char a; char b[]; } s;
> +constexpr const char *t = &s.b[0];
> +constexpr const char *t2 = s.b;
> +constexpr const char *u = &s.b[1];	// { dg-error "array subscript value '1' used on array of type 'char \\\[\\\]' with unknown bounds" }
> +struct V { int a; };
> +extern V v[];
> +constexpr V *w = &v[0];
> +constexpr V *w2 = v;
> +constexpr int *x = &v[0].a;
> +constexpr int y = a[0];			// { dg-error "the value of 'a' is not usable in a constant expression" }
>
> 	Jakub
>
Jakub Jelinek Jan. 25, 2018, 10:53 p.m. UTC | #2
On Thu, Jan 25, 2018 at 01:13:56PM -0700, Martin Sebor wrote:
> On 01/24/2018 04:19 PM, Jakub Jelinek wrote:
> > Hi!
> > 
> > In constexpr evaluation of array references for arrays with unknown bounds,
> > we need to diagnose out of bounds accesses, but really don't know the bounds
> > at compile time, right now GCC will see nelts as error_mark_node + 1 and
> > will not consider them a constant expression at all.
> > From the clang commit message it seems that CWG is leaning towards allowing
> > &array_with_unknown_bound[0] and array_with_unknown_bound, but disallowing
> > any other indexes (i.e. assume the array could have zero elements).
> > The following patch implements that.  Bootstrapped/regtested on x86_64-linux
> > and i686-linux, ok for trunk?
> 
> Unless your goal is to have GCC 8 reject just some subset of
> expressions in the reported test case (and fix the rest in
> the future) then there still are other potentially invalid
> expressions that GCC will continue to accept.  For example,
> the initialization of p in f() is rejected with the patch but
> the use of the same expression in g() is still accepted (Clang
> silently accepts both).  It could be due to the same problem
> as bug 82877.  Other similar issues are being tracked in bug
> 82876 and 82874.

The goal is to fix the PR and improve the handling of unknown
bound arrays while at it.
There is no time nor good timing for something much more substantial.
After all, with the patch we still don't reject e.g.
some cases if g++.dg/cpp0x/pr83993.C testcase uses the [a-z]2 pointers
instead of corresponding array in the array refs it tests.

>   extern const int a[];
> 
>   constexpr int f ()
>   {
>     const int *p = &a[7], *q = &a[0];
>     return p - q;
>   }
> 
>   constexpr int g ()
>   {
>     return &a[7] - &a[0];
>   }
> 
>   constexpr const int i = f ();
>   constexpr const int j = g ();

I believe the fix for this is to save bodies of constexpr
functions/methods before the folding and do the constexpr evaluations on
those rather than on something we've folded already, but that is
certainly not suitable for GCC8.

> Regarding the patch, just a couple of minor nits:
> 
> The consensus we have reached some time back was not to quote
> integer constants so I would suggest to follow it in new code
> and take the opportunity to remove the quoting from surrounding
> code.
> 
>   https://gcc.gnu.org/wiki/DiagnosticsGuidelines#Quoting

In this case I was just extending existing warning and wanted
consistency with that.  Both can be changed in a GCC9 follow-up,
or if Jason/Nathan want it now, even for GCC8, sure.

> In addition, I would suggest to phrase the error as "array
> subscript value %E is used *with* array qD" as opposed to
> "on array."  If we wanted to make it clearer that it's

That looks reasonable.

> the fact that the index is non-zero is the problem mentioning
> it in the warning might help ("non-zero array subscript %E...")

Well, even zero subscript is wrong if lval is false, i.e. a[0] + 1
rather than e.g. &a[0].  So we'd need to have another wordings
for the case when the index is zero.

> Alternatively, since the bounds are unknown is evident from
> the type of the array (e.g., "int[]") just: "array subscript
> value %E may be outside the bounds of array %qD of type %qT"
> might be good enough.

Dunno, perhaps.

	Jakub
Jason Merrill Jan. 31, 2018, 4:02 p.m. UTC | #3
On Thu, Jan 25, 2018 at 5:53 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Thu, Jan 25, 2018 at 01:13:56PM -0700, Martin Sebor wrote:
>> On 01/24/2018 04:19 PM, Jakub Jelinek wrote:
>> > Hi!
>> >
>> > In constexpr evaluation of array references for arrays with unknown bounds,
>> > we need to diagnose out of bounds accesses, but really don't know the bounds
>> > at compile time, right now GCC will see nelts as error_mark_node + 1 and
>> > will not consider them a constant expression at all.
>> > From the clang commit message it seems that CWG is leaning towards allowing
>> > &array_with_unknown_bound[0] and array_with_unknown_bound, but disallowing
>> > any other indexes (i.e. assume the array could have zero elements).
>> > The following patch implements that.  Bootstrapped/regtested on x86_64-linux
>> > and i686-linux, ok for trunk?
>>
>> Unless your goal is to have GCC 8 reject just some subset of
>> expressions in the reported test case (and fix the rest in
>> the future) then there still are other potentially invalid
>> expressions that GCC will continue to accept.  For example,
>> the initialization of p in f() is rejected with the patch but
>> the use of the same expression in g() is still accepted (Clang
>> silently accepts both).  It could be due to the same problem
>> as bug 82877.  Other similar issues are being tracked in bug
>> 82876 and 82874.
>
> The goal is to fix the PR and improve the handling of unknown
> bound arrays while at it.
> There is no time nor good timing for something much more substantial.
> After all, with the patch we still don't reject e.g.
> some cases if g++.dg/cpp0x/pr83993.C testcase uses the [a-z]2 pointers
> instead of corresponding array in the array refs it tests.
>
>>   extern const int a[];
>>
>>   constexpr int f ()
>>   {
>>     const int *p = &a[7], *q = &a[0];
>>     return p - q;
>>   }
>>
>>   constexpr int g ()
>>   {
>>     return &a[7] - &a[0];
>>   }
>>
>>   constexpr const int i = f ();
>>   constexpr const int j = g ();
>
> I believe the fix for this is to save bodies of constexpr
> functions/methods before the folding and do the constexpr evaluations on
> those rather than on something we've folded already, but that is
> certainly not suitable for GCC8.
>
>> Regarding the patch, just a couple of minor nits:
>>
>> The consensus we have reached some time back was not to quote
>> integer constants so I would suggest to follow it in new code
>> and take the opportunity to remove the quoting from surrounding
>> code.
>>
>>   https://gcc.gnu.org/wiki/DiagnosticsGuidelines#Quoting
>
> In this case I was just extending existing warning and wanted
> consistency with that.  Both can be changed in a GCC9 follow-up,
> or if Jason/Nathan want it now, even for GCC8, sure.
>
>> In addition, I would suggest to phrase the error as "array
>> subscript value %E is used *with* array qD" as opposed to
>> "on array."  If we wanted to make it clearer that it's
>
> That looks reasonable.
>
>> the fact that the index is non-zero is the problem mentioning
>> it in the warning might help ("non-zero array subscript %E...")
>
> Well, even zero subscript is wrong if lval is false, i.e. a[0] + 1
> rather than e.g. &a[0].  So we'd need to have another wordings
> for the case when the index is zero.

But if lval is false we want the value of the array, and if we know
the value we know the bounds, so that shouldn't be needed.

Jason
Jakub Jelinek Jan. 31, 2018, 5:45 p.m. UTC | #4
On Wed, Jan 31, 2018 at 11:02:36AM -0500, Jason Merrill wrote:
> > In this case I was just extending existing warning and wanted
> > consistency with that.  Both can be changed in a GCC9 follow-up,
> > or if Jason/Nathan want it now, even for GCC8, sure.

So, do you want %qE as in the patch or %E?  Note, sidx might
actually not be a INTEGER_CST (as we only verify_constant it which
allows all kinds of expressions), though usually it will be.
E.g. the
      if (!tree_fits_shwi_p (index)
          || (i = tree_to_shwi (index)) < 0)
        {
          diag_array_subscript (ctx, ary, index);
case will call it if index is some reduced_constant_expression_p
which is not INTEGER_CST, initializer_constant_valid_p* has lots
of cases, sure not all apply to integral types.

> > Well, even zero subscript is wrong if lval is false, i.e. a[0] + 1
> > rather than e.g. &a[0].  So we'd need to have another wordings
> > for the case when the index is zero.
> 
> But if lval is false we want the value of the array, and if we know
> the value we know the bounds, so that shouldn't be needed.

You're right, when dereferencing it we'll error already earlier,
because the array of unknown bounds can't be constexpr.

I think the " with unknown bounds" part still might be useful to users,
it can be seen in the type as [], but it might take some time to the users
to figure out that is the reason why we disallow it.

So like this?

2018-01-31  Jakub Jelinek  <jakub@redhat.com>

	PR c++/83993
	* constexpr.c (diag_array_subscript): Emit different diagnostics
	if TYPE_DOMAIN (arraytype) is NULL.
	(cxx_eval_array_reference, cxx_eval_store_expression): For arrays
	with NULL TYPE_DOMAIN use size_zero_node as nelts.

	* g++.dg/init/pr83993-1.C: New test.
	* g++.dg/cpp0x/pr83993.C: New test.

--- gcc/cp/constexpr.c.jj	2018-01-19 23:34:04.897278768 +0100
+++ gcc/cp/constexpr.c	2018-01-24 13:38:40.572913190 +0100
@@ -2270,13 +2270,20 @@ diag_array_subscript (const constexpr_ct
       tree sidx = fold_convert (ssizetype, index);
       if (DECL_P (array))
 	{
-	  error ("array subscript value %qE is outside the bounds "
-		 "of array %qD of type %qT", sidx, array, arraytype);
+	  if (TYPE_DOMAIN (arraytype))
+	    error ("array subscript value %qE is outside the bounds "
+	           "of array %qD of type %qT", sidx, array, arraytype);
+	  else
+	    error ("non-zero array subscript %qE is used with array %qD of "
+		   "type %qT with unknown bounds", sidx, array, arraytype);
 	  inform (DECL_SOURCE_LOCATION (array), "declared here");
 	}
-      else
+      else if (TYPE_DOMAIN (arraytype))
 	error ("array subscript value %qE is outside the bounds "
 	       "of array type %qT", sidx, arraytype);
+      else
+	error ("non-zero array subscript %qE is used with array of type %qT "
+	       "with unknown bounds", sidx, arraytype);
     }
 }
 
@@ -2361,7 +2368,12 @@ cxx_eval_array_reference (const constexp
 
   tree nelts;
   if (TREE_CODE (TREE_TYPE (ary)) == ARRAY_TYPE)
-    nelts = array_type_nelts_top (TREE_TYPE (ary));
+    {
+      if (TYPE_DOMAIN (TREE_TYPE (ary)))
+	nelts = array_type_nelts_top (TREE_TYPE (ary));
+      else
+	nelts = size_zero_node;
+    }
   else if (VECTOR_TYPE_P (TREE_TYPE (ary)))
     nelts = size_int (TYPE_VECTOR_SUBPARTS (TREE_TYPE (ary)));
   else
@@ -3439,7 +3451,12 @@ cxx_eval_store_expression (const constex
 	  tree nelts, ary;
 	  ary = TREE_OPERAND (probe, 0);
 	  if (TREE_CODE (TREE_TYPE (ary)) == ARRAY_TYPE)
-	    nelts = array_type_nelts_top (TREE_TYPE (ary));
+	    {
+	      if (TYPE_DOMAIN (TREE_TYPE (ary)))
+		nelts = array_type_nelts_top (TREE_TYPE (ary));
+	      else
+		nelts = size_zero_node;
+	    }
 	  else if (VECTOR_TYPE_P (TREE_TYPE (ary)))
 	    nelts = size_int (TYPE_VECTOR_SUBPARTS (TREE_TYPE (ary)));
 	  else
--- gcc/testsuite/g++.dg/init/pr83993-1.C.jj	2018-01-24 13:45:43.430864528 +0100
+++ gcc/testsuite/g++.dg/init/pr83993-1.C	2018-01-24 13:44:59.352869530 +0100
@@ -0,0 +1,11 @@
+// PR c++/83993
+// { dg-do compile }
+
+extern const int a[];
+const int *const b = &a[0];
+
+int
+foo ()
+{
+  return b[0];
+}
--- gcc/testsuite/g++.dg/cpp0x/pr83993.C.jj	2018-01-24 14:09:01.846716177 +0100
+++ gcc/testsuite/g++.dg/cpp0x/pr83993.C	2018-01-24 14:08:41.246718212 +0100
@@ -0,0 +1,49 @@
+// PR c++/83993
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+extern const int a[];
+const int b[5] = { 1, 2, 3, 4, 5 };
+extern const int c[4];
+constexpr const int *d = &a[0];
+constexpr const int *d2 = a;
+constexpr const int *e = &a[1];		// { dg-error "non-zero array subscript '1' is used with array 'a' of type 'const int \\\[\\\]' with unknown bounds" }
+constexpr const int *f = &b[0];
+constexpr const int *f2 = b;
+constexpr const int *g = &b[5];
+constexpr const int *h = &b[6];		// { dg-error "array subscript value '6' is outside the bounds of array 'b' of type 'const int \\\[5\\\]'" }
+constexpr const int *i = &c[0];
+constexpr const int *i2 = c;
+constexpr const int *j = &c[4];
+constexpr const int *k = &c[5];		// { dg-error "array subscript value '5' is outside the bounds of array 'c' of type 'const int \\\[4\\\]'" }
+extern const int l[];
+
+void
+foo ()
+{
+  extern const int l[3];
+  constexpr const int *m = &l[0];
+  constexpr const int *m2 = l;
+  constexpr const int *n = &l[1];
+  static_assert (m == m2, "");
+}
+
+constexpr const int *m = &l[0];
+constexpr const int *m2 = l;
+constexpr const int *n = &l[1];		// { dg-error "non-zero array subscript '1' is used with array 'l' of type 'const int \\\[\\\]' with unknown bounds" }
+static_assert (d == d2 && f == f2 && i == i2 && m == m2, "");
+const int o[] = { 1, 2 };
+constexpr const int *p = &o[0];
+constexpr const int *p2 = o;
+constexpr const int *q = &o[2];
+constexpr const int *r = &o[3];		// { dg-error "array subscript value '3' is outside the bounds of array 'o' of type 'const int \\\[2\\\]'" }
+struct S { char a; char b[]; } s;
+constexpr const char *t = &s.b[0];
+constexpr const char *t2 = s.b;
+constexpr const char *u = &s.b[1];	// { dg-error "non-zero array subscript '1' is used with array of type 'char \\\[\\\]' with unknown bounds" }
+struct V { int a; };
+extern V v[];
+constexpr V *w = &v[0];
+constexpr V *w2 = v;
+constexpr int *x = &v[0].a;
+constexpr int y = a[0];			// { dg-error "the value of 'a' is not usable in a constant expression" }


	Jakub
Jason Merrill Jan. 31, 2018, 6:01 p.m. UTC | #5
OK.

On Wed, Jan 31, 2018 at 12:45 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Wed, Jan 31, 2018 at 11:02:36AM -0500, Jason Merrill wrote:
>> > In this case I was just extending existing warning and wanted
>> > consistency with that.  Both can be changed in a GCC9 follow-up,
>> > or if Jason/Nathan want it now, even for GCC8, sure.
>
> So, do you want %qE as in the patch or %E?  Note, sidx might
> actually not be a INTEGER_CST (as we only verify_constant it which
> allows all kinds of expressions), though usually it will be.
> E.g. the
>       if (!tree_fits_shwi_p (index)
>           || (i = tree_to_shwi (index)) < 0)
>         {
>           diag_array_subscript (ctx, ary, index);
> case will call it if index is some reduced_constant_expression_p
> which is not INTEGER_CST, initializer_constant_valid_p* has lots
> of cases, sure not all apply to integral types.
>
>> > Well, even zero subscript is wrong if lval is false, i.e. a[0] + 1
>> > rather than e.g. &a[0].  So we'd need to have another wordings
>> > for the case when the index is zero.
>>
>> But if lval is false we want the value of the array, and if we know
>> the value we know the bounds, so that shouldn't be needed.
>
> You're right, when dereferencing it we'll error already earlier,
> because the array of unknown bounds can't be constexpr.
>
> I think the " with unknown bounds" part still might be useful to users,
> it can be seen in the type as [], but it might take some time to the users
> to figure out that is the reason why we disallow it.
>
> So like this?
>
> 2018-01-31  Jakub Jelinek  <jakub@redhat.com>
>
>         PR c++/83993
>         * constexpr.c (diag_array_subscript): Emit different diagnostics
>         if TYPE_DOMAIN (arraytype) is NULL.
>         (cxx_eval_array_reference, cxx_eval_store_expression): For arrays
>         with NULL TYPE_DOMAIN use size_zero_node as nelts.
>
>         * g++.dg/init/pr83993-1.C: New test.
>         * g++.dg/cpp0x/pr83993.C: New test.
>
> --- gcc/cp/constexpr.c.jj       2018-01-19 23:34:04.897278768 +0100
> +++ gcc/cp/constexpr.c  2018-01-24 13:38:40.572913190 +0100
> @@ -2270,13 +2270,20 @@ diag_array_subscript (const constexpr_ct
>        tree sidx = fold_convert (ssizetype, index);
>        if (DECL_P (array))
>         {
> -         error ("array subscript value %qE is outside the bounds "
> -                "of array %qD of type %qT", sidx, array, arraytype);
> +         if (TYPE_DOMAIN (arraytype))
> +           error ("array subscript value %qE is outside the bounds "
> +                  "of array %qD of type %qT", sidx, array, arraytype);
> +         else
> +           error ("non-zero array subscript %qE is used with array %qD of "
> +                  "type %qT with unknown bounds", sidx, array, arraytype);
>           inform (DECL_SOURCE_LOCATION (array), "declared here");
>         }
> -      else
> +      else if (TYPE_DOMAIN (arraytype))
>         error ("array subscript value %qE is outside the bounds "
>                "of array type %qT", sidx, arraytype);
> +      else
> +       error ("non-zero array subscript %qE is used with array of type %qT "
> +              "with unknown bounds", sidx, arraytype);
>      }
>  }
>
> @@ -2361,7 +2368,12 @@ cxx_eval_array_reference (const constexp
>
>    tree nelts;
>    if (TREE_CODE (TREE_TYPE (ary)) == ARRAY_TYPE)
> -    nelts = array_type_nelts_top (TREE_TYPE (ary));
> +    {
> +      if (TYPE_DOMAIN (TREE_TYPE (ary)))
> +       nelts = array_type_nelts_top (TREE_TYPE (ary));
> +      else
> +       nelts = size_zero_node;
> +    }
>    else if (VECTOR_TYPE_P (TREE_TYPE (ary)))
>      nelts = size_int (TYPE_VECTOR_SUBPARTS (TREE_TYPE (ary)));
>    else
> @@ -3439,7 +3451,12 @@ cxx_eval_store_expression (const constex
>           tree nelts, ary;
>           ary = TREE_OPERAND (probe, 0);
>           if (TREE_CODE (TREE_TYPE (ary)) == ARRAY_TYPE)
> -           nelts = array_type_nelts_top (TREE_TYPE (ary));
> +           {
> +             if (TYPE_DOMAIN (TREE_TYPE (ary)))
> +               nelts = array_type_nelts_top (TREE_TYPE (ary));
> +             else
> +               nelts = size_zero_node;
> +           }
>           else if (VECTOR_TYPE_P (TREE_TYPE (ary)))
>             nelts = size_int (TYPE_VECTOR_SUBPARTS (TREE_TYPE (ary)));
>           else
> --- gcc/testsuite/g++.dg/init/pr83993-1.C.jj    2018-01-24 13:45:43.430864528 +0100
> +++ gcc/testsuite/g++.dg/init/pr83993-1.C       2018-01-24 13:44:59.352869530 +0100
> @@ -0,0 +1,11 @@
> +// PR c++/83993
> +// { dg-do compile }
> +
> +extern const int a[];
> +const int *const b = &a[0];
> +
> +int
> +foo ()
> +{
> +  return b[0];
> +}
> --- gcc/testsuite/g++.dg/cpp0x/pr83993.C.jj     2018-01-24 14:09:01.846716177 +0100
> +++ gcc/testsuite/g++.dg/cpp0x/pr83993.C        2018-01-24 14:08:41.246718212 +0100
> @@ -0,0 +1,49 @@
> +// PR c++/83993
> +// { dg-do compile { target c++11 } }
> +// { dg-options "" }
> +
> +extern const int a[];
> +const int b[5] = { 1, 2, 3, 4, 5 };
> +extern const int c[4];
> +constexpr const int *d = &a[0];
> +constexpr const int *d2 = a;
> +constexpr const int *e = &a[1];                // { dg-error "non-zero array subscript '1' is used with array 'a' of type 'const int \\\[\\\]' with unknown bounds" }
> +constexpr const int *f = &b[0];
> +constexpr const int *f2 = b;
> +constexpr const int *g = &b[5];
> +constexpr const int *h = &b[6];                // { dg-error "array subscript value '6' is outside the bounds of array 'b' of type 'const int \\\[5\\\]'" }
> +constexpr const int *i = &c[0];
> +constexpr const int *i2 = c;
> +constexpr const int *j = &c[4];
> +constexpr const int *k = &c[5];                // { dg-error "array subscript value '5' is outside the bounds of array 'c' of type 'const int \\\[4\\\]'" }
> +extern const int l[];
> +
> +void
> +foo ()
> +{
> +  extern const int l[3];
> +  constexpr const int *m = &l[0];
> +  constexpr const int *m2 = l;
> +  constexpr const int *n = &l[1];
> +  static_assert (m == m2, "");
> +}
> +
> +constexpr const int *m = &l[0];
> +constexpr const int *m2 = l;
> +constexpr const int *n = &l[1];                // { dg-error "non-zero array subscript '1' is used with array 'l' of type 'const int \\\[\\\]' with unknown bounds" }
> +static_assert (d == d2 && f == f2 && i == i2 && m == m2, "");
> +const int o[] = { 1, 2 };
> +constexpr const int *p = &o[0];
> +constexpr const int *p2 = o;
> +constexpr const int *q = &o[2];
> +constexpr const int *r = &o[3];                // { dg-error "array subscript value '3' is outside the bounds of array 'o' of type 'const int \\\[2\\\]'" }
> +struct S { char a; char b[]; } s;
> +constexpr const char *t = &s.b[0];
> +constexpr const char *t2 = s.b;
> +constexpr const char *u = &s.b[1];     // { dg-error "non-zero array subscript '1' is used with array of type 'char \\\[\\\]' with unknown bounds" }
> +struct V { int a; };
> +extern V v[];
> +constexpr V *w = &v[0];
> +constexpr V *w2 = v;
> +constexpr int *x = &v[0].a;
> +constexpr int y = a[0];                        // { dg-error "the value of 'a' is not usable in a constant expression" }
>
>
>         Jakub
diff mbox series

Patch

--- gcc/cp/constexpr.c.jj	2018-01-19 23:34:04.897278768 +0100
+++ gcc/cp/constexpr.c	2018-01-24 13:38:40.572913190 +0100
@@ -2270,13 +2270,20 @@  diag_array_subscript (const constexpr_ct
       tree sidx = fold_convert (ssizetype, index);
       if (DECL_P (array))
 	{
-	  error ("array subscript value %qE is outside the bounds "
-		 "of array %qD of type %qT", sidx, array, arraytype);
+	  if (TYPE_DOMAIN (arraytype))
+	    error ("array subscript value %qE is outside the bounds "
+	           "of array %qD of type %qT", sidx, array, arraytype);
+	  else
+	    error ("array subscript value %qE used on array %qD of "
+		   "type %qT with unknown bounds", sidx, array, arraytype);
 	  inform (DECL_SOURCE_LOCATION (array), "declared here");
 	}
-      else
+      else if (TYPE_DOMAIN (arraytype))
 	error ("array subscript value %qE is outside the bounds "
 	       "of array type %qT", sidx, arraytype);
+      else
+	error ("array subscript value %qE used on array of type %qT "
+	       "with unknown bounds", sidx, arraytype);
     }
 }
 
@@ -2361,7 +2368,12 @@  cxx_eval_array_reference (const constexp
 
   tree nelts;
   if (TREE_CODE (TREE_TYPE (ary)) == ARRAY_TYPE)
-    nelts = array_type_nelts_top (TREE_TYPE (ary));
+    {
+      if (TYPE_DOMAIN (TREE_TYPE (ary)))
+	nelts = array_type_nelts_top (TREE_TYPE (ary));
+      else
+	nelts = size_zero_node;
+    }
   else if (VECTOR_TYPE_P (TREE_TYPE (ary)))
     nelts = size_int (TYPE_VECTOR_SUBPARTS (TREE_TYPE (ary)));
   else
@@ -3439,7 +3451,12 @@  cxx_eval_store_expression (const constex
 	  tree nelts, ary;
 	  ary = TREE_OPERAND (probe, 0);
 	  if (TREE_CODE (TREE_TYPE (ary)) == ARRAY_TYPE)
-	    nelts = array_type_nelts_top (TREE_TYPE (ary));
+	    {
+	      if (TYPE_DOMAIN (TREE_TYPE (ary)))
+		nelts = array_type_nelts_top (TREE_TYPE (ary));
+	      else
+		nelts = size_zero_node;
+	    }
 	  else if (VECTOR_TYPE_P (TREE_TYPE (ary)))
 	    nelts = size_int (TYPE_VECTOR_SUBPARTS (TREE_TYPE (ary)));
 	  else
--- gcc/testsuite/g++.dg/init/pr83993-1.C.jj	2018-01-24 13:45:43.430864528 +0100
+++ gcc/testsuite/g++.dg/init/pr83993-1.C	2018-01-24 13:44:59.352869530 +0100
@@ -0,0 +1,11 @@ 
+// PR c++/83993
+// { dg-do compile }
+
+extern const int a[];
+const int *const b = &a[0];
+
+int
+foo ()
+{
+  return b[0];
+}
--- gcc/testsuite/g++.dg/cpp0x/pr83993.C.jj	2018-01-24 14:09:01.846716177 +0100
+++ gcc/testsuite/g++.dg/cpp0x/pr83993.C	2018-01-24 14:08:41.246718212 +0100
@@ -0,0 +1,49 @@ 
+// PR c++/83993
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+extern const int a[];
+const int b[5] = { 1, 2, 3, 4, 5 };
+extern const int c[4];
+constexpr const int *d = &a[0];
+constexpr const int *d2 = a;
+constexpr const int *e = &a[1];		// { dg-error "array subscript value '1' used on array 'a' of type 'const int \\\[\\\]' with unknown bounds" }
+constexpr const int *f = &b[0];
+constexpr const int *f2 = b;
+constexpr const int *g = &b[5];
+constexpr const int *h = &b[6];		// { dg-error "array subscript value '6' is outside the bounds of array 'b' of type 'const int \\\[5\\\]'" }
+constexpr const int *i = &c[0];
+constexpr const int *i2 = c;
+constexpr const int *j = &c[4];
+constexpr const int *k = &c[5];		// { dg-error "array subscript value '5' is outside the bounds of array 'c' of type 'const int \\\[4\\\]'" }
+extern const int l[];
+
+void
+foo ()
+{
+  extern const int l[3];
+  constexpr const int *m = &l[0];
+  constexpr const int *m2 = l;
+  constexpr const int *n = &l[1];
+  static_assert (m == m2, "");
+}
+
+constexpr const int *m = &l[0];
+constexpr const int *m2 = l;
+constexpr const int *n = &l[1];		// { dg-error "array subscript value '1' used on array 'l' of type 'const int \\\[\\\]' with unknown bounds" }
+static_assert (d == d2 && f == f2 && i == i2 && m == m2, "");
+const int o[] = { 1, 2 };
+constexpr const int *p = &o[0];
+constexpr const int *p2 = o;
+constexpr const int *q = &o[2];
+constexpr const int *r = &o[3];		// { dg-error "array subscript value '3' is outside the bounds of array 'o' of type 'const int \\\[2\\\]'" }
+struct S { char a; char b[]; } s;
+constexpr const char *t = &s.b[0];
+constexpr const char *t2 = s.b;
+constexpr const char *u = &s.b[1];	// { dg-error "array subscript value '1' used on array of type 'char \\\[\\\]' with unknown bounds" }
+struct V { int a; };
+extern V v[];
+constexpr V *w = &v[0];
+constexpr V *w2 = v;
+constexpr int *x = &v[0].a;
+constexpr int y = a[0];			// { dg-error "the value of 'a' is not usable in a constant expression" }