diff mbox

c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression

Message ID 56FC5840.6000503@gmail.com
State New
Headers show

Commit Message

Martin Sebor March 30, 2016, 10:50 p.m. UTC
On 03/30/2016 01:25 PM, Jason Merrill wrote:
> On 03/30/2016 12:32 PM, Martin Sebor wrote:
>> On 03/30/2016 09:30 AM, Jason Merrill wrote:
>>> On 03/29/2016 11:57 PM, Martin Sebor wrote:
>>>>> Are we confident that arr[0] won't make it here as
>>>>> POINTER_PLUS_EXPR or
>>>>> some such?
>>>>
>>>> I'm as confident as I can be given that this is my first time
>>>> working in this area.  Which piece of code or what assumption
>>>> in particular are you concerned about?
>>>
>>> I want to be sure that we don't fold these conditions to false.
>>>
>>> constexpr int *ip = 0;
>>> constexpr struct A { int ar[3]; } *ap = 0;
>>>
>>> static_assert(&ip[0] == 0);
>>> static_assert(&(ap->ar[0]) == 0);
>>
>> I see.  Thanks for clarifying.  The asserts pass.  The expressions
>> are folded earlier on (in fact, as we discussed, the second one
>> too early and is accepted even though it's undefined and should be
>> rejected in a constexpr context) and never reach fold_comparison.
>
> Good, then let's add at least the first to one of the tests.

I've enhanced the new constexpr-nullptr-1.C test to verify this.
I added assertions exercising the relational expressions as well
and for sanity compiled the test with CLang.  It turns out that
it rejects the relational expressions with null pointers like
the one below complaining they aren't constant.

   constexpr int i = 0;
   constexpr const int *p = &i;
   constexpr int *q = 0;

   static_assert (q < p, "q < p");

I ended up not using a static_assert for the unspecified subset
even though GCC accepts it.  It seems that they really aren't
valid constant expressions (their results are unspecified for
null pointers) and should be rejected.  Do you agree?  (If you
do, I'll add these cases to c++/70248 that's already tracking
another unspecified case that GCC incorrectly accepts).

>> +           /* Avoid folding references to struct members at offset 0 to
>> +          prevent tests like '&ptr->firstmember == 0' from getting
>> +          eliminated.  When ptr is null, although the -> expression
>> +          is strictly speaking invalid, GCC retains it as a matter
>> +          of QoI.  See PR c/44555. */
>> +           && (TREE_CODE (op0) != ADDR_EXPR
>> +           || TREE_CODE (TREE_OPERAND (op0, 0)) != COMPONENT_REF
>> +           || compare_tree_int (DECL_FIELD_OFFSET ((TREE_OPERAND
>> +                           (TREE_OPERAND (op0, 0), 1))), 0))
>
> Can we look at offset/bitpos here rather than examine the tree structure
> of op0?  get_inner_reference already examined it for us.

Good suggestion, thanks!

>
> Also, it looks like you aren't handling the case with the operands
> switched, i.e. 0 == p and such.

Based on my testing and reading the code I believe the caller
(fold_binary_loc) arranges for the constant argument to always
come second in comparisons.  I've added a comment to the code
to make it clear.

Attached is an updated patch retested on x86_64.

Martin

Comments

Jason Merrill March 31, 2016, 4:22 a.m. UTC | #1
On 03/30/2016 06:50 PM, Martin Sebor wrote:
> On 03/30/2016 01:25 PM, Jason Merrill wrote:
>> On 03/30/2016 12:32 PM, Martin Sebor wrote:
>>> On 03/30/2016 09:30 AM, Jason Merrill wrote:
>>>> On 03/29/2016 11:57 PM, Martin Sebor wrote:
>>>>>> Are we confident that arr[0] won't make it here as
>>>>>> POINTER_PLUS_EXPR or
>>>>>> some such?
>>>>>
>>>>> I'm as confident as I can be given that this is my first time
>>>>> working in this area.  Which piece of code or what assumption
>>>>> in particular are you concerned about?
>>>>
>>>> I want to be sure that we don't fold these conditions to false.
>>>>
>>>> constexpr int *ip = 0;
>>>> constexpr struct A { int ar[3]; } *ap = 0;
>>>>
>>>> static_assert(&ip[0] == 0);
>>>> static_assert(&(ap->ar[0]) == 0);
>>>
>>> I see.  Thanks for clarifying.  The asserts pass.  The expressions
>>> are folded earlier on (in fact, as we discussed, the second one
>>> too early and is accepted even though it's undefined and should be
>>> rejected in a constexpr context) and never reach fold_comparison.
>>
>> Good, then let's add at least the first to one of the tests.
>
> I've enhanced the new constexpr-nullptr-1.C test to verify this.
> I added assertions exercising the relational expressions as well
> and for sanity compiled the test with CLang.  It turns out that
> it rejects the relational expressions with null pointers like
> the one below complaining they aren't constant.
>
>    constexpr int i = 0;
>    constexpr const int *p = &i;
>    constexpr int *q = 0;
>
>    static_assert (q < p, "q < p");
>
> I ended up not using a static_assert for the unspecified subset
> even though GCC accepts it.  It seems that they really aren't
> valid constant expressions (their results are unspecified for
> null pointers) and should be rejected.  Do you agree?  (If you
> do, I'll add these cases to c++/70248 that's already tracking
> another unspecified case that GCC incorrectly accepts).

I agree.

>>> +           /* Avoid folding references to struct members at offset 0 to
>>> +          prevent tests like '&ptr->firstmember == 0' from getting
>>> +          eliminated.  When ptr is null, although the -> expression
>>> +          is strictly speaking invalid, GCC retains it as a matter
>>> +          of QoI.  See PR c/44555. */
>>> +           && (TREE_CODE (op0) != ADDR_EXPR
>>> +           || TREE_CODE (TREE_OPERAND (op0, 0)) != COMPONENT_REF
>>> +           || compare_tree_int (DECL_FIELD_OFFSET ((TREE_OPERAND
>>> +                           (TREE_OPERAND (op0, 0), 1))), 0))
>>
>> Can we look at offset/bitpos here rather than examine the tree structure
>> of op0?  get_inner_reference already examined it for us.
>
> Good suggestion, thanks!

But you're still examining the tree structure:

> +	       && (TREE_CODE (op0) != ADDR_EXPR
> +		   || TREE_CODE (TREE_OPERAND (op0, 0)) != COMPONENT_REF
> +		   || bitpos0 != 0)

Here instead of looking at op0 we can check (offset0 == NULL_TREE && 
bitpos0 != 0), which indicates a constant non-zero offset.

> +	       && TREE_CODE (arg1) == INTEGER_CST
> +	       && integer_zerop (arg1))

And here you don't need the check for INTEGER_CST.

>> Also, it looks like you aren't handling the case with the operands
>> switched, i.e. 0 == p and such.
>
> Based on my testing and reading the code I believe the caller
> (fold_binary_loc) arranges for the constant argument to always
> come second in comparisons.  I've added a comment to the code
> to make it clear.

Great.

Jason
diff mbox

Patch

PR c++/67376 - [5/6 regression] Comparison with pointer to past-the-end
	of array fails inside constant expression
PR c++/70170 - [6 regression] bogus not a constant expression error comparing
	pointer to array to null
PR c++/70172 - incorrect reinterpret_cast from integer to pointer error
	on invalid constexpr initialization
PR c++/70228 - insufficient detail in diagnostics for a constexpr out of bounds
	array subscript

gcc/testsuite/ChangeLog:
2016-03-30  Martin Sebor  <msebor@redhat.com>

	PR c++/67376
	PR c++/70170
	PR c++/70172
	PR c++/70228
	* g++.dg/cpp0x/constexpr-array-ptr10.C: New test.
	* g++.dg/cpp0x/constexpr-array-ptr9.C: New test.
	* g++.dg/cpp0x/constexpr-nullptr-1.C: New test.
	* g++.dg/cpp0x/constexpr-array5.C: Adjust text of expected diagnostic.
	* g++.dg/cpp0x/constexpr-string.C: Same.
	* g++.dg/cpp0x/constexpr-wstring2.C: Same.
	* g++.dg/cpp0x/pr65398.C: Same.
	* g++.dg/ext/constexpr-vla1.C: Same.
	* g++.dg/ext/constexpr-vla2.C: Same.
	* g++.dg/ext/constexpr-vla3.C: Same.
	* g++.dg/ubsan/pr63956.C: Same.

gcc/cp/ChangeLog:
2016-03-30  Martin Sebor  <msebor@redhat.com>

	PR c++/67376
	PR c++/70170
	PR c++/70172
	PR c++/70228
	* constexpr.c (diag_array_subscript): New function.
	(cxx_eval_array_reference): Detect out of bounds array indices.

gcc/ChangeLog:
2016-03-30  Martin Sebor  <msebor@redhat.com>

	PR c++/67376
	* fold-const.c (maybe_nonzero_address): New function.
	(fold_comparison): Call it.  Fold equality and relational
	expressions involving null pointers.
	(tree_single_nonzero_warnv_p): Call maybe_nonzero_address.

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 8ea7111..5d1b8b3 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -1837,6 +1837,30 @@  find_array_ctor_elt (tree ary, tree dindex, bool insert = false)
   return -1;
 }
 
+/* Under the control of CTX, issue a detailed diagnostic for
+   an out-of-bounds subscript INDEX into the expression ARRAY.  */
+
+static void
+diag_array_subscript (const constexpr_ctx *ctx, tree array, tree index)
+{
+  if (!ctx->quiet)
+    {
+      tree arraytype = TREE_TYPE (array);
+
+      /* Convert the unsigned array subscript to a signed integer to avoid
+	 printing huge numbers for small negative values.  */
+      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);
+	  inform (DECL_SOURCE_LOCATION (array), "declared here");
+	}
+      else
+	error ("array subscript value %qE is outside the bounds "
+	       "of array type %qT", sidx, arraytype);
+    }
+}
 
 /* Subroutine of cxx_eval_constant_expression.
    Attempt to reduce a reference to an array slot.  */
@@ -1885,8 +1909,7 @@  cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
   if (!tree_fits_shwi_p (index)
       || (i = tree_to_shwi (index)) < 0)
     {
-      if (!ctx->quiet)
-	error ("negative array subscript");
+      diag_array_subscript (ctx, ary, index);
       *non_constant_p = true;
       return t;
     }
@@ -1898,8 +1921,7 @@  cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
   VERIFY_CONSTANT (nelts);
   if (!tree_int_cst_lt (index, nelts))
     {
-      if (!ctx->quiet)
-	error ("array subscript out of bound");
+      diag_array_subscript (ctx, ary, index);
       *non_constant_p = true;
       return t;
     }
diff --git a/gcc/fold-const.c b/gcc/fold-const.c
index 44fe2a2..16a734b 100644
--- a/gcc/fold-const.c
+++ b/gcc/fold-const.c
@@ -8336,6 +8336,20 @@  pointer_may_wrap_p (tree base, tree offset, HOST_WIDE_INT bitpos)
   return total.to_uhwi () > (unsigned HOST_WIDE_INT) size;
 }
 
+/* Return a positive integer when the symbol DECL is known to have
+   a nonzero address, zero when it's known not to (e.g., it's a weak
+   symbol), and a negative integer when the symbol is not yet in the
+   symbol table and so whether or not its address is zero is unknown.  */
+static int
+maybe_nonzero_address (tree decl)
+{
+  if (DECL_P (decl) && decl_in_symtab_p (decl))
+    if (struct symtab_node *symbol = symtab_node::get_create (decl))
+      return symbol->nonzero_address ();
+
+  return -1;
+}
+
 /* Subroutine of fold_binary.  This routine performs all of the
    transformations that are common to the equality/inequality
    operators (EQ_EXPR and NE_EXPR) and the ordering operators
@@ -8636,6 +8650,42 @@  fold_comparison (location_t loc, enum tree_code code, tree type,
 	    base1 = build_fold_addr_expr_loc (loc, base1);
 	  return fold_build2_loc (loc, code, type, base0, base1);
 	}
+      /* Comparison between an ordinary (non-weak) symbol and a null
+	 pointer can be eliminated since such symbols must have a non
+	 null address.  In C, relational expressions between pointers
+	 to objects and null pointers are undefined.  The results
+	 below follow the C++ rules with the additional property that
+	 every object pointer compares greater than a null pointer.
+      */
+      else if (DECL_P (base0)
+	       && maybe_nonzero_address (base0) > 0
+	       /* Avoid folding references to struct members at offset 0 to
+		  prevent tests like '&ptr->firstmember == 0' from getting
+		  eliminated.  When ptr is null, although the -> expression
+		  is strictly speaking invalid, GCC retains it as a matter
+		  of QoI.  See PR c/44555. */
+	       && (TREE_CODE (op0) != ADDR_EXPR
+		   || TREE_CODE (TREE_OPERAND (op0, 0)) != COMPONENT_REF
+		   || bitpos0 != 0)
+	       /* The caller guarantees that when one of the arguments is
+		  constant it is second.  */
+	       && TREE_CODE (arg1) == INTEGER_CST
+	       && integer_zerop (arg1))
+	{
+	  switch (code)
+	    {
+	    case EQ_EXPR:
+	    case LE_EXPR:
+	    case LT_EXPR:
+	      return boolean_false_node;
+	    case GE_EXPR:
+	    case GT_EXPR:
+	    case NE_EXPR:
+	      return boolean_true_node;
+	    default:
+	      gcc_unreachable ();
+	    }
+	}
     }
 
   /* Transform comparisons of the form X +- C1 CMP Y +- C2 to
@@ -13506,16 +13556,9 @@  tree_single_nonzero_warnv_p (tree t, bool *strict_overflow_p)
 	/* For objects in symbol table check if we know they are non-zero.
 	   Don't do anything for variables and functions before symtab is built;
 	   it is quite possible that they will be declared weak later.  */
-	if (DECL_P (base) && decl_in_symtab_p (base))
-	  {
-	    struct symtab_node *symbol;
-
-	    symbol = symtab_node::get_create (base);
-	    if (symbol)
-	      return symbol->nonzero_address ();
-	    else
-	      return false;
-	  }
+	int nonzero_addr = maybe_nonzero_address (base);
+	if (nonzero_addr >= 0)
+	  return nonzero_addr;
 
 	/* Function local objects are never NULL.  */
 	if (DECL_P (base)
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr10.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr10.C
new file mode 100644
index 0000000..f75b3c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr10.C
@@ -0,0 +1,112 @@ 
+// PR c++/67376 - [5/6 regression] Comparison with pointer to past-the-end
+//                of array fails inside constant expression
+// This test verifies the aspect of the bug raised in comment #10,
+// specifically comparing pointers to null.  The basic regression test
+// is in g++.dg/cpp0x/constexpr-67376.C.
+// Note also that while the description of the bug talks about pointers
+// pointing past the end of arrays but the prolem is more general than
+// that and involves all constexpr object pointers.
+
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "-Wall -Wextra" }
+
+namespace A {
+
+extern int i;
+
+constexpr int *p0 = &i;
+
+constexpr bool b0  = p0;        // { dg-warning "address of .A::i." }
+constexpr bool b1  = p0 == 0;   // { dg-warning "address of .A::i." }
+constexpr bool b2  = p0 != 0;   // { dg-warning "address of .A::i." }
+constexpr bool b3  = p0 <  0;   // { dg-warning "ordered comparison" }
+constexpr bool b4  = p0 <= 0;   // { dg-warning "ordered comparison" }
+constexpr bool b5  = p0 >  0;   // { dg-warning "ordered comparison" }
+constexpr bool b6  = p0 >= 0;   // { dg-warning "ordered comparison" }
+
+constexpr bool b7  = !p0;       // { dg-warning "address of .A::i." }
+constexpr bool b8  = 0 == p0;   // { dg-warning "address of .A::i." }
+constexpr bool b9  = 0 != p0;   // { dg-warning "address of .A::i." }
+constexpr bool b10 = 0 <  p0;   // { dg-warning "ordered comparison" }
+constexpr bool b11 = 0 <= p0;   // { dg-warning "ordered comparison" }
+constexpr bool b12 = 0 >  p0;   // { dg-warning "ordered comparison" }
+constexpr bool b13 = 0 >= p0;   // { dg-warning "ordered comparison" }
+
+}
+
+namespace B {
+
+// PR c++/70172 - incorrect reinterpret_cast from integer to pointer
+// error on invalid constexpr initialization
+
+struct S { int a, b[1]; } s;
+
+constexpr S *p0 = &s;
+
+constexpr int *q0 = p0->b;      // { dg-bogus "reinterpret_cast from integer to pointer" }
+
+}
+
+namespace WeakRefTest1 {
+
+extern __attribute__ ((weak)) int i;
+
+constexpr int *p0 = &i;
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wextra"
+// Suppress warning: ordered comparison of pointer with integer zero
+
+constexpr bool b0  = p0;        // { dg-error "not a constant expression" }
+constexpr bool b1  = p0 == 0;   // { dg-error "not a constant expression" }
+constexpr bool b2  = p0 != 0;   // { dg-error "not a constant expression" }
+constexpr bool b4  = p0 <= 0;   // { dg-error "not a constant expression" }
+constexpr bool b5  = p0 >  0;   // { dg-error "not a constant expression" }
+
+constexpr bool b7  = !p0;       // { dg-error "not a constant expression" }
+constexpr bool b8  = 0 == p0;   // { dg-error "not a constant expression" }
+constexpr bool b9  = 0 != p0;   // { dg-error "not a constant expression" }
+constexpr bool b10 = 0 <  p0;   // { dg-error "not a constant expression" }
+constexpr bool b13 = 0 >= p0;   // { dg-error "not a constant expression" }
+
+// The following are accepted as constant expressions due to bug c++/70196.
+constexpr bool b3  = p0 <  0;
+constexpr bool b6  = p0 >= 0;
+constexpr bool b11 = 0 <= p0;
+constexpr bool b12 = 0 >  p0;
+
+#pragma GCC diagnostic pop
+
+}
+
+namespace WeakRefTest2 {
+
+extern __attribute__ ((weak)) int i;
+
+constexpr int *p1 = &i + 1;
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wextra"
+// Suppress warning: ordered comparison of pointer with integer zero
+
+constexpr bool b0  = p1;        // { dg-error "not a constant expression" }
+constexpr bool b1  = p1 == 0;   // { dg-error "not a constant expression" }
+constexpr bool b2  = p1 != 0;   // { dg-error "not a constant expression" }
+constexpr bool b4  = p1 <= 0;   // { dg-error "not a constant expression" }
+constexpr bool b5  = p1 >  0;   // { dg-error "not a constant expression" }
+
+constexpr bool b7  = !p1;       // { dg-error "not a constant expression" }
+constexpr bool b8  = 0 == p1;   // { dg-error "not a constant expression" }
+constexpr bool b9  = 0 != p1;   // { dg-error "not a constant expression" }
+constexpr bool b10 = 0 <  p1;   // { dg-error "not a constant expression" }
+constexpr bool b13 = 0 >= p1;   // { dg-error "not a constant expression" }
+
+// The following are accepted as constant expressions due to bug c++/70196.
+// constexpr bool b3  = p1 <  0;
+// constexpr bool b6  = p1 >= 0;
+// constexpr bool b11 = 0 <= p1;
+// constexpr bool b12 = 0 >  p1;
+
+#pragma GCC diagnostic pop
+
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr9.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr9.C
new file mode 100644
index 0000000..f0250cb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr9.C
@@ -0,0 +1,57 @@ 
+// PR c++/67376 - [5/6 regression] Comparison with pointer to past-the-end
+//     of array fails inside constant expression
+// { dg-do compile { target c++11 } }
+
+int a [2];
+
+constexpr const int* pa[] = {
+  a,
+  a + 0,
+  a + 1,
+  a + 2,
+  &a [0],
+  &a [0] + 0,
+  &a [0] + 1,
+  &a [0] + 2,
+  &a [1],
+  &a [1] - 1,
+  &a [1] + 0,
+  &a [1] + 1,
+  &a [2] - 2,
+  &a [2] - 1,
+  &a [2] + 0
+};
+
+#define Assert(e) static_assert ((e), #e)
+
+Assert (!(a == 0));
+Assert (!(a == (int*)0));
+Assert (!(a == nullptr));
+
+Assert (a != 0);
+Assert (a != (int*)0);
+Assert (a != nullptr);
+
+Assert (!(0 == a));
+Assert (!((int*)0 == a));
+Assert (!(nullptr == a));
+
+Assert (0 != a);
+Assert ((int*)0 != a);
+Assert (nullptr != a);
+
+bool constexpr test_eq (unsigned inx)
+{
+  return inx ? pa [inx - 1] == 0 && 0 == pa [inx - 1]
+    && test_eq (inx - 1) : pa [inx] == 0 && 0 == pa [inx];
+}
+
+Assert (!test_eq (sizeof pa / sizeof *pa));
+
+bool constexpr test_ne (unsigned inx)
+{
+  return inx ? pa [inx - 1] != 0 && 0 != pa [inx - 1]
+    && test_ne (inx - 1) : pa [inx] != 0 && 0 != pa [inx];
+}
+
+Assert (test_ne (sizeof pa / sizeof *pa));
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-array5.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-array5.C
index 4605b4b..00dfd6d 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-array5.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-array5.C
@@ -3,7 +3,7 @@ 
 
 // Reliable ICE
 constexpr int n[3] = {};
-constexpr int k = n[-1];            // { dg-error "negative" }
+constexpr int k = n[-1];            // { dg-error "array subscript" }
 
 // Some random byte
-constexpr char c = "foo"[-1000];    // { dg-error "negative" }
+constexpr char c = "foo"[-1000];    // { dg-error "array subscript" }
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-1.C
new file mode 100644
index 0000000..420a04b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-1.C
@@ -0,0 +1,125 @@ 
+// Verify the correctness of folding relational expressions involving
+// pointers to array elements and struct data members and null pointers.
+// Although the C semantics of relational expressions are only defined
+// for pointers to objects, C++ makes them well-defined when
+// (nullptr < p) yields true.  See the discussion of the patch for
+// c++/67376 on gcc-patches for additional background.
+
+// { dg-do compile { target c++11 } }
+// { dg-options "-fdump-tree-optimized" }
+
+// Runtime assert.  Used for potentially invalid expressions.
+#define RA(e)  ((e) ? (void)0 : __builtin_abort ())
+
+// Static assert.  Used for valid core constant expressions.
+#define SA(e)  static_assert ((e), #e)
+
+void test_first_array_element ()
+{
+  static constexpr int a[] = { 0 };
+  constexpr const int *null = 0;
+  constexpr const int *pi = a;
+
+  // The following are valid constant expressions since in &*pi
+  // the '&*' "cancel each other out."
+  SA (!(null == &*pi));
+  SA ( (null != &*pi));
+
+  // The validity of the relational expressions involving null
+  // pointers in a constexpr context is questionable.  Use a run
+  // time assertion to verify these.
+  RA ( (null <  &*pi));
+  RA ( (null <= &*pi));
+  RA (!(null >  &*pi));
+  RA (!(null >= &*pi));
+
+  SA (!(&*pi == null));
+  SA ( (&*pi != null));
+  RA (!(&*pi <  null));
+  RA (!(&*pi <= null));
+  RA ( (&*pi >  null));
+  RA ( (&*pi >= null));
+
+  // The following are valid constant expressions since &pi [0] is
+  // equivalent to &*pi.
+  SA (!(null == &pi [0]));
+  SA ( (null != &pi [0]));
+  RA ( (null <  &pi [0]));
+  RA ( (null <= &pi [0]));
+  RA (!(null >  &pi [0]));
+  RA (!(null >= &pi [0]));
+
+  SA (!(&pi [0] == null));
+  SA ( (&pi [0] != null));
+  RA (!(&pi [0] <  null));
+  RA (!(&pi [0] <= null));
+  RA ( (&pi [0] >  null));
+  RA ( (&pi [0] >= null));
+}
+
+void test_first_null_array_element ()
+{
+  constexpr const int *pi = 0;
+  constexpr const int *qi = 0;
+
+  // The following are valid constant expressions since in &*qi
+  // the '&*' "cancel each other out."
+  SA ( (pi == &*qi));
+  SA (!(pi != &*qi));
+
+  // The validity of the relational expressions involving null
+  // pointers in a constexpr context is questionable.
+  RA (!(pi <  &*qi));
+  RA ( (pi <= &*qi));
+  RA (!(pi >  &*qi));
+  RA ( (pi >= &*qi));
+
+  SA ( (&*qi == pi));
+  SA (!(&*qi != pi));
+  RA (!(&*qi <  pi));
+  RA ( (&*qi <= pi));
+  RA (!(&*qi >  pi));
+  RA ( (&*qi >= pi));
+
+  // The following are valid constant expressions since &qi [0] is
+  // equivalent to &*qi.
+  SA ( (pi == &qi [0]));
+  SA (!(pi != &qi [0]));
+  RA (!(pi <  &qi [0]));
+  RA ( (pi <= &qi [0]));
+  RA (!(pi >  &qi [0]));
+  RA ( (pi >= &qi [0]));
+
+  SA ( (&qi [0] == pi));
+  SA (!(&qi [0] != pi));
+  RA (!(&qi [0] <  pi));
+  RA ( (&qi [0] <= pi));
+  RA (!(&qi [0] >  pi));
+  RA ( (&qi [0] >= pi));
+}
+
+void test_first_struct_member ()
+{
+  static struct S { int a, b; } s = { 0, 0 };
+
+  constexpr const int *p = 0;
+  constexpr const S   *q = &s;
+
+  SA (!(p == &q->b));
+  SA ( (p != &q->b));
+  RA ( (p <  &q->b));
+  RA ( (p <= &q->b));
+  RA (!(p >  &q->b));
+  RA (!(p >= &q->b));
+
+  SA (!(&q->b == p));
+  SA ( (&q->b != p));
+  RA (!(&q->b <  p));
+  RA (!(&q->b <= p));
+  RA ( (&q->b >  p));
+  RA ( (&q->b >= p));
+}
+
+// Expect all runtime asserts to have been eliminated as a result
+// of the tested expressions constant folded into true.
+// { dg-final { scan-assembler-not "abort" } }
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-string.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-string.C
index 0f561a4..41fa466 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-string.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-string.C
@@ -2,4 +2,4 @@ 
 
 constexpr char c1 = "hi"[1];
 constexpr char c2 = "hi"[2];
-constexpr char c3 = "hi"[3];	// { dg-error "out of bound" }
+constexpr char c3 = "hi"[3];	// { dg-error "array subscript" }
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-wstring2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-wstring2.C
index db79a9c..4055e0e 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-wstring2.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-wstring2.C
@@ -1,6 +1,6 @@ 
 // PR c++/48570
 // { dg-do compile { target c++11 } }
 
-constexpr wchar_t c1 = L"hi"[3];	// { dg-error "out of bound" }
-constexpr char16_t c2 = u"hi"[3];	// { dg-error "out of bound" }
-constexpr char32_t c3 = U"hi"[3];	// { dg-error "out of bound" }
+constexpr wchar_t c1 = L"hi"[3];	// { dg-error "array subscript" }
+constexpr char16_t c2 = u"hi"[3];	// { dg-error "array subscript" }
+constexpr char32_t c3 = U"hi"[3];	// { dg-error "array subscript" }
diff --git a/gcc/testsuite/g++.dg/cpp0x/pr65398.C b/gcc/testsuite/g++.dg/cpp0x/pr65398.C
index a4aeba5..6bd34a4 100644
--- a/gcc/testsuite/g++.dg/cpp0x/pr65398.C
+++ b/gcc/testsuite/g++.dg/cpp0x/pr65398.C
@@ -12,12 +12,12 @@  constexpr char c5 = *(&s[2] + 0);
 constexpr char c6 = *(&s[0] + 2);
 constexpr char c7 = *(&s[2] + 1);
 
-constexpr char d1 = *(&s[4] - 0); // { dg-error "array subscript out of bound" }
+constexpr char d1 = *(&s[4] - 0); // { dg-error "array subscript" }
 constexpr char d2 = *(&s[4] - 1);
 constexpr char d3 = *(&s[4] - 2);
 constexpr char d4 = *(&s[4] - 3);
 constexpr char d5 = *(&s[4] - 4);
-constexpr char d6 = *(&s[4] - 5);  // { dg-error "negative array subscript" }
+constexpr char d6 = *(&s[4] - 5);  // { dg-error "array subscript" }
 
 /* Don't accept invalid stuff.  */
 constexpr char e1 = *(&s[5] - 1); // { dg-error "is not a constant expression" }
@@ -45,12 +45,12 @@  constexpr int i5 = *(&l[2] + 0);
 constexpr int i6 = *(&l[0] + 2);
 constexpr int i7 = *(&l[2] + 1);
 
-constexpr char j1 = *(&l[4] - 0); // { dg-error "array subscript out of bound" }
+constexpr char j1 = *(&l[4] - 0); // { dg-error "array subscript" }
 constexpr char j2 = *(&l[4] - 1);
 constexpr char j3 = *(&l[4] - 2);
 constexpr char j4 = *(&l[4] - 3);
 constexpr char j5 = *(&l[4] - 4);
-constexpr char j6 = *(&l[4] - 5);  // { dg-error "negative array subscript" }
+constexpr char j6 = *(&l[4] - 5);  // { dg-error "array subscript" }
 
 /* Don't accept invalid stuff.  */
 constexpr char k1 = *(&l[5] - 1); // { dg-error "is not a constant expression" }
diff --git a/gcc/testsuite/g++.dg/ext/constexpr-vla1.C b/gcc/testsuite/g++.dg/ext/constexpr-vla1.C
index a5615bb..21eb93d 100644
--- a/gcc/testsuite/g++.dg/ext/constexpr-vla1.C
+++ b/gcc/testsuite/g++.dg/ext/constexpr-vla1.C
@@ -27,4 +27,4 @@  fn_not_ok (int n)
 }
 
 constexpr int n1 = fn_ok (3);
-constexpr int n2 = fn_not_ok (3); // { dg-error "array subscript out of bound" }
+constexpr int n2 = fn_not_ok (3); // { dg-error "array subscript" }
diff --git a/gcc/testsuite/g++.dg/ext/constexpr-vla2.C b/gcc/testsuite/g++.dg/ext/constexpr-vla2.C
index 6cb1f70..6aab184 100644
--- a/gcc/testsuite/g++.dg/ext/constexpr-vla2.C
+++ b/gcc/testsuite/g++.dg/ext/constexpr-vla2.C
@@ -18,4 +18,4 @@  fn_ok (int n)
 }
 
 constexpr int i1 = fn_ok (3);
-constexpr int i2 = fn_bad (3); // { dg-error "array subscript out of bound" }
+constexpr int i2 = fn_bad (3); // { dg-error "array subscript" }
diff --git a/gcc/testsuite/g++.dg/ext/constexpr-vla3.C b/gcc/testsuite/g++.dg/ext/constexpr-vla3.C
index ba4eb50..33fc968 100644
--- a/gcc/testsuite/g++.dg/ext/constexpr-vla3.C
+++ b/gcc/testsuite/g++.dg/ext/constexpr-vla3.C
@@ -11,4 +11,4 @@  foo (int n)
   return z;
 }
 
-constexpr int n = foo (3); // { dg-error "array subscript out of bound" }
+constexpr int n = foo (3); // { dg-error "array subscript" }
diff --git a/gcc/testsuite/g++.dg/ubsan/pr63956.C b/gcc/testsuite/g++.dg/ubsan/pr63956.C
index b265631..90360be 100644
--- a/gcc/testsuite/g++.dg/ubsan/pr63956.C
+++ b/gcc/testsuite/g++.dg/ubsan/pr63956.C
@@ -86,7 +86,7 @@  fn5 (const int *a, int b)
 
 constexpr int m1[4] = { 1, 2, 3, 4 };
 constexpr int m2 = fn5 (m1, 3);
-constexpr int m3 = fn5 (m1, 4); // { dg-error "array subscript out of bound" }
+constexpr int m3 = fn5 (m1, 4); // { dg-error "array subscript" }
 
 constexpr int
 fn6 (const int &a, int b)
@@ -116,7 +116,7 @@  fn8 (int i)
 }
 
 constexpr int o1 = fn8 (9);
-constexpr int o2 = fn8 (10); // { dg-error "array subscript out of bound" }
+constexpr int o2 = fn8 (10); // { dg-error "array subscript" }
 
 constexpr int
 fn9 (int a, int b)