public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression
@ 2016-03-14 21:25 Martin Sebor
  2016-03-14 22:13 ` Jakub Jelinek
  2016-03-16 19:53 ` Jeff Law
  0 siblings, 2 replies; 26+ messages in thread
From: Martin Sebor @ 2016-03-14 21:25 UTC (permalink / raw)
  To: Gcc Patch List, Jason Merrill; +Cc: Marek Polacek, Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 1283 bytes --]

The attached patch fixes the outstanding cases mentioned in comment
10 on bug c++/67376.  While testing the fix I uncovered a number of
other related problems without which the test would have been
incomplete.  They include:

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++/60760 - arithmetic on null pointers should not be allowed
     in constant expressions

In addition, I include a fix for the issue below that I also came
across while testing the patch and that makes root causing constexpr
problems due to out-of-bounds array subscripts easier:
PR c++/70228 - insufficient detail in diagnostics for a constexpr
     out of bounds array subscript

In a discussion of bug 70170 between those CC'd Marek posted
a prototype patch for match.pd.  While the patch seems to do
the right thing as far as the bug goes, like my own first attempt
at a fix in const-fold.c it caused a couple of regressions (in
pr21294.c and in pr44555.c).  Since I'm not yet familiar enough
with match.pd, in the interest of time I solved those regressions
in const-fold.c rather than in match.pd.

Tested on x86_64.

Martin

[-- Attachment #2: gcc-67376.patch --]
[-- Type: text/x-patch, Size: 38971 bytes --]

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++/60760 - arithmetic on null pointers should not be allowed in constant
	expressions
PR c++/70228 - insufficient detail in diagnostics for a constexpr out of bounds
	array subscript

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

	PR c++/67376
	PR c++/70170
	PR c++/70172
	PR c++/60760
	PR c++/70228
	* g++.dg/cpp0x/constexpr-array-ptr10.C: New test.
	* g++.dg/cpp0x/constexpr-array-ptr11.C: New test.
	* g++.dg/cpp0x/constexpr-array-ptr9.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-14  Martin Sebor  <msebor@redhat.com>

	PR c++/67376
	PR c++/70170
	PR c++/70172
	PR c++/60760
	PR c++/70228
	(cxx_eval_binary_expression): Add argument.
	(cxx_eval_component_reference): Same.
	(cxx_eval_constant_expression): Same.
	(cxx_eval_indirect_ref): Same.
	(cxx_eval_outermost_constant_expr): Same.
	(diag_array_subscript): New function.
	* constexpr.c (cxx_eval_call_expression): Adjust.
	(cxx_eval_conditional_expression): Same.
	(cxx_eval_array_reference): Detect null pointers.
	(cxx_eval_statement_list): Adjust.

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

	PR c++/67376
	* fold-const.c (fold_comparison): Fold equality and relational
	expressions involving null pointers.

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 5f97c9d..5ec5034 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -918,7 +918,8 @@ struct constexpr_ctx {
 static GTY (()) hash_table<constexpr_call_hasher> *constexpr_call_table;
 
 static tree cxx_eval_constant_expression (const constexpr_ctx *, tree,
-					  bool, bool *, bool *, tree * = NULL);
+					  bool, bool *, bool *, bool * = NULL,
+                                          tree * = NULL);
 
 /* Compute a hash value for a constexpr call representation.  */
 
@@ -1390,7 +1391,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 	  tree jump_target = NULL_TREE;
 	  cxx_eval_constant_expression (ctx, body,
 					lval, non_constant_p, overflow_p,
-					&jump_target);
+                                        NULL, &jump_target);
 
 	  if (DECL_CONSTRUCTOR_P (fun))
 	    /* This can be null for a subobject constructor call, in
@@ -1607,20 +1608,21 @@ cxx_eval_unary_expression (const constexpr_ctx *ctx, tree t,
 static tree
 cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t,
 			    bool /*lval*/,
-			    bool *non_constant_p, bool *overflow_p)
+			    bool *non_constant_p, bool *overflow_p,
+                            bool *nullptr_p)
 {
   tree r = NULL_TREE;
   tree orig_lhs = TREE_OPERAND (t, 0);
   tree orig_rhs = TREE_OPERAND (t, 1);
   tree lhs, rhs;
   lhs = cxx_eval_constant_expression (ctx, orig_lhs, /*lval*/false,
-				      non_constant_p, overflow_p);
+				      non_constant_p, overflow_p, nullptr_p);
   /* Don't VERIFY_CONSTANT here, it's unnecessary and will break pointer
      subtraction.  */
   if (*non_constant_p)
     return t;
   rhs = cxx_eval_constant_expression (ctx, orig_rhs, /*lval*/false,
-				      non_constant_p, overflow_p);
+				      non_constant_p, overflow_p, nullptr_p);
   if (*non_constant_p)
     return t;
 
@@ -1642,6 +1644,15 @@ cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t,
 		   || null_member_pointer_value_p (rhs)))
 	r = constant_boolean_node (!is_code_eq, type);
     }
+  if (code == POINTER_PLUS_EXPR && !*non_constant_p
+      && tree_int_cst_equal (lhs, null_pointer_node))
+    {
+      if (!ctx->quiet)
+        error ("arithmetic involving null pointer %qE", lhs);
+      if (nullptr_p)
+        *nullptr_p = true;
+      return t;
+    }
 
   if (r == NULL_TREE)
     r = fold_binary_loc (loc, code, type, lhs, rhs);
@@ -1682,11 +1693,11 @@ cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t,
     return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2),
 					 lval,
 					 non_constant_p, overflow_p,
-					 jump_target);
+					 NULL, jump_target);
   return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
 				       lval,
 				       non_constant_p, overflow_p,
-				       jump_target);
+				       NULL, jump_target);
 }
 
 /* Returns less than, equal to, or greater than zero if KEY is found to be
@@ -1815,6 +1826,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.  */
@@ -1839,11 +1874,26 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
 					false,
 					non_constant_p, overflow_p);
   VERIFY_CONSTANT (index);
+
+  tree arytype = TREE_TYPE (ary);
+  tree nelts = array_type_nelts_top (arytype);
+  /* For VLAs, the number of elements won't be an integer constant.  */
+  nelts = cxx_eval_constant_expression (ctx, nelts, false, non_constant_p,
+					overflow_p);
+  VERIFY_CONSTANT (nelts);
+
+  if (tree_int_cst_lt (nelts, index))
+    {
+      diag_array_subscript (ctx, ary, index);
+      *non_constant_p = true;
+      return t;
+    }
+
   if (lval && ary == oldary && index == oldidx)
     return t;
   else if (lval)
     return build4 (ARRAY_REF, TREE_TYPE (t), ary, index, NULL, NULL);
-  elem_type = TREE_TYPE (TREE_TYPE (ary));
+  elem_type = TREE_TYPE (arytype);
   if (TREE_CODE (ary) == CONSTRUCTOR)
     len = CONSTRUCTOR_NELTS (ary);
   else if (TREE_CODE (ary) == STRING_CST)
@@ -1863,21 +1913,14 @@ 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;
     }
 
-  tree nelts = array_type_nelts_top (TREE_TYPE (ary));
-  /* For VLAs, the number of elements won't be an integer constant.  */
-  nelts = cxx_eval_constant_expression (ctx, nelts, false, non_constant_p,
-					overflow_p);
-  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;
     }
@@ -1935,7 +1978,8 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
 static tree
 cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
 			      bool lval,
-			      bool *non_constant_p, bool *overflow_p)
+			      bool *non_constant_p, bool *overflow_p,
+                              bool *nullptr_p)
 {
   unsigned HOST_WIDE_INT i;
   tree field;
@@ -1944,7 +1988,14 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
   tree orig_whole = TREE_OPERAND (t, 0);
   tree whole = cxx_eval_constant_expression (ctx, orig_whole,
 					     lval,
-					     non_constant_p, overflow_p);
+					     non_constant_p, overflow_p,
+                                             nullptr_p);
+  if (nullptr_p && *nullptr_p)
+    {
+      if (!ctx->quiet)
+        error ("%qE dereferences a null pointer", orig_whole);
+    }
+
   if (TREE_CODE (whole) == PTRMEM_CST)
     whole = cplus_expand_constant (whole);
   if (whole == orig_whole)
@@ -2655,7 +2706,8 @@ cxx_fold_indirect_ref (location_t loc, tree type, tree op0, bool *empty_base)
 static tree
 cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
 		       bool lval,
-		       bool *non_constant_p, bool *overflow_p)
+		       bool *non_constant_p, bool *overflow_p,
+                       bool *nullptr_p)
 {
   tree orig_op0 = TREE_OPERAND (t, 0);
   bool empty_base = false;
@@ -2680,6 +2732,9 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
       tree op0 = cxx_eval_constant_expression (ctx, orig_op0,
 					       /*lval*/false, non_constant_p,
 					       overflow_p);
+      if (nullptr_p && tree_int_cst_equal (op0, null_pointer_node))
+        *nullptr_p = true;
+
       /* Don't VERIFY_CONSTANT here.  */
       if (*non_constant_p)
 	return t;
@@ -3149,7 +3204,7 @@ cxx_eval_statement_list (const constexpr_ctx *ctx, tree t,
 	}
       r = cxx_eval_constant_expression (ctx, stmt, false,
 					non_constant_p, overflow_p,
-					jump_target);
+					NULL, jump_target);
       if (*non_constant_p)
 	break;
       if (returns (jump_target) || breaks (jump_target))
@@ -3285,8 +3340,10 @@ cxx_eval_pointer_plus_expression (const constexpr_ctx *ctx, tree t,
 static tree
 cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 			      bool lval,
-			      bool *non_constant_p, bool *overflow_p,
-			      tree *jump_target)
+			      bool *non_constant_p,
+                              bool *overflow_p,
+                              bool *nullptr_p,
+                              tree *jump_target)
 {
   constexpr_ctx new_ctx;
   tree r = t;
@@ -3300,10 +3357,21 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     {
       if (TREE_OVERFLOW (t) && (!flag_permissive || ctx->quiet))
 	*overflow_p = true;
+
+      if (TREE_CODE (t) == INTEGER_CST
+          && TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE
+          && !integer_zerop (t))
+        {
+          if (!ctx->quiet)
+            error ("null pointer arithmetic in %qE", t);
+          if (nullptr_p)
+            *nullptr_p = true;
+        }
       return t;
     }
 
-  switch (TREE_CODE (t))
+  tree_code tcode = TREE_CODE (t);
+  switch (tcode)
     {
     case RESULT_DECL:
       if (lval)
@@ -3392,7 +3460,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	  {
 	    init = cxx_eval_constant_expression (ctx, init,
 						 false,
-						 non_constant_p, overflow_p);
+						 non_constant_p, overflow_p,
+                                                 nullptr_p);
 	    /* Don't share a CONSTRUCTOR that might be changed.  */
 	    init = unshare_expr (init);
 	    ctx->values->put (r, init);
@@ -3432,7 +3501,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	 initialization of a temporary.  */
       r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
 					false,
-					non_constant_p, overflow_p);
+					non_constant_p, overflow_p,
+                                        nullptr_p);
       if (!*non_constant_p)
 	/* Adjust the type of the result to the type of the temporary.  */
 	r = adjust_temp_type (TREE_TYPE (t), r);
@@ -3454,14 +3524,16 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     case SCOPE_REF:
       r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
 					lval,
-					non_constant_p, overflow_p);
+					non_constant_p, overflow_p,
+                                        nullptr_p);
       break;
 
     case RETURN_EXPR:
       if (TREE_OPERAND (t, 0) != NULL_TREE)
 	r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
 					  lval,
-					  non_constant_p, overflow_p);
+					  non_constant_p, overflow_p,
+                                          nullptr_p);
       *jump_target = t;
       break;
 
@@ -3472,7 +3544,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       else
 	{
 	  r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), false,
-					    non_constant_p, overflow_p);
+					    non_constant_p, overflow_p,
+                                            nullptr_p);
 	  ctx->values->put (t, r);
 	  if (ctx->save_exprs)
 	    ctx->save_exprs->add (t);
@@ -3489,18 +3562,18 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
 					lval,
 					non_constant_p, overflow_p,
-					jump_target);
+                                        nullptr_p, jump_target);
       break;
 
     case TRY_FINALLY_EXPR:
       r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval,
 					non_constant_p, overflow_p,
-					jump_target);
+					nullptr_p, jump_target);
       if (!*non_constant_p)
 	/* Also evaluate the cleanup.  */
 	cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), true,
 				      non_constant_p, overflow_p,
-				      jump_target);
+				      nullptr_p, jump_target);
       break;
 
       /* These differ from cxx_eval_unary_expression in that this doesn't
@@ -3509,7 +3582,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     case MEM_REF:
     case INDIRECT_REF:
       r = cxx_eval_indirect_ref (ctx, t, lval,
-				 non_constant_p, overflow_p);
+				 non_constant_p, overflow_p, nullptr_p);
       break;
 
     case ADDR_EXPR:
@@ -3517,7 +3590,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	tree oldop = TREE_OPERAND (t, 0);
 	tree op = cxx_eval_constant_expression (ctx, oldop,
 						/*lval*/true,
-						non_constant_p, overflow_p);
+						non_constant_p, overflow_p,
+                                                nullptr_p);
 	/* Don't VERIFY_CONSTANT here.  */
 	if (*non_constant_p)
 	  return t;
@@ -3560,17 +3634,17 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	    || TREE_CODE (op1) == EMPTY_CLASS_EXPR)
 	  r = cxx_eval_constant_expression (ctx, op0,
 					    lval, non_constant_p, overflow_p,
-					    jump_target);
+                                            nullptr_p, jump_target);
 	else
 	  {
 	    /* Check that the LHS is constant and then discard it.  */
 	    cxx_eval_constant_expression (ctx, op0,
 					  true, non_constant_p, overflow_p,
-					  jump_target);
+					  nullptr_p, jump_target);
 	    op1 = TREE_OPERAND (t, 1);
 	    r = cxx_eval_constant_expression (ctx, op1,
 					      lval, non_constant_p, overflow_p,
-					      jump_target);
+					      nullptr_p, jump_target);
 	  }
       }
       break;
@@ -3621,7 +3695,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     case RANGE_EXPR:
     case COMPLEX_EXPR:
       r = cxx_eval_binary_expression (ctx, t, lval,
-				      non_constant_p, overflow_p);
+				      non_constant_p, overflow_p, nullptr_p);
       break;
 
       /* fold can introduce non-IF versions of these; still treat them as
@@ -3659,7 +3733,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	  return t;
 	}
       r = cxx_eval_component_reference (ctx, t, lval,
-					non_constant_p, overflow_p);
+					non_constant_p, overflow_p, nullptr_p);
       break;
 
     case BIT_FIELD_REF:
@@ -3712,12 +3786,12 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     case NOP_EXPR:
     case UNARY_PLUS_EXPR:
       {
-	enum tree_code tcode = TREE_CODE (t);
 	tree oldop = TREE_OPERAND (t, 0);
 
 	tree op = cxx_eval_constant_expression (ctx, oldop,
 						lval,
-						non_constant_p, overflow_p);
+						non_constant_p, overflow_p,
+                                                nullptr_p);
 	if (*non_constant_p)
 	  return t;
 	tree type = TREE_TYPE (t);
@@ -3738,15 +3812,28 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 		return t;
 	      }
 	  }
-	if (POINTER_TYPE_P (type)
-	    && TREE_CODE (op) == INTEGER_CST
-	    && !integer_zerop (op))
-	  {
-	    if (!ctx->quiet)
-	      error_at (EXPR_LOC_OR_LOC (t, input_location),
-			"reinterpret_cast from integer to pointer");
-	    *non_constant_p = true;
-	    return t;
+	if (POINTER_TYPE_P (type) && TREE_CODE (op) == INTEGER_CST)
+          {
+            const char *msg = NULL;
+            if (integer_zerop (op))
+              {
+                if (nullptr_p)
+                  *nullptr_p = true;
+                if (!same_type_ignoring_top_level_qualifiers_p (type,
+                                                                TREE_TYPE (op)))
+                  msg = "invalid conversion involving a null pointer";
+              }
+            else
+              msg = "reinterpret_cast from integer to pointer";
+
+            if (msg)
+              {
+                if (!ctx->quiet)
+                  error_at (EXPR_LOC_OR_LOC (t, input_location), msg);
+
+                *non_constant_p = true;
+                return t;
+              }
 	  }
 	if (op == oldop && tcode != UNARY_PLUS_EXPR)
 	  /* We didn't fold at the top so we could check for ptr-int
@@ -3779,7 +3866,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       return cxx_eval_constant_expression (ctx, BIND_EXPR_BODY (t),
 					   lval,
 					   non_constant_p, overflow_p,
-					   jump_target);
+					   nullptr_p, jump_target);
 
     case PREINCREMENT_EXPR:
     case POSTINCREMENT_EXPR:
@@ -3828,7 +3915,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	  tree ctor = lval ? ctx->object : ctx->ctor;
 	  return cxx_eval_constant_expression
 	    (ctx, ctor, lval,
-	     non_constant_p, overflow_p);
+	     non_constant_p, overflow_p, nullptr_p);
 	}
       break;
 
@@ -3935,8 +4022,9 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
 	r = TARGET_EXPR_INITIAL (r);
     }
 
-  r = cxx_eval_constant_expression (&ctx, r,
-				    false, &non_constant_p, &overflow_p);
+  bool nullptr_p = false;
+  r = cxx_eval_constant_expression (&ctx, r, false,
+                                    &non_constant_p, &overflow_p, &nullptr_p);
 
   verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p);
 
diff --git a/gcc/fold-const.c b/gcc/fold-const.c
index 696b4a6..376aa09 100644
--- a/gcc/fold-const.c
+++ b/gcc/fold-const.c
@@ -8639,6 +8639,37 @@ 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 sybols must have a non
+	 null address.  */
+      else if (DECL_P (base0)
+               && (!HAS_DECL_ASSEMBLER_NAME_P (base0) || !DECL_WEAK (base0))
+	       /* 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))
+	       && TREE_CODE (arg1) == INTEGER_CST
+               && compare_tree_int (arg1, 0) == 0)
+	{
+	  switch (code)
+	    {
+	    case GE_EXPR:
+	    case EQ_EXPR:
+	    case LE_EXPR:
+	      return boolean_false_node;
+	    case GT_EXPR:
+	    case LT_EXPR:
+	    case NE_EXPR:
+	      return boolean_true_node;
+	    default: gcc_unreachable ();
+	    }
+	}
     }
 
   /* Transform comparisons of the form X +- C1 CMP Y +- C2 to
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..b3b68ff
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr10.C
@@ -0,0 +1,150 @@
+// 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 -Wno-extra" }
+
+namespace A {
+
+extern int i;
+
+constexpr int *p0 = &i;
+
+constexpr bool b0  = p0;        // { dg-warning "address of .A::i. will always evaluate as .true." }
+
+// The following don't cause the -Waddress warning due to bug c++/70194.
+constexpr bool b1  = p0 == 0;
+constexpr bool b2  = p0 != 0;
+constexpr bool b3  = p0 <  0;
+constexpr bool b4  = p0 <= 0;
+constexpr bool b5  = p0 >  0;
+constexpr bool b6  = p0 >= 0;
+
+constexpr bool b7  = !p0;       // { dg-warning "address of .A::i. will always evaluate as .true." }
+
+// The following don't cause the -Waddress warning due to bug c++/70194.
+constexpr bool b8  = 0 == p0;
+constexpr bool b9  = 0 != p0;
+constexpr bool b10 = 0 <  p0;
+constexpr bool b11 = 0 <= p0;
+constexpr bool b12 = 0 >  p0;
+constexpr bool b13 = 0 >= p0;
+
+}
+
+namespace B {
+
+// 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
+
+struct S { int a, b[1]; } s;
+
+constexpr S *p0 = &s;
+constexpr S *p1 = nullptr;
+
+constexpr int *q0 = p0->b;      // { dg-bogus "reinterpret_cast from integer to pointer" }
+constexpr int *r0 = p1->b;      // { dg-error "null pointer|constant expression" }
+
+constexpr bool b0  = p0;        // { dg-warning "address of .B::s. will always evaluate as .true." }
+
+// The following don't cause the -Waddress warning due to bug c++/70194.
+constexpr bool b1  = p0 == 0;
+constexpr bool b2  = p0 != 0;
+constexpr bool b3  = p0 <  0;
+constexpr bool b4  = p0 <= 0;
+constexpr bool b5  = p0 >  0;
+constexpr bool b6  = p0 >= 0;
+
+constexpr bool b7  = !p0;       // { dg-warning "address of .B::s. will always evaluate as .true." }
+
+// The following don't cause the -Waddress warning due to bug c++/70194.
+constexpr bool b8  = 0 == p0;
+constexpr bool b9  = 0 != p0;
+constexpr bool b10 = 0 <  p0;
+constexpr bool b11 = 0 <= p0;
+constexpr bool b12 = 0 >  p0;
+constexpr bool b13 = 0 >= p0;
+
+}
+
+namespace C {
+
+struct A { int i; const A *pa1; const A *pa0; };
+
+constexpr A a1 = { 0, 0, 0  };
+constexpr A a2 = { 1, &a1, 0 };
+
+constexpr const A *pa2 = &a2;
+constexpr int i0 = pa2->i;
+constexpr int i1 = pa2->pa1->i;
+constexpr int i2 = pa2->pa1->pa0->i;           // { dg-error "null pointer|not a constant" }
+
+constexpr const A *pa3 = &*pa2->pa1->pa0;
+constexpr const A *pa4 = pa2->pa1->pa0 + 1;    // { dg-error "null pointer|not a constant" }
+
+constexpr const int *pi0 = &pa2->pa1->pa0->i;  // { dg-error "null pointer|not a constant" }
+
+constexpr const A *pa5 = 0;
+constexpr const int *pi1 = &pa5->i;            // { dg-error "null pointer|not a constant" }
+
+}
+
+namespace WeakRefTest1 {
+
+extern __attribute__ ((weak)) int i;
+
+constexpr int *p0 = &i;
+
+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;
+
+}
+
+namespace WeakRefTest2 {
+
+extern __attribute__ ((weak)) int i;
+
+constexpr int *p1 = &i + 1;
+
+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;
+
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr11.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr11.C
new file mode 100644
index 0000000..3b28f20
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr11.C
@@ -0,0 +1,128 @@
+// PR c++/60760 - arithmetic on null pointers should not be allowed
+//     in constant expressions
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "-Wno-pointer-arith" }
+
+// Generate a null poiinter.
+constexpr int* null () { return 0; }
+
+namespace SimpleTests {
+
+constexpr int* p0 = nullptr;
+constexpr int* q0 = p0;
+constexpr int* r0 = null ();
+
+// Adding or subtracting zero from a null pointer is valid in C++.
+constexpr int* p1 = p0 + 0;
+constexpr int* p2 = p0 - 0;
+constexpr int* p3 = 0 + p0;
+
+// While the text of the C++ standard still doesn't allow it, CWG
+// issue 232 implies that dererencing a null pointer is intended
+// to be permitted in contexts where the result isn't evaluated.
+// For compatibility with C that should at a minimum include
+// expressions like &*p that are valid there.
+constexpr int* p4 = &*p0;
+constexpr int* p5 = p0 + 1;       // { dg-error "null pointer|not a constant" }
+constexpr int* p6 = 1 + p0;       // { dg-error "null pointer|not a constant" }
+constexpr int* p7 = p0 - 1;       // { dg-error "null pointer|not a constant" }
+constexpr int* p8 = &p0 [0];
+constexpr int* p9 = &0 [p0];
+
+constexpr int* p10 = null () + 2; // { dg-error "null pointer|not a constant" }
+constexpr int* p11 = 3 + null (); // { dg-error "null pointer|not a constant" }
+constexpr int* p12 = null () - 4; // { dg-error "null pointer|not a constant" }
+constexpr int* p13 = &null ()[4]; // { dg-error "null pointer|not a constant" }
+constexpr int* p14 = &3[null ()]; // { dg-error "null pointer|not a constant" }
+
+constexpr int* q1 = q0 + 0;
+constexpr int* q2 = q0 - 0;
+constexpr int* q3 = q0 + 1;       // { dg-error "null pointer|not a constant" }
+constexpr int* q4 = q0 + 2;       // { dg-error "null pointer|not a constant" }
+constexpr int* q5 = &q0 [0];
+
+// Subtracting null pointers from one another is valid.
+constexpr int i0 = p0 - (int*)0;
+constexpr int i1 = p0 - static_cast<int*>(0);
+constexpr int i2 = p0 - (int*)nullptr;
+constexpr int i3 = p0 - static_cast<int*>(nullptr);
+constexpr int i4 = p0 - p0;
+constexpr int i5 = p0 - q0;
+constexpr int i6 = p0 - r0;
+constexpr int i7 = (int*)0 - p0;
+constexpr int i8 = static_cast<int*>(0) - p0;
+constexpr int i9 = (int*)nullptr - p0;
+constexpr int i10 = static_cast<int*>(nullptr) - p0;
+constexpr int i11 = q0 - p0;
+constexpr int i12 = r0 - p0;
+
+}
+
+namespace IndirectTests {
+
+struct S { int i, j; struct SA { struct SB { int *pi; } sb; } sa; };
+
+constexpr S* ps = (S*)0;
+
+// Comparing null pointers is valid.
+constexpr bool b0 = ps == ps;
+constexpr bool b1 = ps != ps;
+constexpr bool b2 = ps <  ps;
+constexpr bool b3 = ps <= ps;
+constexpr bool b4 = ps >  ps;
+constexpr bool b5 = ps >= ps;
+
+constexpr bool b6 = ps == (S*)0;
+constexpr bool b7 = ps != (S*)0;
+constexpr bool b8 = ps <  (S*)0;
+constexpr bool b9 = ps <= (S*)0;
+constexpr bool b10 = ps >  (S*)0;
+constexpr bool b11 = ps >= (S*)0;
+
+constexpr S* ps1 = ps;
+constexpr S* ps2 = ps1;
+
+// The following aren't diagnosed due to a bug.
+// constexpr int* pi0 = &((S*)0)->i;
+// constexpr int* pi1 = &((S*)nullptr)->i;
+
+constexpr int* pj0 = &((S*)0)->j;        // { dg-error "null pointer|not a constant" }
+constexpr int* pj1 = &((S*)nullptr)->j;  // { dg-error "null pointer|not a constant" }
+
+constexpr int* psi = &ps->i;            // { dg-error "null pointer|not a constant" }
+constexpr int* psj = &ps->j;            // { dg-error "null pointer|not a constant" }
+
+constexpr int* ps1i = &ps1->i;          // { dg-error "null pointer|not a constant" }
+constexpr int* ps2i = &ps1->i;          // { dg-error "null pointer|not a constant" }
+
+constexpr int* ps1j = &ps1->j;          // { dg-error "null pointer|not a constant" }
+constexpr int* ps2j = &ps1->j;          // { dg-error "null pointer|not a constant" }
+
+}
+
+namespace FunctionTests {
+
+typedef void Func ();
+
+// Arithmetic on member function pointers is diagnosed with -Wpointer-arith.
+// With constexpr, only zero may be added or subtracted.
+constexpr Func *pf0 = 0;
+constexpr Func *pf1 = pf0 + 0;  // triggers -Wpointer-arith
+constexpr Func *pf2 = pf0 - 0;  // triggers -Wpointer-arith
+constexpr Func *pf3 = 0 + pf0;  // triggers -Wpointer-arith
+constexpr Func *pf4 = pf0 + 1;  // { dg-error "null pointer|not a constant" }
+constexpr Func *pf5 = 2 + pf0;  // { dg-error "null pointer|not a constant" }
+constexpr Func *pf6 = pf0 - 3;  // { dg-error "null pointer|not a constant" }
+
+struct S;
+typedef void (S::*MemFuncPtr)();
+
+// Arithmetic on member function pointers is rejected with a hard error.
+constexpr MemFuncPtr pmf0 = nullptr;
+constexpr MemFuncPtr pmf1 = pmf0 + 0;   // { dg-error "invalid operands" }
+constexpr MemFuncPtr pmf2 = 0 + pmf0;   // { dg-error "invalid operands" }
+constexpr MemFuncPtr pmf3 = pmf0 + 1;   // { dg-error "invalid operands" }
+constexpr MemFuncPtr pmf4 = 1 + pmf0;   // { dg-error "invalid operands" }
+constexpr MemFuncPtr pmf5 = pmf0 - 1;   // { dg-error "invalid operands" }
+
+}
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..c690131
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr9.C
@@ -0,0 +1,56 @@
+// PR c++/67376
+// { 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-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..bab875c 100644
--- a/gcc/testsuite/g++.dg/cpp0x/pr65398.C
+++ b/gcc/testsuite/g++.dg/cpp0x/pr65398.C
@@ -12,17 +12,17 @@ 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" }
-constexpr char e2 = *(&s[5] - 2); // { dg-error "is not a constant expression" }
-constexpr char e3 = *(&s[5] - 3); // { dg-error "is not a constant expression" }
+constexpr char e1 = *(&s[5] - 1); // { dg-error "array subscript" }
+constexpr char e2 = *(&s[5] - 2); // { dg-error "array subscript" }
+constexpr char e3 = *(&s[5] - 3); // { dg-error "array subscript" }
 
 SA (c1 == 'a');
 SA (c2 == 'b');
@@ -45,17 +45,17 @@ 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" }
-constexpr char k2 = *(&l[5] - 2); // { dg-error "is not a constant expression" }
-constexpr char k3 = *(&l[5] - 3); // { dg-error "is not a constant expression" }
+constexpr char k1 = *(&l[5] - 1); // { dg-error "array subscript" }
+constexpr char k2 = *(&l[5] - 2); // { dg-error "array subscript" }
+constexpr char k3 = *(&l[5] - 3); // { dg-error "array subscript" }
 
 SA (i1 == 'c');
 SA (i2 == 'd');
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..98991fd 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)
@@ -106,7 +106,7 @@ fn7 (const int *a, int b)
 
 constexpr int n1 = 7;
 constexpr int n2 = fn7 (&n1, 5);
-constexpr int n3 = fn7 ((const int *) 0, 8); // { dg-error "is not a constant expression" }
+constexpr int n3 = fn7 ((const int *) 0, 8); // { dg-error "null pointer" }
 
 constexpr int
 fn8 (int i)
@@ -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)

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

* Re: [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression
  2016-03-14 21:25 [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression Martin Sebor
@ 2016-03-14 22:13 ` Jakub Jelinek
  2016-03-16 19:38   ` Jeff Law
  2016-03-16 19:53 ` Jeff Law
  1 sibling, 1 reply; 26+ messages in thread
From: Jakub Jelinek @ 2016-03-14 22:13 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Gcc Patch List, Jason Merrill, Marek Polacek

On Mon, Mar 14, 2016 at 03:25:07PM -0600, Martin Sebor wrote:
> 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++/60760 - arithmetic on null pointers should not be allowed in constant
> 	expressions
> PR c++/70228 - insufficient detail in diagnostics for a constexpr out of bounds
> 	array subscript

Can you please check up the formatting in the patch?
Seems e.g. you've replaced tons of tabs with 8 spaces etc. (check your
editor setting, and check the patch with contrib/check-GNU-style.sh).
There is some trailing whitespace too, spaces before [, etc.

	Jakub

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

* Re: [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression
  2016-03-14 22:13 ` Jakub Jelinek
@ 2016-03-16 19:38   ` Jeff Law
  2016-03-16 22:53     ` Jakub Jelinek
  0 siblings, 1 reply; 26+ messages in thread
From: Jeff Law @ 2016-03-16 19:38 UTC (permalink / raw)
  To: Jakub Jelinek, Martin Sebor; +Cc: Gcc Patch List, Jason Merrill, Marek Polacek

On 03/14/2016 04:13 PM, Jakub Jelinek wrote:
> On Mon, Mar 14, 2016 at 03:25:07PM -0600, Martin Sebor wrote:
>> 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++/60760 - arithmetic on null pointers should not be allowed in constant
>> 	expressions
>> PR c++/70228 - insufficient detail in diagnostics for a constexpr out of bounds
>> 	array subscript
>
> Can you please check up the formatting in the patch?
> Seems e.g. you've replaced tons of tabs with 8 spaces etc. (check your
> editor setting, and check the patch with contrib/check-GNU-style.sh).
> There is some trailing whitespace too, spaces before [, etc.
Jakub, do you have any comments on the substance of the patch?  If so, 
it would help immensely if you could provide them so that Martin could 
address technical issues at the same time as he fixes up whitespace nits.

jeff

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

* Re: [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression
  2016-03-14 21:25 [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression Martin Sebor
  2016-03-14 22:13 ` Jakub Jelinek
@ 2016-03-16 19:53 ` Jeff Law
  2016-03-17 21:35   ` Martin Sebor
  1 sibling, 1 reply; 26+ messages in thread
From: Jeff Law @ 2016-03-16 19:53 UTC (permalink / raw)
  To: Martin Sebor, Gcc Patch List, Jason Merrill; +Cc: Marek Polacek, Jakub Jelinek

On 03/14/2016 03:25 PM, Martin Sebor wrote:
> The attached patch fixes the outstanding cases mentioned in comment
> 10 on bug c++/67376.  While testing the fix I uncovered a number of
> other related problems without which the test would have been
> incomplete.  They include:
>
> 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++/60760 - arithmetic on null pointers should not be allowed
>      in constant expressions
>
> In addition, I include a fix for the issue below that I also came
> across while testing the patch and that makes root causing constexpr
> problems due to out-of-bounds array subscripts easier:
> PR c++/70228 - insufficient detail in diagnostics for a constexpr
>      out of bounds array subscript
>
> In a discussion of bug 70170 between those CC'd Marek posted
> a prototype patch for match.pd.  While the patch seems to do
> the right thing as far as the bug goes, like my own first attempt
> at a fix in const-fold.c it caused a couple of regressions (in
> pr21294.c and in pr44555.c).  Since I'm not yet familiar enough
> with match.pd, in the interest of time I solved those regressions
> in const-fold.c rather than in match.pd.
>
> Tested on x86_64.
>
> Martin
>
> gcc-67376.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++/60760 - arithmetic on null pointers should not be allowed in constant
> 	expressions
> PR c++/70228 - insufficient detail in diagnostics for a constexpr out of bounds
> 	array subscript
>
> gcc/testsuite/ChangeLog:
> 2016-03-14  Martin Sebor<msebor@redhat.com>
>
> 	PR c++/67376
> 	PR c++/70170
> 	PR c++/70172
> 	PR c++/60760
> 	PR c++/70228
> 	* g++.dg/cpp0x/constexpr-array-ptr10.C: New test.
> 	* g++.dg/cpp0x/constexpr-array-ptr11.C: New test.
> 	* g++.dg/cpp0x/constexpr-array-ptr9.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-14  Martin Sebor<msebor@redhat.com>
>
> 	PR c++/67376
> 	PR c++/70170
> 	PR c++/70172
> 	PR c++/60760
> 	PR c++/70228
> 	(cxx_eval_binary_expression): Add argument.
> 	(cxx_eval_component_reference): Same.
> 	(cxx_eval_constant_expression): Same.
> 	(cxx_eval_indirect_ref): Same.
> 	(cxx_eval_outermost_constant_expr): Same.
> 	(diag_array_subscript): New function.
> 	* constexpr.c (cxx_eval_call_expression): Adjust.
> 	(cxx_eval_conditional_expression): Same.
> 	(cxx_eval_array_reference): Detect null pointers.
> 	(cxx_eval_statement_list): Adjust.
>
> gcc/ChangeLog:
> 2016-03-14  Martin Sebor<msebor@redhat.com>
>
> 	PR c++/67376
> 	* fold-const.c (fold_comparison): Fold equality and relational
> 	expressions involving null pointers.
>
> diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
> index 5f97c9d..5ec5034 100644
> --- a/gcc/cp/constexpr.c
> +++ b/gcc/cp/constexpr.c
> @@ -918,7 +918,8 @@ struct constexpr_ctx {
>   static GTY (()) hash_table<constexpr_call_hasher> *constexpr_call_table;
>
>   static tree cxx_eval_constant_expression (const constexpr_ctx *, tree,
> -					  bool, bool *, bool *, tree * = NULL);
> +					  bool, bool *, bool *, bool * = NULL,
> +                                          tree * = NULL);
I didn't look deeply, but do you end up fixing all (most) of the callers 
of cxx_eval_constant_expression?  If so, then you don't need the default 
initialization.

At a high level, I'm curious your thoughts on emitting these errors out 
of the front-end rather than after analysis.  I'm thinking in particular 
about constant propagation,  unreachable code elimination and DCE.  The 
former can expose cases that are difficult to catch in the front-end, 
while the latter two might remove an out-of-range comparison/reference.


  diff --git a/gcc/fold-const.c b/gcc/fold-const.c
> index 696b4a6..376aa09 100644
> --- a/gcc/fold-const.c
> +++ b/gcc/fold-const.c
> @@ -8639,6 +8639,37 @@ 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 sybols must have a non
> +	 null address.  */
Hmm, I thought we already had code to do this somewhere.   It looks like 
it's moved around quite a bit.  I think you want to be using 
symtab_node::nonzero_address to determine if a given symbol must bind to 
a nonzero address.



Jeff

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

* Re: [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression
  2016-03-16 19:38   ` Jeff Law
@ 2016-03-16 22:53     ` Jakub Jelinek
  0 siblings, 0 replies; 26+ messages in thread
From: Jakub Jelinek @ 2016-03-16 22:53 UTC (permalink / raw)
  To: Jeff Law; +Cc: Martin Sebor, Gcc Patch List, Jason Merrill, Marek Polacek

On Wed, Mar 16, 2016 at 01:38:21PM -0600, Jeff Law wrote:
> On 03/14/2016 04:13 PM, Jakub Jelinek wrote:
> >On Mon, Mar 14, 2016 at 03:25:07PM -0600, Martin Sebor wrote:
> >>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++/60760 - arithmetic on null pointers should not be allowed in constant
> >>	expressions
> >>PR c++/70228 - insufficient detail in diagnostics for a constexpr out of bounds
> >>	array subscript
> >
> >Can you please check up the formatting in the patch?
> >Seems e.g. you've replaced tons of tabs with 8 spaces etc. (check your
> >editor setting, and check the patch with contrib/check-GNU-style.sh).
> >There is some trailing whitespace too, spaces before [, etc.
> Jakub, do you have any comments on the substance of the patch?  If so, it
> would help immensely if you could provide them so that Martin could address
> technical issues at the same time as he fixes up whitespace nits.

No, I'll defer technical comments to Jason.  The formatting is just
something that caught my eye during the 10 seconds or so spent on reading the
patch flying by.

	Jakub

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

* Re: [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression
  2016-03-16 19:53 ` Jeff Law
@ 2016-03-17 21:35   ` Martin Sebor
  2016-03-18 17:48     ` Jeff Law
  2016-03-18 19:13     ` Jeff Law
  0 siblings, 2 replies; 26+ messages in thread
From: Martin Sebor @ 2016-03-17 21:35 UTC (permalink / raw)
  To: Jeff Law, Gcc Patch List, Jason Merrill; +Cc: Marek Polacek, Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 3978 bytes --]

>>   static tree cxx_eval_constant_expression (const constexpr_ctx *, tree,
>> -                      bool, bool *, bool *, tree * = NULL);
>> +                      bool, bool *, bool *, bool * = NULL,
>> +                                          tree * = NULL);
> I didn't look deeply, but do you end up fixing all (most) of the callers
> of cxx_eval_constant_expression?  If so, then you don't need the default
> initialization.

Thanks for the comments.  The patch only modifies about 10 out
of the 70 or so calls to the function in the file and the default
argument helps avoid making the rest of the changes.  (I have some
ideas for improving the APIs of these functions that I'd like to
run by Jason when we're done with the 6.0 work.)

> At a high level, I'm curious your thoughts on emitting these errors out
> of the front-end rather than after analysis.  I'm thinking in particular
> about constant propagation,  unreachable code elimination and DCE.  The
> former can expose cases that are difficult to catch in the front-end,
> while the latter two might remove an out-of-range comparison/reference.

The difficulty I've run into with detecting these problems in later
phases is that some invalid expressions have already been simplified
by the front end.  The example that applies here (even though this
is still the front end) is this:

   constexpr int* p = 0;
   constexpr bool b0 = &p[0] == 0;   // accepted
   constexpr bool b1 = &p[1] == 0;   // rejected

Both b0 and b1 are invalid and should be diagnosed, but only b1
is.  b1 isn't because because by the time we see its initializer
in constexpr.c it's been transformed into the equivalent of "b1
= (int*)ps" (though we don't see the cast which would also make
it invalid).

But if we can avoid these early simplifying transformations and
retain a more faithful representation of the original source then
doing the checking later will likely be simpler and result in
detecting more problems with greater consistency and less effort.

>   diff --git a/gcc/fold-const.c b/gcc/fold-const.c
>> index 696b4a6..376aa09 100644
>> --- a/gcc/fold-const.c
>> +++ b/gcc/fold-const.c
>> @@ -8639,6 +8639,37 @@ 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 sybols must have a non
>> +     null address.  */
> Hmm, I thought we already had code to do this somewhere.   It looks like
> it's moved around quite a bit.  I think you want to be using
> symtab_node::nonzero_address to determine if a given symbol must bind to
> a nonzero address.

Thanks for the hint.  I had looked for existing functions but
couldn't find one that worked.  decl_with_nonnull_addr_p() in
c-common.c looked promising but it's not accessible here and
it doesn't do the right thing when HAS_DECL_ASSEMBLER_NAME_P()
is false (it ICEs).

There are a few functions in the fold-const.c itself that make
use of symtab_node::nonzero_address(), either directly or
indirectly (tree_expr_nonzero_p and tree_expr_nonzero_warnv_p)
but none is usable in this context (they don't handle VAR_DECL,
and adding that handling appears more involved than the current
approach).

In the end, I added a new function, maybe_nonzero_address(),
that calls symtab_node::nonzero_address(), and that I factored
out of tree_single_nonzero_warnv_p() that I was then able to
use in fold_comparison().

I've made a few other small adjustments to the patch to avoid
one false positive, and a few test cases, and tweak the expected
diagnostics now that Marek has fixed 70194.

I've also wrote myself a small sed script to replace blocks of
8 spaces with tabs and ran the patch through it.  I'll integrate
it into my workflow so I hopefully don't have to worry about this
ever again.

Martin


[-- Attachment #2: gcc-67376.patch --]
[-- Type: text/x-patch, Size: 42279 bytes --]

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++/60760 - arithmetic on null pointers should not be allowed in constant
	expressions
PR c++/70228 - insufficient detail in diagnostics for a constexpr out of bounds
	array subscript

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

	PR c++/67376
	PR c++/70170
	PR c++/70172
	PR c++/60760
	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-array5.C: Adjust text of expected diagnostic.
	* g++.dg/cpp0x/constexpr-nullptr.C: Add test cases.
	* 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-17  Martin Sebor  <msebor@redhat.com>

	PR c++/67376
	PR c++/70170
	PR c++/70172
	PR c++/60760
	PR c++/70228
	* constexpr.c (cxx_eval_binary_expression): Add argument.
	(cxx_eval_component_reference): Same.
	(cxx_eval_constant_expression): Same.
	(cxx_eval_indirect_ref): Same.
	(cxx_eval_outermost_constant_expr): Same.
	(diag_array_subscript): New function.
	(cxx_eval_call_expression): Adjust.
	(cxx_eval_conditional_expression): Same.
	(cxx_eval_array_reference): Detect null pointers.
	(cxx_eval_statement_list): Adjust.

gcc/ChangeLog:
2016-03-17  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.

Index: gcc/cp/constexpr.c
===================================================================
--- gcc/cp/constexpr.c	(revision 234306)
+++ gcc/cp/constexpr.c	(working copy)
@@ -918,7 +918,8 @@ struct constexpr_ctx {
 static GTY (()) hash_table<constexpr_call_hasher> *constexpr_call_table;
 
 static tree cxx_eval_constant_expression (const constexpr_ctx *, tree,
-					  bool, bool *, bool *, tree * = NULL);
+					  bool, bool *, bool *, bool * = NULL,
+			                  tree * = NULL);
 
 /* Compute a hash value for a constexpr call representation.  */
 
@@ -1390,7 +1391,7 @@ cxx_eval_call_expression (const constexp
 	  tree jump_target = NULL_TREE;
 	  cxx_eval_constant_expression (ctx, body,
 					lval, non_constant_p, overflow_p,
-					&jump_target);
+			                NULL, &jump_target);
 
 	  if (DECL_CONSTRUCTOR_P (fun))
 	    /* This can be null for a subobject constructor call, in
@@ -1607,20 +1608,21 @@ cxx_eval_unary_expression (const constex
 static tree
 cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t,
 			    bool /*lval*/,
-			    bool *non_constant_p, bool *overflow_p)
+			    bool *non_constant_p, bool *overflow_p,
+			    bool *nullptr_p)
 {
   tree r = NULL_TREE;
   tree orig_lhs = TREE_OPERAND (t, 0);
   tree orig_rhs = TREE_OPERAND (t, 1);
   tree lhs, rhs;
   lhs = cxx_eval_constant_expression (ctx, orig_lhs, /*lval*/false,
-				      non_constant_p, overflow_p);
+				      non_constant_p, overflow_p, nullptr_p);
   /* Don't VERIFY_CONSTANT here, it's unnecessary and will break pointer
      subtraction.  */
   if (*non_constant_p)
     return t;
   rhs = cxx_eval_constant_expression (ctx, orig_rhs, /*lval*/false,
-				      non_constant_p, overflow_p);
+				      non_constant_p, overflow_p, nullptr_p);
   if (*non_constant_p)
     return t;
 
@@ -1642,6 +1644,15 @@ cxx_eval_binary_expression (const conste
 		   || null_member_pointer_value_p (rhs)))
 	r = constant_boolean_node (!is_code_eq, type);
     }
+  if (code == POINTER_PLUS_EXPR && !*non_constant_p
+      && tree_int_cst_equal (lhs, null_pointer_node))
+    {
+      if (!ctx->quiet)
+	error ("arithmetic involving null pointer %qE", lhs);
+      if (nullptr_p)
+	*nullptr_p = true;
+      return t;
+    }
 
   if (r == NULL_TREE)
     r = fold_binary_loc (loc, code, type, lhs, rhs);
@@ -1682,11 +1693,11 @@ cxx_eval_conditional_expression (const c
     return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2),
 					 lval,
 					 non_constant_p, overflow_p,
-					 jump_target);
+					 NULL, jump_target);
   return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
 				       lval,
 				       non_constant_p, overflow_p,
-				       jump_target);
+				       NULL, jump_target);
 }
 
 /* Returns less than, equal to, or greater than zero if KEY is found to be
@@ -1815,6 +1826,30 @@ find_array_ctor_elt (tree ary, tree dind
   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.  */
@@ -1839,11 +1874,26 @@ cxx_eval_array_reference (const constexp
 					false,
 					non_constant_p, overflow_p);
   VERIFY_CONSTANT (index);
+
+  tree arytype = TREE_TYPE (ary);
+  tree nelts = array_type_nelts_top (arytype);
+  /* For VLAs, the number of elements won't be an integer constant.  */
+  nelts = cxx_eval_constant_expression (ctx, nelts, false, non_constant_p,
+					overflow_p);
+  VERIFY_CONSTANT (nelts);
+
+  if (tree_int_cst_lt (nelts, index))
+    {
+      diag_array_subscript (ctx, ary, index);
+      *non_constant_p = true;
+      return t;
+    }
+
   if (lval && ary == oldary && index == oldidx)
     return t;
   else if (lval)
     return build4 (ARRAY_REF, TREE_TYPE (t), ary, index, NULL, NULL);
-  elem_type = TREE_TYPE (TREE_TYPE (ary));
+  elem_type = TREE_TYPE (arytype);
   if (TREE_CODE (ary) == CONSTRUCTOR)
     len = CONSTRUCTOR_NELTS (ary);
   else if (TREE_CODE (ary) == STRING_CST)
@@ -1863,21 +1913,14 @@ cxx_eval_array_reference (const constexp
   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;
     }
 
-  tree nelts = array_type_nelts_top (TREE_TYPE (ary));
-  /* For VLAs, the number of elements won't be an integer constant.  */
-  nelts = cxx_eval_constant_expression (ctx, nelts, false, non_constant_p,
-					overflow_p);
-  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;
     }
@@ -1935,7 +1978,8 @@ cxx_eval_array_reference (const constexp
 static tree
 cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
 			      bool lval,
-			      bool *non_constant_p, bool *overflow_p)
+			      bool *non_constant_p, bool *overflow_p,
+			      bool *nullptr_p)
 {
   unsigned HOST_WIDE_INT i;
   tree field;
@@ -1944,7 +1988,14 @@ cxx_eval_component_reference (const cons
   tree orig_whole = TREE_OPERAND (t, 0);
   tree whole = cxx_eval_constant_expression (ctx, orig_whole,
 					     lval,
-					     non_constant_p, overflow_p);
+					     non_constant_p, overflow_p,
+			                     nullptr_p);
+  if (nullptr_p && *nullptr_p)
+    {
+      if (!ctx->quiet)
+	error ("%qE dereferences a null pointer", orig_whole);
+    }
+
   if (TREE_CODE (whole) == PTRMEM_CST)
     whole = cplus_expand_constant (whole);
   if (whole == orig_whole)
@@ -2655,7 +2706,8 @@ cxx_fold_indirect_ref (location_t loc, t
 static tree
 cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
 		       bool lval,
-		       bool *non_constant_p, bool *overflow_p)
+		       bool *non_constant_p, bool *overflow_p,
+		       bool *nullptr_p)
 {
   tree orig_op0 = TREE_OPERAND (t, 0);
   bool empty_base = false;
@@ -2680,6 +2732,9 @@ cxx_eval_indirect_ref (const constexpr_c
       tree op0 = cxx_eval_constant_expression (ctx, orig_op0,
 					       /*lval*/false, non_constant_p,
 					       overflow_p);
+      if (nullptr_p && tree_int_cst_equal (op0, null_pointer_node))
+	*nullptr_p = true;
+
       /* Don't VERIFY_CONSTANT here.  */
       if (*non_constant_p)
 	return t;
@@ -3149,7 +3204,7 @@ cxx_eval_statement_list (const constexpr
 	}
       r = cxx_eval_constant_expression (ctx, stmt, false,
 					non_constant_p, overflow_p,
-					jump_target);
+					NULL, jump_target);
       if (*non_constant_p)
 	break;
       if (returns (jump_target) || breaks (jump_target))
@@ -3285,8 +3340,10 @@ cxx_eval_pointer_plus_expression (const
 static tree
 cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 			      bool lval,
-			      bool *non_constant_p, bool *overflow_p,
-			      tree *jump_target)
+			      bool *non_constant_p,
+			      bool *overflow_p,
+			      bool *nullptr_p,
+			      tree *jump_target)
 {
   constexpr_ctx new_ctx;
   tree r = t;
@@ -3300,10 +3357,21 @@ cxx_eval_constant_expression (const cons
     {
       if (TREE_OVERFLOW (t) && (!flag_permissive || ctx->quiet))
 	*overflow_p = true;
+
+      if (TREE_CODE (t) == INTEGER_CST
+	  && TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE
+	  && !integer_zerop (t))
+	{
+	  if (!ctx->quiet)
+	    error ("null pointer arithmetic in %qE", t);
+	  if (nullptr_p)
+	    *nullptr_p = true;
+	}
       return t;
     }
 
-  switch (TREE_CODE (t))
+  tree_code tcode = TREE_CODE (t);
+  switch (tcode)
     {
     case RESULT_DECL:
       if (lval)
@@ -3392,7 +3460,8 @@ cxx_eval_constant_expression (const cons
 	  {
 	    init = cxx_eval_constant_expression (ctx, init,
 						 false,
-						 non_constant_p, overflow_p);
+						 non_constant_p, overflow_p,
+			                         nullptr_p);
 	    /* Don't share a CONSTRUCTOR that might be changed.  */
 	    init = unshare_expr (init);
 	    ctx->values->put (r, init);
@@ -3432,7 +3501,8 @@ cxx_eval_constant_expression (const cons
 	 initialization of a temporary.  */
       r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
 					false,
-					non_constant_p, overflow_p);
+					non_constant_p, overflow_p,
+			                nullptr_p);
       if (!*non_constant_p)
 	/* Adjust the type of the result to the type of the temporary.  */
 	r = adjust_temp_type (TREE_TYPE (t), r);
@@ -3454,14 +3524,16 @@ cxx_eval_constant_expression (const cons
     case SCOPE_REF:
       r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
 					lval,
-					non_constant_p, overflow_p);
+					non_constant_p, overflow_p,
+			                nullptr_p);
       break;
 
     case RETURN_EXPR:
       if (TREE_OPERAND (t, 0) != NULL_TREE)
 	r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
 					  lval,
-					  non_constant_p, overflow_p);
+					  non_constant_p, overflow_p,
+			                  nullptr_p);
       *jump_target = t;
       break;
 
@@ -3472,7 +3544,8 @@ cxx_eval_constant_expression (const cons
       else
 	{
 	  r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), false,
-					    non_constant_p, overflow_p);
+					    non_constant_p, overflow_p,
+			                    nullptr_p);
 	  ctx->values->put (t, r);
 	  if (ctx->save_exprs)
 	    ctx->save_exprs->add (t);
@@ -3489,18 +3562,18 @@ cxx_eval_constant_expression (const cons
       r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
 					lval,
 					non_constant_p, overflow_p,
-					jump_target);
+			                nullptr_p, jump_target);
       break;
 
     case TRY_FINALLY_EXPR:
       r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval,
 					non_constant_p, overflow_p,
-					jump_target);
+					nullptr_p, jump_target);
       if (!*non_constant_p)
 	/* Also evaluate the cleanup.  */
 	cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), true,
 				      non_constant_p, overflow_p,
-				      jump_target);
+				      nullptr_p, jump_target);
       break;
 
       /* These differ from cxx_eval_unary_expression in that this doesn't
@@ -3509,7 +3582,7 @@ cxx_eval_constant_expression (const cons
     case MEM_REF:
     case INDIRECT_REF:
       r = cxx_eval_indirect_ref (ctx, t, lval,
-				 non_constant_p, overflow_p);
+				 non_constant_p, overflow_p, nullptr_p);
       break;
 
     case ADDR_EXPR:
@@ -3517,7 +3590,8 @@ cxx_eval_constant_expression (const cons
 	tree oldop = TREE_OPERAND (t, 0);
 	tree op = cxx_eval_constant_expression (ctx, oldop,
 						/*lval*/true,
-						non_constant_p, overflow_p);
+						non_constant_p, overflow_p,
+			                        nullptr_p);
 	/* Don't VERIFY_CONSTANT here.  */
 	if (*non_constant_p)
 	  return t;
@@ -3560,17 +3634,17 @@ cxx_eval_constant_expression (const cons
 	    || TREE_CODE (op1) == EMPTY_CLASS_EXPR)
 	  r = cxx_eval_constant_expression (ctx, op0,
 					    lval, non_constant_p, overflow_p,
-					    jump_target);
+			                    nullptr_p, jump_target);
 	else
 	  {
 	    /* Check that the LHS is constant and then discard it.  */
 	    cxx_eval_constant_expression (ctx, op0,
 					  true, non_constant_p, overflow_p,
-					  jump_target);
+					  nullptr_p, jump_target);
 	    op1 = TREE_OPERAND (t, 1);
 	    r = cxx_eval_constant_expression (ctx, op1,
 					      lval, non_constant_p, overflow_p,
-					      jump_target);
+					      nullptr_p, jump_target);
 	  }
       }
       break;
@@ -3621,7 +3695,7 @@ cxx_eval_constant_expression (const cons
     case RANGE_EXPR:
     case COMPLEX_EXPR:
       r = cxx_eval_binary_expression (ctx, t, lval,
-				      non_constant_p, overflow_p);
+				      non_constant_p, overflow_p, nullptr_p);
       break;
 
       /* fold can introduce non-IF versions of these; still treat them as
@@ -3659,7 +3733,7 @@ cxx_eval_constant_expression (const cons
 	  return t;
 	}
       r = cxx_eval_component_reference (ctx, t, lval,
-					non_constant_p, overflow_p);
+					non_constant_p, overflow_p, nullptr_p);
       break;
 
     case BIT_FIELD_REF:
@@ -3712,12 +3786,12 @@ cxx_eval_constant_expression (const cons
     case NOP_EXPR:
     case UNARY_PLUS_EXPR:
       {
-	enum tree_code tcode = TREE_CODE (t);
 	tree oldop = TREE_OPERAND (t, 0);
 
 	tree op = cxx_eval_constant_expression (ctx, oldop,
 						lval,
-						non_constant_p, overflow_p);
+						non_constant_p, overflow_p,
+			                        nullptr_p);
 	if (*non_constant_p)
 	  return t;
 	tree type = TREE_TYPE (t);
@@ -3738,15 +3812,32 @@ cxx_eval_constant_expression (const cons
 		return t;
 	      }
 	  }
-	if (POINTER_TYPE_P (type)
-	    && TREE_CODE (op) == INTEGER_CST
-	    && !integer_zerop (op))
-	  {
-	    if (!ctx->quiet)
-	      error_at (EXPR_LOC_OR_LOC (t, input_location),
-			"reinterpret_cast from integer to pointer");
-	    *non_constant_p = true;
-	    return t;
+	if (POINTER_TYPE_P (type) && TREE_CODE (op) == INTEGER_CST)
+	  {
+	    const char *msg = NULL;
+	    if (integer_zerop (op))
+	      {
+		if (nullptr_p)
+		  *nullptr_p = true;
+
+		tree t1 = TREE_TYPE (type);
+		tree t2 = TREE_TYPE (op);
+		if (TREE_TYPE (t2))
+		  t2 = TREE_TYPE (t2);
+		if (!same_type_ignoring_top_level_qualifiers_p (t1, t2))
+		  msg = "invalid conversion involving a null pointer";
+	      }
+	    else
+	      msg = "reinterpret_cast from integer to pointer";
+
+	    if (msg)
+	      {
+		if (!ctx->quiet)
+		  error_at (EXPR_LOC_OR_LOC (t, input_location), msg);
+
+		*non_constant_p = true;
+		return t;
+	      }
 	  }
 	if (op == oldop && tcode != UNARY_PLUS_EXPR)
 	  /* We didn't fold at the top so we could check for ptr-int
@@ -3779,7 +3870,7 @@ cxx_eval_constant_expression (const cons
       return cxx_eval_constant_expression (ctx, BIND_EXPR_BODY (t),
 					   lval,
 					   non_constant_p, overflow_p,
-					   jump_target);
+					   nullptr_p, jump_target);
 
     case PREINCREMENT_EXPR:
     case POSTINCREMENT_EXPR:
@@ -3828,7 +3919,7 @@ cxx_eval_constant_expression (const cons
 	  tree ctor = lval ? ctx->object : ctx->ctor;
 	  return cxx_eval_constant_expression
 	    (ctx, ctor, lval,
-	     non_constant_p, overflow_p);
+	     non_constant_p, overflow_p, nullptr_p);
 	}
       break;
 
@@ -3935,8 +4026,9 @@ cxx_eval_outermost_constant_expr (tree t
 	r = TARGET_EXPR_INITIAL (r);
     }
 
-  r = cxx_eval_constant_expression (&ctx, r,
-				    false, &non_constant_p, &overflow_p);
+  bool nullptr_p = false;
+  r = cxx_eval_constant_expression (&ctx, r, false,
+			            &non_constant_p, &overflow_p, &nullptr_p);
 
   verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p);
 
Index: gcc/fold-const.c
===================================================================
--- gcc/fold-const.c	(revision 234306)
+++ gcc/fold-const.c	(working copy)
@@ -8339,6 +8339,20 @@ pointer_may_wrap_p (tree base, tree offs
   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
@@ -8639,6 +8653,38 @@ fold_comparison (location_t loc, enum tr
 	    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 sybols must have a non
+	 null address.  */
+      else if (DECL_P (base0)
+	       && maybe_nonzero_address (base0) > 0
+	       // && (!HAS_DECL_ASSEMBLER_NAME_P (base0) || !DECL_WEAK (base0))
+	       /* 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))
+	       && TREE_CODE (arg1) == INTEGER_CST
+	       && compare_tree_int (arg1, 0) == 0)
+	{
+	  switch (code)
+	    {
+	    case GE_EXPR:
+	    case EQ_EXPR:
+	    case LE_EXPR:
+	      return boolean_false_node;
+	    case GT_EXPR:
+	    case LT_EXPR:
+	    case NE_EXPR:
+	      return boolean_true_node;
+	    default: gcc_unreachable ();
+	    }
+	}
     }
 
   /* Transform comparisons of the form X +- C1 CMP Y +- C2 to
@@ -13509,16 +13555,9 @@ tree_single_nonzero_warnv_p (tree t, boo
 	/* 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)
Index: gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr10.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr10.C	(revision 0)
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr10.C	(working copy)
@@ -0,0 +1,154 @@
+// 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++/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
+
+struct S { int a, b[1]; } s;
+
+constexpr S *p0 = &s;
+constexpr S *p1 = nullptr;
+
+constexpr int *q0 = p0->b;      // { dg-bogus "reinterpret_cast from integer to pointer" }
+constexpr int *r0 = p1->b;      // { dg-error "null pointer|constant expression" }
+
+constexpr bool b0  = p0;        // { dg-warning "address of .B::s." }
+constexpr bool b1  = p0 == 0;   // { dg-warning "address of .B::s." }
+constexpr bool b2  = p0 != 0;   // { dg-warning "address of .B::s." }
+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 .B::s." }
+constexpr bool b8  = 0 == p0;   // { dg-warning "address of .B::s." }
+constexpr bool b9  = 0 != p0;   // { dg-warning "address of .B::s." }
+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 C {
+
+struct A { int i; const A *pa1; const A *pa0; };
+
+constexpr A a1 = { 0, 0, 0  };
+constexpr A a2 = { 1, &a1, 0 };
+
+constexpr const A *pa2 = &a2;
+constexpr int i0 = pa2->i;
+constexpr int i1 = pa2->pa1->i;
+constexpr int i2 = pa2->pa1->pa0->i;           // { dg-error "null pointer|not a constant" }
+
+constexpr const A *pa3 = &*pa2->pa1->pa0;
+constexpr const A *pa4 = pa2->pa1->pa0 + 1;    // { dg-error "null pointer|not a constant" }
+
+constexpr const int *pi0 = &pa2->pa1->pa0->i;  // { dg-error "null pointer|not a constant" }
+
+constexpr const A *pa5 = 0;
+constexpr const int *pi1 = &pa5->i;            // { dg-error "null pointer|not a constant" }
+
+}
+
+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
+
+}
Index: gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr9.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr9.C	(revision 0)
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr9.C	(working copy)
@@ -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));
Index: gcc/testsuite/g++.dg/cpp0x/constexpr-array5.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/constexpr-array5.C	(revision 234306)
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-array5.C	(working copy)
@@ -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" }
Index: gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr.C	(revision 234306)
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr.C	(working copy)
@@ -1,6 +1,152 @@
+// PR c++/60760 - arithmetic on null pointers should not be allowed
+//     in constant expressions
 // { dg-do compile { target c++11 } }
+// { dg-additional-options "-Wno-pointer-arith" }
+
+namespace N {
 
 constexpr int zero() { return 0; }
 
 void* ptr1 = zero();		// { dg-error "int" }
 constexpr void* ptr2 = zero();	// { dg-error "int" }
+
+}
+
+#define Assert(e)   static_assert (e, #e)
+
+
+// Generate a null poiinter.
+constexpr int* null () { return 0; }
+
+namespace SimpleTests {
+
+constexpr int* p0 = nullptr;
+constexpr int* q0 = p0;
+constexpr int* r0 = null ();
+
+// Adding or subtracting zero from a null pointer is valid in C++.
+constexpr int* p1 = p0 + 0;
+constexpr int* p2 = p0 - 0;
+constexpr int* p3 = 0 + p0;
+
+// While the text of the C++ standard still doesn't allow it, CWG
+// issue 232 implies that dererencing a null pointer is intended
+// to be permitted in contexts where the result isn't evaluated.
+// For compatibility with C that should at a minimum include
+// expressions like &*p that are valid there.
+constexpr int* p4 = &*p0;
+constexpr int* p5 = p0 + 1;       // { dg-error "null pointer|not a constant" }
+constexpr int* p6 = 1 + p0;       // { dg-error "null pointer|not a constant" }
+constexpr int* p7 = p0 - 1;       // { dg-error "null pointer|not a constant" }
+constexpr int* p8 = &p0 [0];
+constexpr int* p9 = &0 [p0];
+
+constexpr int* p10 = null () + 2; // { dg-error "null pointer|not a constant" }
+constexpr int* p11 = 3 + null (); // { dg-error "null pointer|not a constant" }
+constexpr int* p12 = null () - 4; // { dg-error "null pointer|not a constant" }
+constexpr int* p13 = &null ()[4]; // { dg-error "null pointer|not a constant" }
+constexpr int* p14 = &3[null ()]; // { dg-error "null pointer|not a constant" }
+
+constexpr int* p15 = &null ()[0];
+constexpr int* p16 = &0[null ()];
+
+Assert (p15 == null ());
+Assert (&*p15 == &*null ());
+Assert (&p15 [0] == &null () [0]);
+Assert (&null () [0] == &0 [null ()]);
+Assert (&0 [null ()]  == &null () [0]);
+Assert (&null () [0] == p0);
+Assert (p0 == &null () [0]);
+Assert (&null () [0] == p0);
+
+constexpr int* q1 = q0 + 0;
+constexpr int* q2 = q0 - 0;
+constexpr int* q3 = q0 + 1;       // { dg-error "null pointer|not a constant" }
+constexpr int* q4 = q0 + 2;       // { dg-error "null pointer|not a constant" }
+constexpr int* q5 = &q0 [0];
+
+// Subtracting null pointers from one another is valid.
+Assert (0 == (p0 - (int*)0));
+Assert (0 == (p0 - static_cast<int*>(0)));
+Assert (0 == (p0 - (int*)nullptr));
+Assert (0 == (p0 - static_cast<int*>(nullptr)));
+Assert (0 == (p0 - p0));
+Assert (0 == (p0 - q0));
+Assert (0 == (p0 - r0));
+Assert (0 == ((int*)0 - p0));
+Assert (0 == (static_cast<int*>(0) - p0));
+Assert (0 == ((int*)nullptr - p0));
+Assert (0 == ( static_cast<int*>(nullptr) - p0));
+Assert (0 == ( q0 - p0));
+Assert (0 == ( r0 - p0));
+
+}
+
+namespace IndirectTests {
+
+struct S { int i, j; struct SA { struct SB { int *pi; } sb; } sa; };
+
+constexpr S* ps = (S*)0;
+
+// Comparing null pointers is valid.
+Assert ( (ps == ps));
+Assert (!(ps != ps));
+Assert (!(ps <  ps));
+Assert ( (ps <= ps));
+Assert (!(ps >  ps));
+Assert ( (ps >= ps));
+
+Assert ( (ps == (S*)0));
+Assert (!(ps != (S*)0));
+Assert (!(ps <  (S*)0));
+Assert ( (ps <= (S*)0));
+Assert (!(ps >  (S*)0));
+Assert ( (ps >= (S*)0));
+
+constexpr S* ps1 = ps;
+constexpr S* ps2 = ps1;
+
+// The following aren't diagnosed due to a bug.
+// constexpr int* pi0 = &((S*)0)->i;
+// constexpr int* pi1 = &((S*)nullptr)->i;
+
+constexpr int* pj0 = &((S*)0)->j;        // { dg-error "null pointer|not a constant" }
+constexpr int* pj1 = &((S*)nullptr)->j;  // { dg-error "null pointer|not a constant" }
+
+constexpr int* psi = &ps->i;            // { dg-error "null pointer|not a constant" }
+constexpr int* psj = &ps->j;            // { dg-error "null pointer|not a constant" }
+
+constexpr int* ps1i = &ps1->i;          // { dg-error "null pointer|not a constant" }
+constexpr int* ps2i = &ps1->i;          // { dg-error "null pointer|not a constant" }
+
+constexpr int* ps1j = &ps1->j;          // { dg-error "null pointer|not a constant" }
+constexpr int* ps2j = &ps1->j;          // { dg-error "null pointer|not a constant" }
+
+}
+
+namespace FunctionTests {
+
+typedef void Func ();
+
+// Arithmetic on member function pointers is diagnosed with -Wpointer-arith.
+// With constexpr, only zero may be added or subtracted.
+constexpr Func *pf0 = 0;
+constexpr Func *pf1 = pf0 + 0;  // triggers -Wpointer-arith
+constexpr Func *pf2 = pf0 - 0;  // triggers -Wpointer-arith
+constexpr Func *pf3 = 0 + pf0;  // triggers -Wpointer-arith
+constexpr Func *pf4 = pf0 + 1;  // { dg-error "null pointer|not a constant" }
+constexpr Func *pf5 = 2 + pf0;  // { dg-error "null pointer|not a constant" }
+constexpr Func *pf6 = pf0 - 3;  // { dg-error "null pointer|not a constant" }
+
+struct S;
+typedef void (S::*MemFuncPtr)();
+
+// Arithmetic on member function pointers is rejected with a hard error.
+constexpr MemFuncPtr pmf0 = nullptr;
+constexpr MemFuncPtr pmf1 = pmf0 + 0;   // { dg-error "invalid operands" }
+constexpr MemFuncPtr pmf2 = 0 + pmf0;   // { dg-error "invalid operands" }
+constexpr MemFuncPtr pmf3 = pmf0 + 1;   // { dg-error "invalid operands" }
+constexpr MemFuncPtr pmf4 = 1 + pmf0;   // { dg-error "invalid operands" }
+constexpr MemFuncPtr pmf5 = pmf0 - 1;   // { dg-error "invalid operands" }
+
+}
Index: gcc/testsuite/g++.dg/cpp0x/constexpr-string.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/constexpr-string.C	(revision 234306)
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-string.C	(working copy)
@@ -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" }
Index: gcc/testsuite/g++.dg/cpp0x/constexpr-tuple.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/constexpr-tuple.C	(revision 234306)
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-tuple.C	(working copy)
@@ -66,30 +66,30 @@ class background_hello
 public:
     background_hello()
     {
-      __builtin_printf("default ctor called, this=%p\n", this);
+      __builtin_printf("%s, this=%p, c=%i\n", __PRETTY_FUNCTION__, this, c);
       ++c;
     }
 
     background_hello(const background_hello &)
     {
-      __builtin_printf("copy ctor called\n");
+      __builtin_printf("%s, this=%p, c=%i\n", __PRETTY_FUNCTION__, this, c);
       ++c;
     }
 
     background_hello(background_hello &&)
     {
-      __builtin_printf("move ctor called\n");
+      __builtin_printf("%s, this=%p, c=%i\n", __PRETTY_FUNCTION__, this, c);
       ++c;
     }
 
     void operator ()() const
     {
-      __builtin_printf("void background_hello::operator()() called, this=%p\n", this);
+      __builtin_printf("%s, this=%p, c=%i\n", __PRETTY_FUNCTION__, this, c);
     }
 
     ~background_hello()
     {
-      __builtin_printf("destructor called, this=%p\n", this);
+      __builtin_printf("%s, this=%p, c=%i\n", __PRETTY_FUNCTION__, this, c);
       --c;
     }
 
Index: gcc/testsuite/g++.dg/cpp0x/constexpr-wstring2.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/constexpr-wstring2.C	(revision 234306)
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-wstring2.C	(working copy)
@@ -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" }
Index: gcc/testsuite/g++.dg/cpp0x/pr65398.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/pr65398.C	(revision 234306)
+++ gcc/testsuite/g++.dg/cpp0x/pr65398.C	(working copy)
@@ -12,17 +12,17 @@ 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" }
-constexpr char e2 = *(&s[5] - 2); // { dg-error "is not a constant expression" }
-constexpr char e3 = *(&s[5] - 3); // { dg-error "is not a constant expression" }
+constexpr char e1 = *(&s[5] - 1); // { dg-error "array subscript" }
+constexpr char e2 = *(&s[5] - 2); // { dg-error "array subscript" }
+constexpr char e3 = *(&s[5] - 3); // { dg-error "array subscript" }
 
 SA (c1 == 'a');
 SA (c2 == 'b');
@@ -45,17 +45,17 @@ 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" }
-constexpr char k2 = *(&l[5] - 2); // { dg-error "is not a constant expression" }
-constexpr char k3 = *(&l[5] - 3); // { dg-error "is not a constant expression" }
+constexpr char k1 = *(&l[5] - 1); // { dg-error "array subscript" }
+constexpr char k2 = *(&l[5] - 2); // { dg-error "array subscript" }
+constexpr char k3 = *(&l[5] - 3); // { dg-error "array subscript" }
 
 SA (i1 == 'c');
 SA (i2 == 'd');
Index: gcc/testsuite/g++.dg/ext/constexpr-vla1.C
===================================================================
--- gcc/testsuite/g++.dg/ext/constexpr-vla1.C	(revision 234306)
+++ gcc/testsuite/g++.dg/ext/constexpr-vla1.C	(working copy)
@@ -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" }
Index: gcc/testsuite/g++.dg/ext/constexpr-vla2.C
===================================================================
--- gcc/testsuite/g++.dg/ext/constexpr-vla2.C	(revision 234306)
+++ gcc/testsuite/g++.dg/ext/constexpr-vla2.C	(working copy)
@@ -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" }
Index: gcc/testsuite/g++.dg/ext/constexpr-vla3.C
===================================================================
--- gcc/testsuite/g++.dg/ext/constexpr-vla3.C	(revision 234306)
+++ gcc/testsuite/g++.dg/ext/constexpr-vla3.C	(working copy)
@@ -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" }
Index: gcc/testsuite/g++.dg/ubsan/pr63956.C
===================================================================
--- gcc/testsuite/g++.dg/ubsan/pr63956.C	(revision 234306)
+++ gcc/testsuite/g++.dg/ubsan/pr63956.C	(working copy)
@@ -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)
@@ -106,7 +106,7 @@ fn7 (const int *a, int b)
 
 constexpr int n1 = 7;
 constexpr int n2 = fn7 (&n1, 5);
-constexpr int n3 = fn7 ((const int *) 0, 8); // { dg-error "is not a constant expression" }
+constexpr int n3 = fn7 ((const int *) 0, 8); // { dg-error "null pointer" }
 
 constexpr int
 fn8 (int i)
@@ -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)

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

* Re: [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression
  2016-03-17 21:35   ` Martin Sebor
@ 2016-03-18 17:48     ` Jeff Law
  2016-03-21 17:55       ` Jason Merrill
  2016-03-18 19:13     ` Jeff Law
  1 sibling, 1 reply; 26+ messages in thread
From: Jeff Law @ 2016-03-18 17:48 UTC (permalink / raw)
  To: Martin Sebor, Gcc Patch List, Jason Merrill; +Cc: Marek Polacek, Jakub Jelinek

On 03/17/2016 03:16 PM, Martin Sebor wrote:
>>>   static tree cxx_eval_constant_expression (const constexpr_ctx *, tree,
>>> -                      bool, bool *, bool *, tree * = NULL);
>>> +                      bool, bool *, bool *, bool * = NULL,
>>> +                                          tree * = NULL);
>> I didn't look deeply, but do you end up fixing all (most) of the callers
>> of cxx_eval_constant_expression?  If so, then you don't need the default
>> initialization.
>
> Thanks for the comments.  The patch only modifies about 10 out
> of the 70 or so calls to the function in the file and the default
> argument helps avoid making the rest of the changes.  (I have some
> ideas for improving the APIs of these functions that I'd like to
> run by Jason when we're done with the 6.0 work.)
OK.  Then let's keep the default initialization.


>
> The difficulty I've run into with detecting these problems in later
> phases is that some invalid expressions have already been simplified
> by the front end.  The example that applies here (even though this
> is still the front end) is this:
Yea.  I was hoping that the delayed folding work would be helping in 
getting a more faithful representation out of the front-ends.

>
>    constexpr int* p = 0;
>    constexpr bool b0 = &p[0] == 0;   // accepted
>    constexpr bool b1 = &p[1] == 0;   // rejected
>
> Both b0 and b1 are invalid and should be diagnosed, but only b1
> is.  b1 isn't because because by the time we see its initializer
> in constexpr.c it's been transformed into the equivalent of "b1
> = (int*)ps" (though we don't see the cast which would also make
> it invalid).
>
> But if we can avoid these early simplifying transformations and
> retain a more faithful representation of the original source then
> doing the checking later will likely be simpler and result in
> detecting more problems with greater consistency and less effort.
Do we know where the folding is happening for this case and is it 
something we can reasonably defer?    ie, is this just a case we missed 
as part of the deferred folding work and hence should have its own 
distinct BZ to track?

>> Hmm, I thought we already had code to do this somewhere.   It looks like
>> it's moved around quite a bit.  I think you want to be using
>> symtab_node::nonzero_address to determine if a given symbol must bind to
>> a nonzero address.
>
> Thanks for the hint.  I had looked for existing functions but
> couldn't find one that worked.  decl_with_nonnull_addr_p() in
> c-common.c looked promising but it's not accessible here and
> it doesn't do the right thing when HAS_DECL_ASSEMBLER_NAME_P()
> is false (it ICEs).
Yea, I found the same mis-mash of bits that didn't look directly usable 
for the problem you're tackling.  What's odd is I would have sworn that 
we had code to do exactly what you wanted, but I wasn't able to find it, 
either as a distinct routine or open-coded.

>
> In the end, I added a new function, maybe_nonzero_address(),
> that calls symtab_node::nonzero_address(), and that I factored
> out of tree_single_nonzero_warnv_p() that I was then able to
> use in fold_comparison().
Sounds good.

>
> I've made a few other small adjustments to the patch to avoid
> one false positive, and a few test cases, and tweak the expected
> diagnostics now that Marek has fixed 70194.
>
> I've also wrote myself a small sed script to replace blocks of
> 8 spaces with tabs and ran the patch through it.  I'll integrate
> it into my workflow so I hopefully don't have to worry about this
> ever again.
I'll try to take a look at the updated patch shortly.  It may still hit 
too much of the C++ front-end for me to be comfortable reviewing -- 
we'll see.

jeff

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

* Re: [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression
  2016-03-17 21:35   ` Martin Sebor
  2016-03-18 17:48     ` Jeff Law
@ 2016-03-18 19:13     ` Jeff Law
  1 sibling, 0 replies; 26+ messages in thread
From: Jeff Law @ 2016-03-18 19:13 UTC (permalink / raw)
  To: Martin Sebor, Gcc Patch List, Jason Merrill; +Cc: Marek Polacek, Jakub Jelinek

On 03/17/2016 03:16 PM, Martin Sebor wrote:

>
> gcc-67376.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++/60760 - arithmetic on null pointers should not be allowed in constant
> 	expressions
> PR c++/70228 - insufficient detail in diagnostics for a constexpr out of bounds
> 	array subscript
>
> gcc/testsuite/ChangeLog:
> 2016-03-17  Martin Sebor<msebor@redhat.com>
>
> 	PR c++/67376
> 	PR c++/70170
> 	PR c++/70172
> 	PR c++/60760
> 	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-array5.C: Adjust text of expected diagnostic.
> 	* g++.dg/cpp0x/constexpr-nullptr.C: Add test cases.
> 	* 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-17  Martin Sebor<msebor@redhat.com>
>
> 	PR c++/67376
> 	PR c++/70170
> 	PR c++/70172
> 	PR c++/60760
> 	PR c++/70228
> 	* constexpr.c (cxx_eval_binary_expression): Add argument.
> 	(cxx_eval_component_reference): Same.
> 	(cxx_eval_constant_expression): Same.
> 	(cxx_eval_indirect_ref): Same.
> 	(cxx_eval_outermost_constant_expr): Same.
> 	(diag_array_subscript): New function.
> 	(cxx_eval_call_expression): Adjust.
> 	(cxx_eval_conditional_expression): Same.
> 	(cxx_eval_array_reference): Detect null pointers.
> 	(cxx_eval_statement_list): Adjust.
>
> gcc/ChangeLog:
> 2016-03-17  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.
>
> Index: gcc/cp/constexpr.c
> ===================================================================
> --- gcc/cp/constexpr.c	(revision 234306)
> +++ gcc/cp/constexpr.c	(working copy)
> @@ -1839,11 +1874,26 @@ cxx_eval_array_reference (const constexp

> @@ -3300,10 +3357,21 @@ cxx_eval_constant_expression (const cons
> +
> +      if (TREE_CODE (t) == INTEGER_CST
> +	  && TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE
> +	  && !integer_zerop (t))
> +	{
> +	  if (!ctx->quiet)
> +	    error ("null pointer arithmetic in %qE", t);
> +	  if (nullptr_p)
> +	    *nullptr_p = true;
> +	}
Something looks odd here.

You're testing !integer_zerop, so in T is going to be non-NULL, but you 
mentioned null pointer arithmetic in the error message.  Am I missing 
something here?



> @@ -3738,15 +3812,32 @@ cxx_eval_constant_expression (const cons
> Index: gcc/fold-const.c
> @@ -8639,6 +8653,38 @@ fold_comparison (location_t loc, enum tr
>   	    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 sybols must have a non
> +	 null address.  */
> +      else if (DECL_P (base0)
> +	       && maybe_nonzero_address (base0) > 0
> +	       // && (!HAS_DECL_ASSEMBLER_NAME_P (base0) || !DECL_WEAK (base0))
Please remove the commented out line.


> +	       /* 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))
> +	       && TREE_CODE (arg1) == INTEGER_CST
> +	       && compare_tree_int (arg1, 0) == 0)
> +	{
> +	  switch (code)
> +	    {
> +	    case GE_EXPR:
> +	    case EQ_EXPR:
> +	    case LE_EXPR:
> +	      return boolean_false_node;
> +	    case GT_EXPR:
> +	    case LT_EXPR:
> +	    case NE_EXPR:
> +	      return boolean_true_node;
> +	    default: gcc_unreachable ();
Can you put the gcc_unreachable on a new line?  I know there's a few 
cases in the sources where it's on the default: line, but the vast 
majority have it on its own line.



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

* Re: [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression
  2016-03-18 17:48     ` Jeff Law
@ 2016-03-21 17:55       ` Jason Merrill
  2016-03-21 22:13         ` Jeff Law
  0 siblings, 1 reply; 26+ messages in thread
From: Jason Merrill @ 2016-03-21 17:55 UTC (permalink / raw)
  To: Jeff Law, Martin Sebor, Gcc Patch List; +Cc: Marek Polacek, Jakub Jelinek

On 03/18/2016 01:04 PM, Jeff Law wrote:
> On 03/17/2016 03:16 PM, Martin Sebor wrote:
>> The difficulty I've run into with detecting these problems in later
>> phases is that some invalid expressions have already been simplified
>> by the front end.  The example that applies here (even though this
>> is still the front end) is this:
> Yea.  I was hoping that the delayed folding work would be helping in
> getting a more faithful representation out of the front-ends.

It should.

>>    constexpr int* p = 0;
>>    constexpr bool b0 = &p[0] == 0;   // accepted
>>    constexpr bool b1 = &p[1] == 0;   // rejected
>>
>> Both b0 and b1 are invalid and should be diagnosed, but only b1
>> is.  b1 isn't because because by the time we see its initializer
>> in constexpr.c it's been transformed into the equivalent of "b1
>> = (int*)ps" (though we don't see the cast which would also make
>> it invalid).
>>
>> But if we can avoid these early simplifying transformations and
>> retain a more faithful representation of the original source then
>> doing the checking later will likely be simpler and result in
>> detecting more problems with greater consistency and less effort.
> Do we know where the folding is happening for this case and is it
> something we can reasonably defer?    ie, is this just a case we missed
> as part of the deferred folding work and hence should have its own
> distinct BZ to track?

Yes, why is it already folded?

Jason

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

* Re: [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression
  2016-03-21 17:55       ` Jason Merrill
@ 2016-03-21 22:13         ` Jeff Law
  2016-03-22 19:10           ` Jason Merrill
  0 siblings, 1 reply; 26+ messages in thread
From: Jeff Law @ 2016-03-21 22:13 UTC (permalink / raw)
  To: Jason Merrill, Martin Sebor, Gcc Patch List; +Cc: Marek Polacek, Jakub Jelinek

On 03/21/2016 11:54 AM, Jason Merrill wrote:
>>> Both b0 and b1 are invalid and should be diagnosed, but only b1
>>> is.  b1 isn't because because by the time we see its initializer
>>> in constexpr.c it's been transformed into the equivalent of "b1
>>> = (int*)ps" (though we don't see the cast which would also make
>>> it invalid).
>>>
>>> But if we can avoid these early simplifying transformations and
>>> retain a more faithful representation of the original source then
>>> doing the checking later will likely be simpler and result in
>>> detecting more problems with greater consistency and less effort.
>> Do we know where the folding is happening for this case and is it
>> something we can reasonably defer?    ie, is this just a case we missed
>> as part of the deferred folding work and hence should have its own
>> distinct BZ to track?
>
> Yes, why is it already folded?
Let's pull that out into a separate BZ and tackle it for gcc-7.

jeff

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

* Re: [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression
  2016-03-21 22:13         ` Jeff Law
@ 2016-03-22 19:10           ` Jason Merrill
  2016-03-22 20:38             ` Martin Sebor
  0 siblings, 1 reply; 26+ messages in thread
From: Jason Merrill @ 2016-03-22 19:10 UTC (permalink / raw)
  To: Jeff Law, Martin Sebor, Gcc Patch List; +Cc: Marek Polacek, Jakub Jelinek

On 03/21/2016 06:09 PM, Jeff Law wrote:
> On 03/21/2016 11:54 AM, Jason Merrill wrote:
>>>> Both b0 and b1 are invalid and should be diagnosed, but only b1
>>>> is.  b1 isn't because because by the time we see its initializer
>>>> in constexpr.c it's been transformed into the equivalent of "b1
>>>> = (int*)ps" (though we don't see the cast which would also make
>>>> it invalid).
>>>>
>>>> But if we can avoid these early simplifying transformations and
>>>> retain a more faithful representation of the original source then
>>>> doing the checking later will likely be simpler and result in
>>>> detecting more problems with greater consistency and less effort.
>>> Do we know where the folding is happening for this case and is it
>>> something we can reasonably defer?    ie, is this just a case we missed
>>> as part of the deferred folding work and hence should have its own
>>> distinct BZ to track?
>>
>> Yes, why is it already folded?

> Let's pull that out into a separate BZ and tackle it for gcc-7.

I need to understand the issue before I agree to defer it.

It turns out that the problem is with how cp_build_binary_op calls 
cp_pointer_int_sum and thus the c-common pointer_int_sum, which folds.

The POINTER_PLUS_EXPRs thus created have been a source of many issues 
with constexpr evaluation, since it's impossible to reconstruct the 
original expression, especially because POINTER_PLUS_EXPR uses an 
unsigned second operand.  Deferring lowering to POINTER_PLUS_EXPR would 
help a lot.  But it would indeed be a significant risk at this point.

I think let's defer the fix for c++/60760 (i.e. the nullptr_p bits) 
until stage 1, when it can be combined with the POINTER_PLUS_EXPR fix, 
and put the rest of this patch in now.

Jason

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

* Re: [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression
  2016-03-22 19:10           ` Jason Merrill
@ 2016-03-22 20:38             ` Martin Sebor
  2016-03-23 20:41               ` Jason Merrill
  0 siblings, 1 reply; 26+ messages in thread
From: Martin Sebor @ 2016-03-22 20:38 UTC (permalink / raw)
  To: Jason Merrill, Jeff Law, Gcc Patch List; +Cc: Marek Polacek, Jakub Jelinek

On 03/22/2016 12:52 PM, Jason Merrill wrote:
> On 03/21/2016 06:09 PM, Jeff Law wrote:
>> On 03/21/2016 11:54 AM, Jason Merrill wrote:
>>>>> Both b0 and b1 are invalid and should be diagnosed, but only b1
>>>>> is.  b1 isn't because because by the time we see its initializer
>>>>> in constexpr.c it's been transformed into the equivalent of "b1
>>>>> = (int*)ps" (though we don't see the cast which would also make
>>>>> it invalid).
>>>>>
>>>>> But if we can avoid these early simplifying transformations and
>>>>> retain a more faithful representation of the original source then
>>>>> doing the checking later will likely be simpler and result in
>>>>> detecting more problems with greater consistency and less effort.
>>>> Do we know where the folding is happening for this case and is it
>>>> something we can reasonably defer?    ie, is this just a case we missed
>>>> as part of the deferred folding work and hence should have its own
>>>> distinct BZ to track?
>>>
>>> Yes, why is it already folded?
>
>> Let's pull that out into a separate BZ and tackle it for gcc-7.
>
> I need to understand the issue before I agree to defer it.
>
> It turns out that the problem is with how cp_build_binary_op calls
> cp_pointer_int_sum and thus the c-common pointer_int_sum, which folds.
>
> The POINTER_PLUS_EXPRs thus created have been a source of many issues
> with constexpr evaluation, since it's impossible to reconstruct the
> original expression, especially because POINTER_PLUS_EXPR uses an
> unsigned second operand.  Deferring lowering to POINTER_PLUS_EXPR would
> help a lot.  But it would indeed be a significant risk at this point.
>
> I think let's defer the fix for c++/60760 (i.e. the nullptr_p bits)
> until stage 1, when it can be combined with the POINTER_PLUS_EXPR fix,
> and put the rest of this patch in now.

I can split up the patch into two and post the subset without
the fix for c++/60760, though I don't expect to be done with
it after I get back (next week).

I'd like to understand your concern with the fix for c++/60760.
Is it that it's incomplete (doesn't reject taking the address
of the first member of a struct, as in &null->first_member),
or are you worried that the changes may not be stable enough?

Martin

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

* Re: [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression
  2016-03-22 20:38             ` Martin Sebor
@ 2016-03-23 20:41               ` Jason Merrill
  2016-03-28 23:20                 ` Martin Sebor
  0 siblings, 1 reply; 26+ messages in thread
From: Jason Merrill @ 2016-03-23 20:41 UTC (permalink / raw)
  To: Martin Sebor, Jeff Law, Gcc Patch List; +Cc: Marek Polacek, Jakub Jelinek

On 03/22/2016 04:01 PM, Martin Sebor wrote:
> On 03/22/2016 12:52 PM, Jason Merrill wrote:
>> On 03/21/2016 06:09 PM, Jeff Law wrote:
>>> On 03/21/2016 11:54 AM, Jason Merrill wrote:
>>>>>> Both b0 and b1 are invalid and should be diagnosed, but only b1
>>>>>> is.  b1 isn't because because by the time we see its initializer
>>>>>> in constexpr.c it's been transformed into the equivalent of "b1
>>>>>> = (int*)ps" (though we don't see the cast which would also make
>>>>>> it invalid).
>>>>>>
>>>>>> But if we can avoid these early simplifying transformations and
>>>>>> retain a more faithful representation of the original source then
>>>>>> doing the checking later will likely be simpler and result in
>>>>>> detecting more problems with greater consistency and less effort.
>>>>> Do we know where the folding is happening for this case and is it
>>>>> something we can reasonably defer?    ie, is this just a case we
>>>>> missed
>>>>> as part of the deferred folding work and hence should have its own
>>>>> distinct BZ to track?
>>>>
>>>> Yes, why is it already folded?
>>
>>> Let's pull that out into a separate BZ and tackle it for gcc-7.
>>
>> I need to understand the issue before I agree to defer it.
>>
>> It turns out that the problem is with how cp_build_binary_op calls
>> cp_pointer_int_sum and thus the c-common pointer_int_sum, which folds.
>>
>> The POINTER_PLUS_EXPRs thus created have been a source of many issues
>> with constexpr evaluation, since it's impossible to reconstruct the
>> original expression, especially because POINTER_PLUS_EXPR uses an
>> unsigned second operand.  Deferring lowering to POINTER_PLUS_EXPR would
>> help a lot.  But it would indeed be a significant risk at this point.
>>
>> I think let's defer the fix for c++/60760 (i.e. the nullptr_p bits)
>> until stage 1, when it can be combined with the POINTER_PLUS_EXPR fix,
>> and put the rest of this patch in now.
>
> I can split up the patch into two and post the subset without
> the fix for c++/60760, though I don't expect to be done with
> it after I get back (next week).
>
> I'd like to understand your concern with the fix for c++/60760.
> Is it that it's incomplete (doesn't reject taking the address
> of the first member of a struct, as in &null->first_member),
> or are you worried that the changes may not be stable enough?

More the latter; it seems like significant new code and doesn't fix a 
regression.

Jason

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

* Re: [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression
  2016-03-23 20:41               ` Jason Merrill
@ 2016-03-28 23:20                 ` Martin Sebor
  2016-03-29 19:07                   ` Jason Merrill
  0 siblings, 1 reply; 26+ messages in thread
From: Martin Sebor @ 2016-03-28 23:20 UTC (permalink / raw)
  To: Jason Merrill, Jeff Law, Gcc Patch List; +Cc: Marek Polacek, Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 799 bytes --]

>>> I think let's defer the fix for c++/60760 (i.e. the nullptr_p bits)
>>> until stage 1, when it can be combined with the POINTER_PLUS_EXPR fix,
>>> and put the rest of this patch in now.
>>
>> I can split up the patch into two and post the subset without
>> the fix for c++/60760, though I don't expect to be done with
>> it after I get back (next week).
>>
>> I'd like to understand your concern with the fix for c++/60760.
>> Is it that it's incomplete (doesn't reject taking the address
>> of the first member of a struct, as in &null->first_member),
>> or are you worried that the changes may not be stable enough?
>
> More the latter; it seems like significant new code and doesn't fix a
> regression.

Attached is an updated patch without the fix for c++/60760, retested
on x86_64.

Martin

[-- Attachment #2: gcc-67376.patch --]
[-- Type: text/x-patch, Size: 18999 bytes --]

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-18  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-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-18  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-18  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 7776cac..c900080 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.  */
@@ -1861,6 +1885,7 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
 					false,
 					non_constant_p, overflow_p);
   VERIFY_CONSTANT (index);
+
   if (lval && ary == oldary && index == oldidx)
     return t;
   else if (lval)
@@ -1885,8 +1910,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 +1922,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..3f65243 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,38 @@ 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 sybols must have a non
+	 null address.  */
+      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
+		   || compare_tree_int (DECL_FIELD_OFFSET ((TREE_OPERAND
+				           (TREE_OPERAND (op0, 0), 1))), 0))
+	       && TREE_CODE (arg1) == INTEGER_CST
+	       && compare_tree_int (arg1, 0) == 0)
+	{
+	  switch (code)
+	    {
+	    case GE_EXPR:
+	    case EQ_EXPR:
+	    case LE_EXPR:
+	      return boolean_false_node;
+	    case GT_EXPR:
+	    case LT_EXPR:
+	    case NE_EXPR:
+	      return boolean_true_node;
+	    default:
+	      gcc_unreachable ();
+	    }
+	}
     }
 
   /* Transform comparisons of the form X +- C1 CMP Y +- C2 to
@@ -13506,16 +13552,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-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..bab875c 100644
--- a/gcc/testsuite/g++.dg/cpp0x/pr65398.C
+++ b/gcc/testsuite/g++.dg/cpp0x/pr65398.C
@@ -12,17 +12,17 @@ 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" }
-constexpr char e2 = *(&s[5] - 2); // { dg-error "is not a constant expression" }
-constexpr char e3 = *(&s[5] - 3); // { dg-error "is not a constant expression" }
+constexpr char e1 = *(&s[5] - 1); // { dg-error "array subscript" }
+constexpr char e2 = *(&s[5] - 2); // { dg-error "array subscript" }
+constexpr char e3 = *(&s[5] - 3); // { dg-error "array subscript" }
 
 SA (c1 == 'a');
 SA (c2 == 'b');
@@ -45,17 +45,17 @@ 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" }
-constexpr char k2 = *(&l[5] - 2); // { dg-error "is not a constant expression" }
-constexpr char k3 = *(&l[5] - 3); // { dg-error "is not a constant expression" }
+constexpr char k1 = *(&l[5] - 1); // { dg-error "array subscript" }
+constexpr char k2 = *(&l[5] - 2); // { dg-error "array subscript" }
+constexpr char k3 = *(&l[5] - 3); // { dg-error "array subscript" }
 
 SA (i1 == 'c');
 SA (i2 == 'd');
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)

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

* Re: [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression
  2016-03-28 23:20                 ` Martin Sebor
@ 2016-03-29 19:07                   ` Jason Merrill
  2016-03-30  5:56                     ` Martin Sebor
  0 siblings, 1 reply; 26+ messages in thread
From: Jason Merrill @ 2016-03-29 19:07 UTC (permalink / raw)
  To: Martin Sebor, Jeff Law, Gcc Patch List; +Cc: Marek Polacek, Jakub Jelinek

On 03/28/2016 06:04 PM, Martin Sebor wrote:
> +	       && compare_tree_int (arg1, 0) == 0)

This can be integer_zerop.

> +	    case GE_EXPR:
> +	    case EQ_EXPR:
> +	    case LE_EXPR:
> +	      return boolean_false_node;
> +	    case GT_EXPR:
> +	    case LT_EXPR:
> +	    case NE_EXPR:
> +	      return boolean_true_node;

EQ and NE make sense, but I would expect both > and >= to be true, < and 
<= to be false.

Are we confident that arr[0] won't make it here as POINTER_PLUS_EXPR or 
some such?

Jason

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

* Re: [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression
  2016-03-29 19:07                   ` Jason Merrill
@ 2016-03-30  5:56                     ` Martin Sebor
  2016-03-30 16:02                       ` Jason Merrill
  0 siblings, 1 reply; 26+ messages in thread
From: Martin Sebor @ 2016-03-30  5:56 UTC (permalink / raw)
  To: Jason Merrill, Jeff Law, Gcc Patch List; +Cc: Marek Polacek, Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 1664 bytes --]

On 03/29/2016 12:54 PM, Jason Merrill wrote:
> On 03/28/2016 06:04 PM, Martin Sebor wrote:
>> +           && compare_tree_int (arg1, 0) == 0)
>
> This can be integer_zerop.

Sure.

>
>> +        case GE_EXPR:
>> +        case EQ_EXPR:
>> +        case LE_EXPR:
>> +          return boolean_false_node;
>> +        case GT_EXPR:
>> +        case LT_EXPR:
>> +        case NE_EXPR:
>> +          return boolean_true_node;
>
> EQ and NE make sense, but I would expect both > and >= to be true, < and
> <= to be false.

I was convinced I had a reason for this but it doesn't seem
to affect regression test results so I must have been wrong.

Relational expressions involving object and null pointers are
undefined in C and I thought unspecified in C++, but given that
GCC evaluates (0 < p) to true it looks like you're right and C++
does seem to require all the others to evaluate as you said.

With the decision to remove the nullptr changes I tried to keep
the amount of testing of null pointers to the minimum necessary
to exercise the fix for comment #10 on 67376.  In light of your
expectation I've added a test to better exercise the relational
expressions involving pointers to struct data members.
Interestingly, stepping through it revealed that the problem
cases you pointed out above are actually handled by
generic_simplify() and never end up in fold_comparison().

Attached is an updated patch.

> 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?

Martin

[-- Attachment #2: gcc-67376.patch --]
[-- Type: text/x-patch, Size: 19562 bytes --]

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-29  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-29  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-29  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..2415094 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.  */
@@ -1861,6 +1885,7 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
 					false,
 					non_constant_p, overflow_p);
   VERIFY_CONSTANT (index);
+
   if (lval && ary == oldary && index == oldidx)
     return t;
   else if (lval)
@@ -1885,8 +1910,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 +1922,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..2c718fb 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,40 @@ 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
+		   || compare_tree_int (DECL_FIELD_OFFSET ((TREE_OPERAND
+				           (TREE_OPERAND (op0, 0), 1))), 0))
+	       && 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 +13554,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..71963102
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-1.C
@@ -0,0 +1,36 @@
+// Verify the correctness of folding relational expressions involving
+// pointers to 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 } }
+
+constexpr const int *p = 0;
+
+struct S { int a, b; };
+
+S s = { 0, 0 };
+
+constexpr const S *q = &s;
+
+#define A(e)  static_assert ((e), #e)
+
+int main ()
+{
+  A (!(p == &q->b));
+  A ( (p != &q->b));
+  A ( (p <  &q->b));
+  A ( (p <= &q->b));
+  A (!(p >  &q->b));
+  A (!(p >= &q->b));
+
+  A (!(&q->b == p));
+  A ( (&q->b != p));
+  A (!(&q->b <  p));
+  A (!(&q->b <= p));
+  A ( (&q->b >  p));
+  A ( (&q->b >= p));
+}
+
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)

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

* Re: [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression
  2016-03-30  5:56                     ` Martin Sebor
@ 2016-03-30 16:02                       ` Jason Merrill
  2016-03-30 16:58                         ` Martin Sebor
  0 siblings, 1 reply; 26+ messages in thread
From: Jason Merrill @ 2016-03-30 16:02 UTC (permalink / raw)
  To: Martin Sebor, Jeff Law, Gcc Patch List; +Cc: Marek Polacek, Jakub Jelinek

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);

Jason

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

* Re: [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression
  2016-03-30 16:02                       ` Jason Merrill
@ 2016-03-30 16:58                         ` Martin Sebor
  2016-03-30 20:19                           ` Jason Merrill
  0 siblings, 1 reply; 26+ messages in thread
From: Martin Sebor @ 2016-03-30 16:58 UTC (permalink / raw)
  To: Jason Merrill, Jeff Law, Gcc Patch List; +Cc: Marek Polacek, Jakub Jelinek

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.

Martin

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

* Re: [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression
  2016-03-30 16:58                         ` Martin Sebor
@ 2016-03-30 20:19                           ` Jason Merrill
  2016-03-30 23:09                             ` Martin Sebor
  2016-03-31 17:53                             ` Jeff Law
  0 siblings, 2 replies; 26+ messages in thread
From: Jason Merrill @ 2016-03-30 20:19 UTC (permalink / raw)
  To: Martin Sebor, Jeff Law, Gcc Patch List; +Cc: Marek Polacek, Jakub Jelinek

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.

> +	       /* 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.

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

Jason

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

* Re: [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression
  2016-03-30 20:19                           ` Jason Merrill
@ 2016-03-30 23:09                             ` Martin Sebor
  2016-03-31  8:48                               ` Jason Merrill
  2016-03-31 17:53                             ` Jeff Law
  1 sibling, 1 reply; 26+ messages in thread
From: Martin Sebor @ 2016-03-30 23:09 UTC (permalink / raw)
  To: Jason Merrill, Jeff Law, Gcc Patch List; +Cc: Marek Polacek, Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 2948 bytes --]

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

[-- Attachment #2: gcc-67376.patch --]
[-- Type: text/x-patch, Size: 21978 bytes --]

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)

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

* Re: [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression
  2016-03-30 23:09                             ` Martin Sebor
@ 2016-03-31  8:48                               ` Jason Merrill
  2016-03-31 19:30                                 ` Martin Sebor
  0 siblings, 1 reply; 26+ messages in thread
From: Jason Merrill @ 2016-03-31  8:48 UTC (permalink / raw)
  To: Martin Sebor, Jeff Law, Gcc Patch List; +Cc: Marek Polacek, Jakub Jelinek

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

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

* Re: [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression
  2016-03-30 20:19                           ` Jason Merrill
  2016-03-30 23:09                             ` Martin Sebor
@ 2016-03-31 17:53                             ` Jeff Law
  1 sibling, 0 replies; 26+ messages in thread
From: Jeff Law @ 2016-03-31 17:53 UTC (permalink / raw)
  To: Jason Merrill, Martin Sebor, Gcc Patch List; +Cc: Marek Polacek, Jakub Jelinek

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.
>
>> +           /* 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.
>
> Also, it looks like you aren't handling the case with the operands
> switched, i.e. 0 == p and such.
Presumably all this stuff runs prior to the call to shorten_compare and 
friends which would canonicalize 0 == p into p == 0?

And yes, I still want to separate the canonicalization, warning and 
optimization that's done by shorten_compare and friends.  It just fell 
out of gcc-6 due to lack of time.

jeff



>
> Jason
>

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

* Re: [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression
  2016-03-31  8:48                               ` Jason Merrill
@ 2016-03-31 19:30                                 ` Martin Sebor
  2016-03-31 20:07                                   ` Jason Merrill
  0 siblings, 1 reply; 26+ messages in thread
From: Martin Sebor @ 2016-03-31 19:30 UTC (permalink / raw)
  To: Jason Merrill, Jeff Law, Gcc Patch List; +Cc: Marek Polacek, Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 1471 bytes --]

>>>> +           /* 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.

I've updated the patch to simplify the tests.

I have to confess I don't yet find it straightforward to tell
exactly which tests are essential to avoid an ICE and which
ones can safely be simplified.

Martin

[-- Attachment #2: gcc-67376.patch --]
[-- Type: text/x-patch, Size: 21886 bytes --]

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-31  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-31  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-31  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..eb66143 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,39 @@ 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. */
+	       && (offset0 == NULL_TREE && bitpos0 != 0)
+	       /* The caller guarantees that when one of the arguments is
+		  constant (i.e., null in this case) it is second.  */
+	       && 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 +13553,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)

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

* Re: [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression
  2016-03-31 19:30                                 ` Martin Sebor
@ 2016-03-31 20:07                                   ` Jason Merrill
  2016-05-31 20:33                                     ` Martin Sebor
  0 siblings, 1 reply; 26+ messages in thread
From: Jason Merrill @ 2016-03-31 20:07 UTC (permalink / raw)
  To: Martin Sebor, Jeff Law, Gcc Patch List; +Cc: Marek Polacek, Jakub Jelinek

OK, thanks.

Jason

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

* Re: [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression
  2016-03-31 20:07                                   ` Jason Merrill
@ 2016-05-31 20:33                                     ` Martin Sebor
  2016-05-31 20:48                                       ` Jason Merrill
  0 siblings, 1 reply; 26+ messages in thread
From: Martin Sebor @ 2016-05-31 20:33 UTC (permalink / raw)
  To: Jason Merrill, Jeff Law, Gcc Patch List; +Cc: Marek Polacek, Jakub Jelinek

On 03/31/2016 11:54 AM, Jason Merrill wrote:
> OK, thanks.

This bug was fixed in 6.1 but it is also a GCC 5 regression and
the patch hasn't been applied there yet.  Should I go ahead and
backport it to the 5.x branch?

Martin

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

* Re: [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression
  2016-05-31 20:33                                     ` Martin Sebor
@ 2016-05-31 20:48                                       ` Jason Merrill
  0 siblings, 0 replies; 26+ messages in thread
From: Jason Merrill @ 2016-05-31 20:48 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Jeff Law, Gcc Patch List, Marek Polacek, Jakub Jelinek

On Tue, May 31, 2016 at 1:18 PM, Martin Sebor <msebor@gmail.com> wrote:
> On 03/31/2016 11:54 AM, Jason Merrill wrote:
>>
>> OK, thanks.
>
> This bug was fixed in 6.1 but it is also a GCC 5 regression and
> the patch hasn't been applied there yet.  Should I go ahead and
> backport it to the 5.x branch?

It seems safe enough to me, but since it touches fold-const let's get
a release manager opinion as well.  Jakub?

Jason

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

end of thread, other threads:[~2016-05-31 17:45 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-03-14 21:25 [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression Martin Sebor
2016-03-14 22:13 ` Jakub Jelinek
2016-03-16 19:38   ` Jeff Law
2016-03-16 22:53     ` Jakub Jelinek
2016-03-16 19:53 ` Jeff Law
2016-03-17 21:35   ` Martin Sebor
2016-03-18 17:48     ` Jeff Law
2016-03-21 17:55       ` Jason Merrill
2016-03-21 22:13         ` Jeff Law
2016-03-22 19:10           ` Jason Merrill
2016-03-22 20:38             ` Martin Sebor
2016-03-23 20:41               ` Jason Merrill
2016-03-28 23:20                 ` Martin Sebor
2016-03-29 19:07                   ` Jason Merrill
2016-03-30  5:56                     ` Martin Sebor
2016-03-30 16:02                       ` Jason Merrill
2016-03-30 16:58                         ` Martin Sebor
2016-03-30 20:19                           ` Jason Merrill
2016-03-30 23:09                             ` Martin Sebor
2016-03-31  8:48                               ` Jason Merrill
2016-03-31 19:30                                 ` Martin Sebor
2016-03-31 20:07                                   ` Jason Merrill
2016-05-31 20:33                                     ` Martin Sebor
2016-05-31 20:48                                       ` Jason Merrill
2016-03-31 17:53                             ` Jeff Law
2016-03-18 19:13     ` Jeff Law

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