public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
@ 2016-05-12 22:34 Martin Sebor
  2016-05-17 19:44 ` Jason Merrill
  0 siblings, 1 reply; 18+ messages in thread
From: Martin Sebor @ 2016-05-12 22:34 UTC (permalink / raw)
  To: Gcc Patch List, Jason Merrill

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

Attached is a resubmission of the patch for c++/60760 originally
submitted late in the 6.0 cycle along with a patch for c++/67376.
Since c++/60760 was not a regression, it was decided that it
would be safer to defer the fix until after the 6.1.0 release.

While retesting this patch I was happy to notice that it also
fixes another bug: c++/71091 - constexpr reference bound to a null
pointer dereference accepted.

Martin

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

PR c++/60760 - arithmetic on null pointers should not be allowed
   in constant expressions
PR c++/71091 - constexpr reference bound to a null pointer dereference
   accepted

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

	PR c++/60760
	PR c++/71091
	* g++.dg/cpp0x/constexpr-nullptr-2.C: New test.
	* gcc/testsuite/g++.dg/ubsan/pr63956.C: Adjust.

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

	PR c++/60760
	PR c++/71091
	* constexpr.c (cxx_eval_call_expression): Add argument.
	(cxx_eval_unary_expression): Same.
	(cxx_eval_conditional_expression): Same.
	(cxx_eval_array_reference): Same.
	(cxx_fold_indirect_ref): Same.
	(cxx_eval_statement_list): Same.
	(cxx_eval_loop_expr): Same.
	(cxx_eval_binary_expression): Same.  Detect and reject invalid uses
	of null pointers.
	(cxx_eval_component_reference): Same.
	(cxx_eval_constant_expression): Same.
	(cxx_eval_indirect_ref):   Add argument. Detect invalid uses of null
	pointers without rejecting them here.
	(cxx_eval_outermost_constant_expr): Adjust.

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 6054d1a..3821ad0 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.  */

@@ -1491,7 +1492,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 	  tree jump_target = NULL_TREE;
 	  cxx_eval_constant_expression (&ctx_with_save_exprs, 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
@@ -1716,20 +1717,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;

@@ -1751,6 +1753,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);
@@ -1791,11 +1802,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
@@ -2066,7 +2077,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;
@@ -2075,7 +2087,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)
@@ -2807,7 +2826,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;
@@ -2832,6 +2852,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;
@@ -3328,7 +3351,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))
@@ -3360,7 +3383,8 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
       new_ctx.save_exprs = &save_exprs;

       cxx_eval_constant_expression (&new_ctx, body, /*lval*/false,
-				    non_constant_p, overflow_p, jump_target);
+				    non_constant_p, overflow_p, NULL,
+				    jump_target);

       /* Forget saved values of SAVE_EXPRs.  */
       for (hash_set<tree>::iterator iter = save_exprs.begin();
@@ -3457,15 +3481,20 @@ cxx_eval_pointer_plus_expression (const constexpr_ctx *ctx, tree t,
 }

 /* Attempt to reduce the expression T to a constant value.
-   On failure, issue diagnostic and return error_mark_node.  */
+   On failure, issue diagnostic and return error_mark_node.
+   Similar to NON_CONSTANT_P and OVERFLOW_P, when NULLPTR_P is non-null,
+   the object it points to is set to true when an invalid use of a null
+   pointer is detected.  */
 /* FIXME unify with c_fully_fold */
 /* FIXME overflow_p is too global */

 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;
@@ -3484,10 +3513,22 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	  if (!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)
@@ -3576,7 +3617,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_constructor (init);
 	    ctx->values->put (r, init);
@@ -3616,7 +3658,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);
@@ -3638,14 +3681,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;

@@ -3656,7 +3701,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);
@@ -3673,18 +3719,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
@@ -3693,7 +3739,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:
@@ -3701,7 +3747,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;
@@ -3744,19 +3791,19 @@ 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);
 	    if (*non_constant_p)
 	      return t;
 	    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;
@@ -3807,7 +3854,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
@@ -3845,7 +3892,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:
@@ -3898,12 +3945,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);
@@ -3924,15 +3971,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
@@ -3965,7 +4025,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:
@@ -4014,7 +4074,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;

@@ -4132,8 +4192,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/testsuite/g++.dg/cpp0x/constexpr-nullptr-2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-2.C
new file mode 100644
index 0000000..9ee1316
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-2.C
@@ -0,0 +1,191 @@
+// PR c++/60760 - arithmetic on null pointers should not be allowed
+//     in constant expressions
+// PR c++/71091 - constexpr reference bound to a null pointer dereference
+//     accepted
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "-Wno-pointer-arith" }
+
+// Generate a null poiinter.
+constexpr int* null () { return 0; }
+
+// Test case from comment #0 in c++/60760.
+namespace PR_60760_comment_0 {
+
+constexpr int* ptr = nullptr;
+constexpr int* ptr2 = ptr + 1;		  // { dg-error "null pointer|not a constant" }
+
+}
+
+// Test case from comment #1 in c++/60760.
+namespace PR_60760_comment_1 {
+
+constexpr int* ptr = nullptr;
+constexpr int x = 0;
+constexpr int* ptr2 = ptr + x;   // Adding zero is valid.
+constexpr int* ptr3 = ptr - x;   // As is subtracting zero.
+
+}
+
+// Test case from c++/71091.
+namespace PR_71091 {
+
+constexpr int *p = 0;
+constexpr int &r = *p;			  // { dg-error "null pointer" }
+
+}
+
+// Other testr cases.
+namespace C {
+
+struct S { int a, b[1]; } s;
+
+constexpr S *p0 = &s;
+constexpr S *p1 = nullptr;
+
+constexpr int *r0 = p1->b;		      // { dg-error "null pointer|constant expression" }
+
+}
+
+namespace D {
+
+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 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/ubsan/pr63956.C b/gcc/testsuite/g++.dg/ubsan/pr63956.C
index 25db8a4..ac01fa4 100644
--- a/gcc/testsuite/g++.dg/ubsan/pr63956.C
+++ b/gcc/testsuite/g++.dg/ubsan/pr63956.C
@@ -92,7 +92,7 @@ constexpr int
 fn6 (const int &a, int b)
 {
   if (b != 2)
-    b = a;  // { dg-error "is not a constant expression" }
+    b = a;
   return 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);
+constexpr int n3 = fn7 ((const int *) 0, 8);  // { dg-error "null pointer" }

 constexpr int
 fn8 (int i)

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

* Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
  2016-05-12 22:34 [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions Martin Sebor
@ 2016-05-17 19:44 ` Jason Merrill
       [not found]   ` <574E0A41.9070204@gmail.com>
  0 siblings, 1 reply; 18+ messages in thread
From: Jason Merrill @ 2016-05-17 19:44 UTC (permalink / raw)
  To: Martin Sebor, Gcc Patch List

On 05/12/2016 06:34 PM, Martin Sebor wrote:
> Attached is a resubmission of the patch for c++/60760 originally
> submitted late in the 6.0 cycle along with a patch for c++/67376.
> Since c++/60760 was not a regression, it was decided that it
> would be safer to defer the fix until after the 6.1.0 release.
>
> While retesting this patch I was happy to notice that it also
> fixes another bug: c++/71091 - constexpr reference bound to a null
> pointer dereference accepted.

I'm not sure why we need to track nullptr_p through everything.  Can't 
we set *non_constant_p instead in the places where it's problematic, as 
in cxx_eval_binary_expression?

I understand that the complication comes because of needing to allow

constexpr int *p = &*(int*)0;

but I don't see how cxx_eval_component_reference could come up with a 
constant value for the referent of a null pointer, so we already reject

struct A { int i; };
constexpr A* p = nullptr;
constexpr int i = p->i;

In cxx_eval_indirect_ref, we could check !lval and reject the 
constant-expression at that point.

Jason

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

* Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
       [not found]   ` <574E0A41.9070204@gmail.com>
@ 2016-06-01 14:36     ` Jason Merrill
  2016-06-01 18:44       ` Martin Sebor
  0 siblings, 1 reply; 18+ messages in thread
From: Jason Merrill @ 2016-06-01 14:36 UTC (permalink / raw)
  To: Martin Sebor, Gcc Patch List

On 05/31/2016 06:03 PM, Martin Sebor wrote:
> On 05/17/2016 01:44 PM, Jason Merrill wrote:
>> On 05/12/2016 06:34 PM, Martin Sebor wrote:
>>> Attached is a resubmission of the patch for c++/60760 originally
>>> submitted late in the 6.0 cycle along with a patch for c++/67376.
>>> Since c++/60760 was not a regression, it was decided that it
>>> would be safer to defer the fix until after the 6.1.0 release.
>>>
>>> While retesting this patch I was happy to notice that it also
>>> fixes another bug: c++/71091 - constexpr reference bound to a null
>>> pointer dereference accepted.
>>
>> I'm not sure why we need to track nullptr_p through everything.  Can't
>> we set *non_constant_p instead in the places where it's problematic, as
>> in cxx_eval_binary_expression?
>
> That would have certainly been my preference over adding another
> argument to a subset of the functions, making their declarations
> inconsistent.  Unfortunately, I couldn't come up with a simpler
> way to make it work.
>
> FWIW, my first inclination was to make the nullptr_p flag part
> of constexpr_ctx and move the other xxx_p arguments there, to
> simplify the interface, until I noticed that all the functions
> take a const constexpr_ctx*, preventing them to modify it.  It
> seemed like too much of a change to fix a bug in stage 3, but
> I'd be comfortable making it in stage 1 if you think it's
> worthwhile (though my preference would be to do in a separate
> commit, after fixing this bug).
>>
>> I understand that the complication comes because of needing to allow
>>
>> constexpr int *p = &*(int*)0;
>>
>> but I don't see how cxx_eval_component_reference could come up with a
>> constant value for the referent of a null pointer, so we already reject
>>
>> struct A { int i; };
>> constexpr A* p = nullptr;
>> constexpr int i = p->i;
>>
>> In cxx_eval_indirect_ref, we could check !lval and reject the
>> constant-expression at that point.
>
> The new code in cxx_eval_component_reference diagnoses the following
> problem that's not detected otherwise:
>
>   struct S { const S *s; };
>
>   constexpr S s = { 0 };
>
>   constexpr const void *p = &s.s->s;

Note that this falls under core issue 1530, which has not been resolved. 
  I believe that this is fine under the current wording; only "access" 
to a non-static data member is undefined, and "access" is defined as 
reading or writing.  There has been discussion in the context of UBsan 
about making this undefined, but that hasn't been decided yet.  But I'm 
OK with changing G++ to go in that direction.

cxx_eval_component_reference could check whether 'whole' is a null (or 
other invalid) lvalue; for that testcase it's an INDIRECT_REF of 0.

Jason

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

* Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
  2016-06-01 14:36     ` Jason Merrill
@ 2016-06-01 18:44       ` Martin Sebor
  2016-06-01 19:20         ` Jason Merrill
  0 siblings, 1 reply; 18+ messages in thread
From: Martin Sebor @ 2016-06-01 18:44 UTC (permalink / raw)
  To: Jason Merrill, Gcc Patch List

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

>> The new code in cxx_eval_component_reference diagnoses the following
>> problem that's not detected otherwise:
>>
>>   struct S { const S *s; };
>>
>>   constexpr S s = { 0 };
>>
>>   constexpr const void *p = &s.s->s;
>
> Note that this falls under core issue 1530, which has not been resolved.

I don't quite see the relevance of this issue.  It's concerned
with storage in which an object will exist at some point in
the future when its lifetime begins or where it existed in
the past before its lifetime ended.  There is no object or
storage at s.s above because s.s is null.

It might perhaps be okay to accept the expression above as
an extension with the rationale that the offset of the first
member is zero and so its address must be a null pointer, but
it surely wouldn't be okay in the modified test case below
where the second member's offset is non-zero and there may
not be a way to represent that offset as a pointer. This
seems unquestionably undefined to me.  Did I miss something?

   struct S { const S *s; int i; };

   constexpr S s = { 0 };

   constexpr const void *p = &s.s->i;

>   I believe that this is fine under the current wording; only "access"
> to a non-static data member is undefined, and "access" is defined as
> reading or writing.  There has been discussion in the context of UBsan
> about making this undefined, but that hasn't been decided yet.  But I'm
> OK with changing G++ to go in that direction.
>
> cxx_eval_component_reference could check whether 'whole' is a null (or
> other invalid) lvalue; for that testcase it's an INDIRECT_REF of 0.

Thanks for the hint.  I've made that change and also adjusted
the rest of the patch to avoid passing the nullptr_p around.
It was surprisingly simpler to do than I remembered from my
first attempt back in March.

Martin

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

PR c++/60760 - arithmetic on null pointers should not be allowed
   in constant expressions
PR c++/71091 - constexpr reference bound to a null pointer dereference
   accepted

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

	PR c++/60760
	PR c++/71091
	* g++.dg/cpp0x/constexpr-nullptr-2.C: New test.
	* gcc/testsuite/g++.dg/ubsan/pr63956.C: Adjust.

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

	PR c++/60760
	PR c++/71091
	* constexpr.c (cxx_eval_binary_expression): Reject invalid arithmetic
	involving null pointers.
	(cxx_eval_constant_expression): Same.
	(cxx_eval_component_reference): Reject null pointer dereferences.

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 482f8af..d1fe276 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -1757,6 +1757,13 @@ 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 a null pointer in %qE", lhs);
+      return t;
+    }
 
   if (r == NULL_TREE)
     r = fold_binary_loc (loc, code, type, lhs, rhs);
@@ -2097,6 +2104,11 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
   tree whole = cxx_eval_constant_expression (ctx, orig_whole,
 					     lval,
 					     non_constant_p, overflow_p);
+  if (TREE_CODE (whole) == INDIRECT_REF
+      && integer_zerop (TREE_OPERAND (whole, 0))
+      && !ctx->quiet)
+    error ("dereferencing a null pointer in %qE", orig_whole);
+
   if (TREE_CODE (whole) == PTRMEM_CST)
     whole = cplus_expand_constant (whole);
   if (whole == orig_whole)
@@ -3505,10 +3517,20 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	  if (!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 ("arithmetic involving a null pointer in %qE", t);
+        }
+
       return t;
     }
 
-  switch (TREE_CODE (t))
+  tree_code tcode = TREE_CODE (t);
+  switch (tcode)
     {
     case RESULT_DECL:
       if (lval)
@@ -3919,7 +3941,6 @@ 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,
@@ -3945,15 +3966,26 @@ 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 (!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
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-2.C
new file mode 100644
index 0000000..9ee1316
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-2.C
@@ -0,0 +1,191 @@
+// PR c++/60760 - arithmetic on null pointers should not be allowed
+//     in constant expressions
+// PR c++/71091 - constexpr reference bound to a null pointer dereference
+//     accepted
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "-Wno-pointer-arith" }
+
+// Generate a null poiinter.
+constexpr int* null () { return 0; }
+
+// Test case from comment #0 in c++/60760.
+namespace PR_60760_comment_0 {
+
+constexpr int* ptr = nullptr;
+constexpr int* ptr2 = ptr + 1;                  // { dg-error "null pointer|not a constant" }
+
+}
+
+// Test case from comment #1 in c++/60760.
+namespace PR_60760_comment_1 {
+
+constexpr int* ptr = nullptr;
+constexpr int x = 0;
+constexpr int* ptr2 = ptr + x;   // Adding zero is valid.
+constexpr int* ptr3 = ptr - x;   // As is subtracting zero.
+
+}
+
+// Test case from c++/71091.
+namespace PR_71091 {
+
+constexpr int *p = 0;
+constexpr int &r = *p;                          // { dg-error "null pointer" }
+
+}
+
+// Other testr cases.
+namespace C {
+
+struct S { int a, b[1]; } s;
+
+constexpr S *p0 = &s;
+constexpr S *p1 = nullptr;
+
+constexpr int *r0 = p1->b;                      // { dg-error "null pointer|constant expression" }
+
+}
+
+namespace D {
+
+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 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/ubsan/pr63956.C b/gcc/testsuite/g++.dg/ubsan/pr63956.C
index 25db8a4..ac01fa4 100644
--- a/gcc/testsuite/g++.dg/ubsan/pr63956.C
+++ b/gcc/testsuite/g++.dg/ubsan/pr63956.C
@@ -92,7 +92,7 @@ constexpr int
 fn6 (const int &a, int b)
 {
   if (b != 2)
-    b = a;  // { dg-error "is not a constant expression" }
+    b = a;
   return 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);
+constexpr int n3 = fn7 ((const int *) 0, 8);  // { dg-error "null pointer" }
 
 constexpr int
 fn8 (int i)

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

* Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
  2016-06-01 18:44       ` Martin Sebor
@ 2016-06-01 19:20         ` Jason Merrill
  2016-06-02  2:50           ` Martin Sebor
  2016-06-20 19:17           ` Martin Sebor
  0 siblings, 2 replies; 18+ messages in thread
From: Jason Merrill @ 2016-06-01 19:20 UTC (permalink / raw)
  To: Martin Sebor, Gcc Patch List

On 06/01/2016 02:44 PM, Martin Sebor wrote:
>>> The new code in cxx_eval_component_reference diagnoses the following
>>> problem that's not detected otherwise:
>>>
>>>   struct S { const S *s; };
>>>
>>>   constexpr S s = { 0 };
>>>
>>>   constexpr const void *p = &s.s->s;
>>
>> Note that this falls under core issue 1530, which has not been resolved.
>
> I don't quite see the relevance of this issue.  It's concerned
> with storage in which an object will exist at some point in
> the future when its lifetime begins or where it existed in
> the past before its lifetime ended.  There is no object or
> storage at s.s above because s.s is null.

True.  Unfortunately there isn't any wording about what you can do with 
the result of indirecting a null pointer or pointer to one-past-the-end 
of an array.  There was some proposed for issue 232, but that hasn't 
been a high priority.  What do you see in the standard currently that 
makes it undefined?

> It might perhaps be okay to accept the expression above as
> an extension with the rationale that the offset of the first
> member is zero and so its address must be a null pointer, but
> it surely wouldn't be okay in the modified test case below
> where the second member's offset is non-zero and there may
> not be a way to represent that offset as a pointer.

Why not?  It would be a pointer to address 4 or whatever.

> Thanks for the hint.  I've made that change and also adjusted
> the rest of the patch to avoid passing the nullptr_p around.
> It was surprisingly simpler to do than I remembered from my
> first attempt back in March.

> +  if (code == POINTER_PLUS_EXPR && !*non_constant_p
> +      && tree_int_cst_equal (lhs, null_pointer_node))
> +    {
> +      if (!ctx->quiet)
> +        error ("arithmetic involving a null pointer in %qE", lhs);
> +      return t;
> +    }

> +  if (TREE_CODE (whole) == INDIRECT_REF
> +      && integer_zerop (TREE_OPERAND (whole, 0))
> +      && !ctx->quiet)
> +    error ("dereferencing a null pointer in %qE", orig_whole);

> +      if (TREE_CODE (t) == INTEGER_CST
> +          && TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE
> +          && !integer_zerop (t))
> +        {
> +          if (!ctx->quiet)
> +            error ("arithmetic involving a null pointer in %qE", t);
> +        }

These places should all set *non_constant_p, and the second should 
return t after doing so.  OK with that change.

Jason

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

* Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
  2016-06-01 19:20         ` Jason Merrill
@ 2016-06-02  2:50           ` Martin Sebor
  2016-06-02 15:05             ` Jason Merrill
  2016-06-20 19:17           ` Martin Sebor
  1 sibling, 1 reply; 18+ messages in thread
From: Martin Sebor @ 2016-06-02  2:50 UTC (permalink / raw)
  To: Jason Merrill, Gcc Patch List

On 06/01/2016 01:20 PM, Jason Merrill wrote:
> On 06/01/2016 02:44 PM, Martin Sebor wrote:
>>>> The new code in cxx_eval_component_reference diagnoses the following
>>>> problem that's not detected otherwise:
>>>>
>>>>   struct S { const S *s; };
>>>>
>>>>   constexpr S s = { 0 };
>>>>
>>>>   constexpr const void *p = &s.s->s;
>>>
>>> Note that this falls under core issue 1530, which has not been resolved.
>>
>> I don't quite see the relevance of this issue.  It's concerned
>> with storage in which an object will exist at some point in
>> the future when its lifetime begins or where it existed in
>> the past before its lifetime ended.  There is no object or
>> storage at s.s above because s.s is null.
>
> True.  Unfortunately there isn't any wording about what you can do with
> the result of indirecting a null pointer or pointer to one-past-the-end
> of an array.  There was some proposed for issue 232, but that hasn't
> been a high priority.  What do you see in the standard currently that
> makes it undefined?

In N4567, expr.ref, the E1->E2 expression which is equivalent
to (*E1).E2, the closest match is paragraph 4.2:

   If E2 is a non-static data member and the type of E1 is "cq1
   vq1 X", and the type of E2 is "cq2 vq2 T", the expression
   designates the named member of the object designated by the
   first expression.

There is no object, so 4.2 doesn't apply, and since there is
no other bullet that would apply, the behavior is undefined.

>> It might perhaps be okay to accept the expression above as
>> an extension with the rationale that the offset of the first
>> member is zero and so its address must be a null pointer, but
>> it surely wouldn't be okay in the modified test case below
>> where the second member's offset is non-zero and there may
>> not be a way to represent that offset as a pointer.
>
> Why not?  It would be a pointer to address 4 or whatever.

Because the hardware may simply not have a representation for
4 (or any small integer) in a pointer type.  Possibly because
it uses that bit pattern for some special mapping and when
there is no mapping, using the invalid pointer value traps.
Whether or not this is the case is implementation-defined
(certainly in C for this reason, and I expect also in C++).

For core constant expressions, my impression is that they
are deliberately constrained to a fairly narrow guaranteed-
to-be-valid subset, which is why things like reinterpret_cast
are not allowed.  The expression we're discussing basically
boils down to a conversion of the offset 4 to a pointer,
which is equivalent to the forbidden reinterpret_cast.

I agree that the standard text isn't ironclad on this, but
I can't think of a useful purpose that allowing constexpr
expressions to form invalid addresses would serve even in
the (common) case where the hardware does allow it.

 From a practical point of view, if GCC were to continue to
allow it, it would need to track those pointers and diagnose
them on their first use, because that is undefined in any
case.

>> +  if (code == POINTER_PLUS_EXPR && !*non_constant_p
>> +      && tree_int_cst_equal (lhs, null_pointer_node))
>> +    {
>> +      if (!ctx->quiet)
>> +        error ("arithmetic involving a null pointer in %qE", lhs);
>> +      return t;
>> +    }
>
>> +  if (TREE_CODE (whole) == INDIRECT_REF
>> +      && integer_zerop (TREE_OPERAND (whole, 0))
>> +      && !ctx->quiet)
>> +    error ("dereferencing a null pointer in %qE", orig_whole);
>
>> +      if (TREE_CODE (t) == INTEGER_CST
>> +          && TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE
>> +          && !integer_zerop (t))
>> +        {
>> +          if (!ctx->quiet)
>> +            error ("arithmetic involving a null pointer in %qE", t);
>> +        }
>
> These places should all set *non_constant_p, and the second should
> return t after doing so.  OK with that change.

Thanks.  I've made the change, but I haven't managed to come up
with a test to exercise it.  IIUC, the purpose setting
non_constant_p while the quiet flag is set is to determine without
causing errors that expressions are not valid constant expressions
by passing them to __builtin_constant_p, correct?

If so, then there must be something wrong somewhere because the
test case below fails the static assertion (the first error is
expected with the patch):

$ cat null.c && /home/msebor/build/gcc-60760/gcc/xgcc -B 
/home/msebor/build/gcc-60760/gcc -S -Wall -Wextra -Wpedantic -xc++ null.c
constexpr int *p = 0;
constexpr int *q = p + 1;

static_assert (!__builtin_constant_p (p + 1), "<<<");

null.c:2:24: error: arithmetic involving a null pointer in ‘0u’
  constexpr int *q = p + 1;
                         ^
null.c:4:1: error: static assertion failed: <<<
  static_assert (!__builtin_constant_p (p + 1), "<<<");
  ^~~~~~~~~~~~~

(I know of at least one bug here, c++/70552, but this one is new.)

Martin

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

* Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
  2016-06-02  2:50           ` Martin Sebor
@ 2016-06-02 15:05             ` Jason Merrill
  0 siblings, 0 replies; 18+ messages in thread
From: Jason Merrill @ 2016-06-02 15:05 UTC (permalink / raw)
  To: Martin Sebor, Gcc Patch List

On 06/01/2016 10:49 PM, Martin Sebor wrote:
> On 06/01/2016 01:20 PM, Jason Merrill wrote:
>> On 06/01/2016 02:44 PM, Martin Sebor wrote:
>>>>> The new code in cxx_eval_component_reference diagnoses the following
>>>>> problem that's not detected otherwise:
>>>>>
>>>>>   struct S { const S *s; };
>>>>>
>>>>>   constexpr S s = { 0 };
>>>>>
>>>>>   constexpr const void *p = &s.s->s;
>>>>
>>>> Note that this falls under core issue 1530, which has not been
>>>> resolved.
>>>
>>> I don't quite see the relevance of this issue.  It's concerned
>>> with storage in which an object will exist at some point in
>>> the future when its lifetime begins or where it existed in
>>> the past before its lifetime ended.  There is no object or
>>> storage at s.s above because s.s is null.
>>
>> True.  Unfortunately there isn't any wording about what you can do with
>> the result of indirecting a null pointer or pointer to one-past-the-end
>> of an array.  There was some proposed for issue 232, but that hasn't
>> been a high priority.  What do you see in the standard currently that
>> makes it undefined?
>
> In N4567, expr.ref, the E1->E2 expression which is equivalent
> to (*E1).E2, the closest match is paragraph 4.2:
>
>   If E2 is a non-static data member and the type of E1 is "cq1
>   vq1 X", and the type of E2 is "cq2 vq2 T", the expression
>   designates the named member of the object designated by the
>   first expression.
>
> There is no object, so 4.2 doesn't apply, and since there is
> no other bullet that would apply, the behavior is undefined.

OK, I'll buy that.

>>> +  if (code == POINTER_PLUS_EXPR && !*non_constant_p
>>> +      && tree_int_cst_equal (lhs, null_pointer_node))
>>> +    {
>>> +      if (!ctx->quiet)
>>> +        error ("arithmetic involving a null pointer in %qE", lhs);
>>> +      return t;
>>> +    }
>>
>>> +  if (TREE_CODE (whole) == INDIRECT_REF
>>> +      && integer_zerop (TREE_OPERAND (whole, 0))
>>> +      && !ctx->quiet)
>>> +    error ("dereferencing a null pointer in %qE", orig_whole);
>>
>>> +      if (TREE_CODE (t) == INTEGER_CST
>>> +          && TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE
>>> +          && !integer_zerop (t))
>>> +        {
>>> +          if (!ctx->quiet)
>>> +            error ("arithmetic involving a null pointer in %qE", t);
>>> +        }
>>
>> These places should all set *non_constant_p, and the second should
>> return t after doing so.  OK with that change.
>
> Thanks.  I've made the change, but I haven't managed to come up
> with a test to exercise it.  IIUC, the purpose setting
> non_constant_p while the quiet flag is set is to determine without
> causing errors that expressions are not valid constant expressions
> by passing them to __builtin_constant_p, correct?

No, __builtin_constant_p allows more things than C++ constant 
expressions.  The purpose of setting *non_constant_p is to cause 
maybe_constant_value to return its argument unchanged.

Jason

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

* Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
  2016-06-01 19:20         ` Jason Merrill
  2016-06-02  2:50           ` Martin Sebor
@ 2016-06-20 19:17           ` Martin Sebor
  2016-06-23 21:36             ` Jason Merrill
  1 sibling, 1 reply; 18+ messages in thread
From: Martin Sebor @ 2016-06-20 19:17 UTC (permalink / raw)
  To: Jason Merrill, Gcc Patch List

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

>> +  if (TREE_CODE (whole) == INDIRECT_REF
>> +      && integer_zerop (TREE_OPERAND (whole, 0))
>> +      && !ctx->quiet)
>> +    error ("dereferencing a null pointer in %qE", orig_whole);
>
>> +      if (TREE_CODE (t) == INTEGER_CST
>> +          && TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE
>> +          && !integer_zerop (t))
>> +        {
>> +          if (!ctx->quiet)
>> +            error ("arithmetic involving a null pointer in %qE", t);
>> +        }
>
> These places should all set *non_constant_p, and the second should
> return t after doing so.  OK with that change.

Some additional testing exposed a couple of bugs in the patch.
First, adding or subtracting a constant zero to or from a null
pointer is valid (it was rejected with the previous patch).
Second, the code in cxx_eval_constant_expression that tried
to detect invalid conversions didn't handle qualifiers
correctly in all cases, causing conversions that add constness
to be rejected.  Finally, while fixing these issues I decided
that dereferencing and indirecting through null pointers would
be more appropriately handled in the functions that deal with
those expressions rather than in cxx_eval_constant_expression.

Since the updates aren't completely trivial I post the new
version of the patch for another review before committing it.

Thanks
Martin


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

PR c++/60760 - arithmetic on null pointers should not be allowed in constant
  expressions
PR c++/71091 - constexpr reference bound to a null pointer dereference
   accepted

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

	PR c++/60760
	PR c++/71091
	* constexpr.c (cxx_eval_binary_expression): Reject invalid expressions
	involving null pointers.
	(cxx_eval_component_reference): Reject null pointer dereferences.
	(cxx_eval_indirect_ref): Reject indirecting through null pointers.
	(cxx_eval_constant_expression): Reject invalid expressions involving
	null pointers.

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

	PR c++/60760
	PR c++/71091
	* g++.dg/cpp0x/constexpr-nullptr-2.C: New test.
	* g++.dg/cpp1y/constexpr-sfinae.C: Correct.
	* g++.dg/ubsan/pr63956.C: Correct.

Index: gcc/cp/constexpr.c
===================================================================
--- gcc/cp/constexpr.c	(revision 237582)
+++ gcc/cp/constexpr.c	(working copy)
@@ -1811,6 +1811,14 @@ cxx_eval_binary_expression (const constexpr_ctx *c
 		   || 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)
+      && !tree_int_cst_equal (rhs, integer_zero_node))
+    {
+      if (!ctx->quiet)
+        error ("arithmetic involving a null pointer in %qE", lhs);
+      return t;
+    }
 
   if (r == NULL_TREE)
     r = fold_binary_loc (loc, code, type, lhs, rhs);
@@ -2151,6 +2159,11 @@ cxx_eval_component_reference (const constexpr_ctx
   tree whole = cxx_eval_constant_expression (ctx, orig_whole,
 					     lval,
 					     non_constant_p, overflow_p);
+  if (TREE_CODE (whole) == INDIRECT_REF
+      && integer_zerop (TREE_OPERAND (whole, 0))
+      && !ctx->quiet)
+    error ("dereferencing a null pointer in %qE", orig_whole);
+
   if (TREE_CODE (whole) == PTRMEM_CST)
     whole = cplus_expand_constant (whole);
   if (whole == orig_whole)
@@ -2911,6 +2924,14 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, t
       if (*non_constant_p)
 	return t;
 
+      if (integer_zerop (op0))
+	{
+	  if (!ctx->quiet)
+	    error ("dereferencing a null pointer");
+	  *non_constant_p = true;
+	  return t;
+	}
+
       r = cxx_fold_indirect_ref (EXPR_LOCATION (t), TREE_TYPE (t), op0,
 				 &empty_base);
       if (r == NULL_TREE)
@@ -3559,10 +3580,20 @@ cxx_eval_constant_expression (const constexpr_ctx
 	  if (!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 ("arithmetic involving a null pointer in %qE", t);
+        }
+
       return t;
     }
 
-  switch (TREE_CODE (t))
+  tree_code tcode = TREE_CODE (t);
+  switch (tcode)
     {
     case RESULT_DECL:
       if (lval)
@@ -3973,7 +4004,6 @@ cxx_eval_constant_expression (const constexpr_ctx
     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,
@@ -3999,15 +4029,43 @@ cxx_eval_constant_expression (const constexpr_ctx
 		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)
+          {
+	    if (integer_zerop (op))
+	      {
+		if (TREE_CODE (type) == REFERENCE_TYPE)
+		  {
+		    if (!ctx->quiet)
+		      error_at (EXPR_LOC_OR_LOC (t, input_location),
+				"dereferencing a null pointer");
+		    *non_constant_p = true;
+		    return t;
+		  }
+		else if (TREE_CODE (TREE_TYPE (op)) == POINTER_TYPE)
+		  {
+		    tree t1 = TREE_TYPE (type);
+		    tree t2 = TREE_TYPE (TREE_TYPE (op));
+		
+		    if (!same_type_ignoring_top_level_qualifiers_p (t1, t2))
+		      {
+			if (!ctx->quiet)
+			  error_at (EXPR_LOC_OR_LOC (t, input_location),
+			    "invalid conversion involving a null pointer");
+			
+			*non_constant_p = true;
+			return t;
+		      }
+		  }
+	      }
+            else if (!integer_zerop (op))
+	      {
+		if (!ctx->quiet)
+		  error_at (EXPR_LOC_OR_LOC (t, input_location),
+			    "invalid conversion from %qT to %qT",
+			    TREE_TYPE (op), type);
+		*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
Index: gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-2.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-2.C	(nonexistent)
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-2.C	(working copy)
@@ -0,0 +1,238 @@
+// PR c++/60760 - arithmetic on null pointers should not be allowed
+//     in constant expressions
+// PR c++/71091 - constexpr reference bound to a null pointer dereference
+//     accepted
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "-Wno-pointer-arith" }
+
+// Generate a null poiinter.
+constexpr int* null () { return 0; }
+
+// Test case from comment #0 in c++/60760.
+namespace PR_60760_comment_0 {
+
+constexpr int* ptr = nullptr;
+constexpr int* ptr2 = ptr + 1;   // { dg-error "null pointer|not a constant" }
+
+}
+
+// Test case from comment #1 in c++/60760.
+namespace PR_60760_comment_1 {
+
+constexpr int* ptr = nullptr;
+
+constexpr int zero = 0;
+constexpr int* ptr2 = ptr + zero;   // Adding zero is valid.
+constexpr int* ptr3 = ptr - zero;   // As is subtracting zero.
+
+}
+
+// Test case from c++/71091.
+namespace PR_71091 {
+
+constexpr int *p = 0;
+constexpr const int &r = *p;   // { dg-error "dereferencing a null pointer" }
+
+}
+
+// Other test cases.
+namespace C {
+
+struct S { int a, b[1]; } s;
+
+constexpr S *p0 = &s;
+constexpr S *p1 = nullptr;
+constexpr int *r0 = p1->b;   // { dg-error "null pointer|constant expression" }
+
+// Adding and subtracting zero from and to a null pointer is valid.
+constexpr S* r1 = p1 + 0;
+constexpr S* r2 = r1 - 0;
+
+constexpr int zero = 0;
+
+constexpr S* r3 = r2 + zero;
+constexpr S* r4 = r3 - zero;
+
+static_assert (r4 == nullptr, "r4 == nullptr");
+
+constexpr const S *pcs = p0;
+constexpr int d1 = pcs - p0;
+constexpr int d2 = p0 - pcs;
+
+constexpr bool b = !p1 && !pcs;
+}
+
+namespace D {
+
+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 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 IncompleteTypeTests {
+
+// The type must be complete.
+struct X;
+constexpr X *px0 = nullptr;
+constexpr X *px1 = px0 + 0;     // { dg-error "invalid use of incomplete type"  }
+constexpr X *px2 = px0 - 0;     // { dg-error "invalid use of incomplete type"  }
+constexpr X *px3 = px0 - px0;   // { dg-error "invalid use of incomplete type"  }
+
+}
+
+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" }
+
+}
+
+namespace ConversionTest {
+
+struct A {
+  int *p;
+};
+
+constexpr const int* f (const int *p) { return p; }
+
+void f ()
+{
+  static_assert (!f (0), "f (a.p)");
+  static_assert (!f (nullptr), "f (a.p)");
+
+  constexpr A a = A ();
+
+  static_assert (!f (a.p), "f (a.p)");
+}
+
+}
Index: gcc/testsuite/g++.dg/cpp1y/constexpr-sfinae.C
===================================================================
--- gcc/testsuite/g++.dg/cpp1y/constexpr-sfinae.C	(revision 237582)
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-sfinae.C	(working copy)
@@ -90,22 +90,28 @@ namespace NullPointerArithmetic {
 constexpr int i = 0;
 constexpr const int* a[] = { 0, &i };
 
-// Well-defined core constant expressoons involving null pointers.
+// Well-defined core constant expressions involving null pointers.
 constexpr __PTRDIFF_TYPE__ d00 = a [0] - a [0];
 constexpr __PTRDIFF_TYPE__ d11 = a [1] - a [1];
 
-// Undefined core constant expressoons involving null pointers.
+// Undefined core constant expressions involving null pointers.
 // constexpr __PTRDIFF_TYPE__ d01 = a [0] - a [1];
 // constexpr __PTRDIFF_TYPE__ d10 = a [1] - a [0];
 
-constexpr bool nullptr_sub_0 (int i, int j) { return 1 + a [i != 0] - a [j]; }
+// Valid when i == j.
+constexpr bool
+nullptr_sub_0 (bool i, bool j) { return 1 + a [!i] - a [!j]; }
 
-constexpr bool nullptr_sub_1 (int i, int j) { return 1 + a [i == 0] - a [j]; }
+// Valid when i != j.
+constexpr bool
+nullptr_sub_1 (bool i, bool j) { return 1 + a [i] - a [!j]; }
 
-template <int I>
+// Selected when I == 0.
+template <bool I>
 constexpr int f (int (*)[nullptr_sub_0 (I, 0)] = 0) { return 0; }
 
-template <int I>
+// Selected when I != 0.
+template <bool I>
 constexpr int f (int (*)[nullptr_sub_1 (I, 0)] = 0) { return 1; }
 
 constexpr int n0 = f<0>();
Index: gcc/testsuite/g++.dg/ubsan/pr63956.C
===================================================================
--- gcc/testsuite/g++.dg/ubsan/pr63956.C	(revision 237582)
+++ gcc/testsuite/g++.dg/ubsan/pr63956.C	(working copy)
@@ -92,7 +92,7 @@ constexpr int
 fn6 (const int &a, int b)
 {
   if (b != 2)
-    b = a;  // { dg-error "is not a constant expression" }
+    b = a;
   return 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);
+constexpr int n3 = fn7 ((const int *) 0, 8);  // { dg-error "null pointer" }
 
 constexpr int
 fn8 (int i)

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

* Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
  2016-06-20 19:17           ` Martin Sebor
@ 2016-06-23 21:36             ` Jason Merrill
  2016-07-06 22:20               ` Martin Sebor
  0 siblings, 1 reply; 18+ messages in thread
From: Jason Merrill @ 2016-06-23 21:36 UTC (permalink / raw)
  To: Martin Sebor, Gcc Patch List

On 06/20/2016 10:17 PM, Martin Sebor wrote:
> +      && tree_int_cst_equal (lhs, null_pointer_node)
> +      && !tree_int_cst_equal (rhs, integer_zero_node))

Not integer_zerop?

> +			    "invalid conversion involving a null pointer");
...
> +			    "invalid conversion from %qT to %qT",

The conversion isn't invalid, it just isn't a constant expression.  For 
the null pointer to pointer conversion, does this properly allow 
conversion to void* or to base*?

> +	    if (integer_zerop (op))
...
> +         else if (!integer_zerop (op))

The second test seems redundant.

Jason

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

* Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
  2016-06-23 21:36             ` Jason Merrill
@ 2016-07-06 22:20               ` Martin Sebor
  2016-07-14 15:04                 ` Martin Sebor
                                   ` (2 more replies)
  0 siblings, 3 replies; 18+ messages in thread
From: Martin Sebor @ 2016-07-06 22:20 UTC (permalink / raw)
  To: Jason Merrill, Gcc Patch List

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

On 06/23/2016 03:36 PM, Jason Merrill wrote:
> On 06/20/2016 10:17 PM, Martin Sebor wrote:
>> +      && tree_int_cst_equal (lhs, null_pointer_node)
>> +      && !tree_int_cst_equal (rhs, integer_zero_node))
>
> Not integer_zerop?
>
>> +                "invalid conversion involving a null pointer");
> ...
>> +                "invalid conversion from %qT to %qT",
>
> The conversion isn't invalid, it just isn't a constant expression.

(Sorry for the delay following up on this review.  I got busy
with something else.)

I've adjusted the text of the diagnostics, though the first one
is also issued for conversions that are invalid even outside
constexpr, such as those that cast away constness, or those that
cast to incomplete type.  Without -fpermissve those are already
diagnosed by this point but I'm not sure how much trouble to go
to here to avoid diagnosing them again, or at all with
-fpermissve.

> For
> the null pointer to pointer conversion, does this properly allow
> conversion to void* or to base*?

It didn't handle either but does now.  Thank you for calling it
out.  Surprisingly, a regression run including libstdc++ didn't
catch it.  I've added tests to exercise it.

>
>> +        if (integer_zerop (op))
> ...
>> +         else if (!integer_zerop (op))
>
> The second test seems redundant.

I have removed it.

Martin

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

PR c++/60760 - arithmetic on null pointers should not be allowed in constant
  expressions
PR c++/71091 - constexpr reference bound to a null pointer dereference
   accepted

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

        PR c++/60760
        PR c++/71091
        * constexpr.c (cxx_eval_binary_expression): Reject invalid expressions
        involving null pointers.
        (cxx_eval_component_reference): Reject null pointer dereferences.
        (cxx_eval_indirect_ref): Reject indirecting through null pointers.
        (cxx_eval_constant_expression): Reject invalid expressions involving
        null pointers.

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

        PR c++/60760
        PR c++/71091
	* g++.dg/cpp0x/constexpr-cast.C: New test.
        * g++.dg/cpp0x/constexpr-nullptr-2.C: New test.
        * g++.dg/cpp1y/constexpr-sfinae.C: Correct.
        * g++.dg/ubsan/pr63956.C: Correct.

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index ba40435..83954d8 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -1811,6 +1811,13 @@ 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
+      && integer_zerop (lhs) && !integer_zerop (rhs))
+    {
+      if (!ctx->quiet)
+        error ("arithmetic involving a null pointer in %qE", lhs);
+      return t;
+    }
 
   if (r == NULL_TREE)
     r = fold_binary_loc (loc, code, type, lhs, rhs);
@@ -2151,6 +2158,11 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
   tree whole = cxx_eval_constant_expression (ctx, orig_whole,
 					     lval,
 					     non_constant_p, overflow_p);
+  if (TREE_CODE (whole) == INDIRECT_REF
+      && integer_zerop (TREE_OPERAND (whole, 0))
+      && !ctx->quiet)
+    error ("dereferencing a null pointer in %qE", orig_whole);
+
   if (TREE_CODE (whole) == PTRMEM_CST)
     whole = cplus_expand_constant (whole);
   if (whole == orig_whole)
@@ -2911,6 +2923,14 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
       if (*non_constant_p)
 	return t;
 
+      if (integer_zerop (op0))
+	{
+	  if (!ctx->quiet)
+	    error ("dereferencing a null pointer");
+	  *non_constant_p = true;
+	  return t;
+	}
+
       r = cxx_fold_indirect_ref (EXPR_LOCATION (t), TREE_TYPE (t), op0,
 				 &empty_base);
       if (r == NULL_TREE)
@@ -3559,10 +3579,22 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	  if (!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 ("value %qE of type %qT is not a constant expression",
+		   t, TREE_TYPE (t));
+	  *non_constant_p = true;
+        }
+
       return t;
     }
 
-  switch (TREE_CODE (t))
+  tree_code tcode = TREE_CODE (t);
+  switch (tcode)
     {
     case RESULT_DECL:
       if (lval)
@@ -3973,7 +4005,6 @@ 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,
@@ -3999,15 +4030,48 @@ 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)
+          {
+	    if (integer_zerop (op))
+	      {
+		if (TREE_CODE (type) == REFERENCE_TYPE)
+		  {
+		    if (!ctx->quiet)
+		      error_at (EXPR_LOC_OR_LOC (t, input_location),
+				"dereferencing a null pointer");
+		    *non_constant_p = true;
+		    return t;
+		  }
+		else if (TREE_CODE (TREE_TYPE (op)) == POINTER_TYPE)
+		  {
+		    tree from = TREE_TYPE (op);
+
+		    if (!can_convert (type, from, tf_none))
+		      {
+			if (!ctx->quiet)
+			  error_at (EXPR_LOC_OR_LOC (t, input_location),
+				    "conversion of %qT null pointer to %qT "
+				    "is not a constant expression",
+				    from, type);
+			*non_constant_p = true;
+			return t;
+		      }
+		  }
+	      }
+            else
+	      {
+		/* This detects for example:
+		     reinterpret_cast<void*>(sizeof 0)
+		*/
+		if (!ctx->quiet)
+		  error_at (EXPR_LOC_OR_LOC (t, input_location),
+			    "%<reinterpret_cast<%T>(%E)%> is not "
+			    "a constant-expression",
+			    type, op);
+		*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
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C
new file mode 100644
index 0000000..8e11193
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C
@@ -0,0 +1,24 @@
+// Test to verify that evaluating reinterpret_cast is diagnosed in
+// constant expressions.
+// { dg-do compile { target c++11 } }
+
+int i;
+
+// The following is accepted due to bug 49171.
+constexpr void *q = reinterpret_cast<void*>(&i);    // { dg-error "" "bug c++/49171" { xfail *-*-*-* } }
+
+constexpr void *r0 = reinterpret_cast<void*>(1);    // { dg-error "not a constant expression" }
+constexpr void *r1 = reinterpret_cast<void*>(sizeof 'x');  // { dg-error ".reinterpret_cast<void\\*>\\(1ul\\). is not a constant-expression" }
+
+template <class T>
+constexpr bool f ()
+{
+#if __cplusplus > 201103L
+  T *p = reinterpret_cast<T*>(sizeof (T));
+  return p;
+#else
+  return *reinterpret_cast<T*>(sizeof (T));
+#endif
+}
+
+constexpr bool b = f<int>();   // { dg-error "not a constant expression" }
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-1.C
index 420a04b..fbf1362 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-1.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-1.C
@@ -6,7 +6,7 @@
 // c++/67376 on gcc-patches for additional background.
 
 // { dg-do compile { target c++11 } }
-// { dg-options "-fdump-tree-optimized" }
+// { dg-options "-fdump-tree-optimized -ftrack-macro-expansion=0" }
 
 // Runtime assert.  Used for potentially invalid expressions.
 #define RA(e)  ((e) ? (void)0 : __builtin_abort ())
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-2.C
new file mode 100644
index 0000000..4777b53
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-2.C
@@ -0,0 +1,303 @@
+// PR c++/60760 - arithmetic on null pointers should not be allowed
+//     in constant expressions
+// PR c++/71091 - constexpr reference bound to a null pointer dereference
+//     accepted
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "-Wno-pointer-arith" }
+
+// Generate a null poiinter.
+constexpr int* null () { return 0; }
+
+// Test case from comment #0 in c++/60760.
+namespace PR_60760_comment_0 {
+
+constexpr int* ptr = nullptr;
+constexpr int* ptr2 = ptr + 1;   // { dg-error "null pointer|not a constant" }
+
+}
+
+// Test case from comment #1 in c++/60760.
+namespace PR_60760_comment_1 {
+
+constexpr int* ptr = nullptr;
+
+constexpr int zero = 0;
+constexpr int* ptr2 = ptr + zero;   // Adding zero is valid.
+constexpr int* ptr3 = ptr - zero;   // As is subtracting zero.
+
+}
+
+// Test case from c++/71091.
+namespace PR_71091 {
+
+constexpr int *p = 0;
+constexpr const int &r = *p;   // { dg-error "dereferencing a null pointer" }
+
+}
+
+// Other test cases.
+namespace C {
+
+struct S { int a, b[1]; } s;
+
+constexpr S *p0 = &s;
+constexpr S *p1 = nullptr;
+constexpr int *r0 = p1->b;   // { dg-error "null pointer|constant expression" }
+
+// Adding and subtracting zero from and to a null pointer is valid.
+constexpr S* r1 = p1 + 0;
+constexpr S* r2 = r1 - 0;
+
+constexpr int zero = 0;
+
+constexpr S* r3 = r2 + zero;
+constexpr S* r4 = r3 - zero;
+
+static_assert (r4 == nullptr, "r4 == nullptr");
+
+constexpr const S *pcs = p0;
+constexpr int d1 = pcs - p0;
+constexpr int d2 = p0 - pcs;
+
+constexpr bool b = !p1 && !pcs;
+}
+
+namespace D {
+
+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 SimpleTests {
+
+constexpr int* p0 = nullptr;
+constexpr int* q0 = p0;
+constexpr int* r0 = null ();
+
+// Conversion to cv-qualified void* is valid.
+constexpr void* pv0 = p0;
+constexpr const void* pv1 = p0;
+constexpr volatile void* pv2 = p0;
+constexpr const volatile void* pv3 = p0;
+constexpr void* pv4 = static_cast<void*>(p0);
+constexpr const void* pv5 = static_cast<const void*>(p0);
+
+// The following should be rejected but isn't because of bug c++/49171
+// - [C++0x][constexpr] Constant expressions support reinterpret_cast
+constexpr void* pv6 = reinterpret_cast<void*>(p0);   // { dg-error "" "bug c++/49171" { xfail *-*-* } }
+
+// 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 IncompleteTypeTests {
+
+// The type must be complete.
+struct X;
+constexpr X *px0 = nullptr;
+constexpr X *px1 = px0 + 0;     // { dg-error "invalid use of incomplete type"  }
+constexpr X *px2 = px0 - 0;     // { dg-error "invalid use of incomplete type"  }
+constexpr X *px3 = px0 - px0;   // { dg-error "invalid use of incomplete type"  }
+
+constexpr void *pv0 = px0;
+constexpr void *pv1 = pv0;
+constexpr const void *pv2 = pv0;
+constexpr void *pv3 = pv2;      // { dg-error "invalid conversion|not a constant expression" }
+constexpr const void *pv4 = pv2;
+
+constexpr X *px4 = pv0;         // { dg-error "invalid conversion|not a constant expression" }
+
+}
+
+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 "not a constant expression" }
+constexpr int* pj1 = &((S*)nullptr)->j;  // { dg-error "not a constant expression" }
+
+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 BaseAndDerivedTests {
+
+struct A { };
+struct B: A { };
+struct C: B { };
+struct D: B, C { };                     // { dg-warning "inaccessible" }
+
+constexpr D *pd0 = 0;
+constexpr C *pc0 = 0;
+constexpr B *pb0 = 0;
+
+constexpr A *pa0 = pb0;
+constexpr A *pa1 = static_cast<A*>(pb0);
+constexpr A *pa2 = pc0;
+constexpr A *pa3 = pd0;                   // { dg-error "ambiguous base" }
+constexpr A *pa4 = static_cast<A*>(pd0);  // { dg-error "ambiguous base" }
+
+constexpr B *pb1 = pa0;                   // { dg-error "invalid conversion|not a constant expression" }
+constexpr B *pb2 = static_cast<B*>(pa0);  // { dg-error "not a constant expression" }
+
+constexpr C *pc1 = pa0;                   // { dg-error "invalid conversion|not a constant expression" }
+constexpr D *pd1 = pa0;                   // { dg-error "ambiguous base|invalid conversion" }
+
+struct E: private A { };
+
+constexpr E *pe0 = 0;
+constexpr A *pa5 = pe0;                 // { dg-error "inaccessible base of" }
+
+struct VA { virtual ~VA (); };
+struct VB: virtual VA { };
+struct VC: virtual VA { };
+struct VD: VB, VC { };
+
+constexpr VD *pvd0 = 0;
+constexpr VC *pvc0 = 0;
+constexpr VB *pvb0 = 0;
+
+constexpr VA *pva0 = pvb0;
+constexpr VA *pva1 = pvc0;
+constexpr VA *pva2 = pvd0;
+
+constexpr VB *pvb1 = pva0;              // { dg-error "invalid conversion|cannot convert from pointer to base class" }
+
+}
+
+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" }
+
+}
+
+namespace ConversionTest {
+
+struct A {
+  int *p;
+};
+
+constexpr const int* f (const int *p) { return p; }
+
+void f ()
+{
+  static_assert (!f (0), "f (a.p)");
+  static_assert (!f (nullptr), "f (a.p)");
+
+  constexpr A a = A ();
+
+  static_assert (!f (a.p), "f (a.p)");
+}
+
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-sfinae.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-sfinae.C
index a83d7f4..4a7deb8 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-sfinae.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-sfinae.C
@@ -90,22 +90,28 @@ namespace NullPointerArithmetic {
 constexpr int i = 0;
 constexpr const int* a[] = { 0, &i };
 
-// Well-defined core constant expressoons involving null pointers.
+// Well-defined core constant expressions involving null pointers.
 constexpr __PTRDIFF_TYPE__ d00 = a [0] - a [0];
 constexpr __PTRDIFF_TYPE__ d11 = a [1] - a [1];
 
-// Undefined core constant expressoons involving null pointers.
+// Undefined core constant expressions involving null pointers.
 // constexpr __PTRDIFF_TYPE__ d01 = a [0] - a [1];
 // constexpr __PTRDIFF_TYPE__ d10 = a [1] - a [0];
 
-constexpr bool nullptr_sub_0 (int i, int j) { return 1 + a [i != 0] - a [j]; }
+// Valid when i == j.
+constexpr bool
+nullptr_sub_0 (bool i, bool j) { return 1 + a [!i] - a [!j]; }
 
-constexpr bool nullptr_sub_1 (int i, int j) { return 1 + a [i == 0] - a [j]; }
+// Valid when i != j.
+constexpr bool
+nullptr_sub_1 (bool i, bool j) { return 1 + a [i] - a [!j]; }
 
-template <int I>
+// Selected when I == 0.
+template <bool I>
 constexpr int f (int (*)[nullptr_sub_0 (I, 0)] = 0) { return 0; }
 
-template <int I>
+// Selected when I != 0.
+template <bool I>
 constexpr int f (int (*)[nullptr_sub_1 (I, 0)] = 0) { return 1; }
 
 constexpr int n0 = f<0>();
diff --git a/gcc/testsuite/g++.dg/ubsan/pr63956.C b/gcc/testsuite/g++.dg/ubsan/pr63956.C
index 25db8a4..ac01fa4 100644
--- a/gcc/testsuite/g++.dg/ubsan/pr63956.C
+++ b/gcc/testsuite/g++.dg/ubsan/pr63956.C
@@ -92,7 +92,7 @@ constexpr int
 fn6 (const int &a, int b)
 {
   if (b != 2)
-    b = a;  // { dg-error "is not a constant expression" }
+    b = a;
   return 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);
+constexpr int n3 = fn7 ((const int *) 0, 8);  // { dg-error "null pointer" }
 
 constexpr int
 fn8 (int i)

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

* Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
  2016-07-06 22:20               ` Martin Sebor
@ 2016-07-14 15:04                 ` Martin Sebor
  2016-07-18 17:51                 ` Jason Merrill
  2016-08-02  6:34                 ` Thomas Schwinge
  2 siblings, 0 replies; 18+ messages in thread
From: Martin Sebor @ 2016-07-14 15:04 UTC (permalink / raw)
  To: Jason Merrill, Gcc Patch List

Ping.  Jason, do you have any further comments or concerns with
the updated patch?

https://gcc.gnu.org/ml/gcc-patches/2016-07/msg00280.html

Thanks
Martin

On 07/06/2016 04:20 PM, Martin Sebor wrote:
> On 06/23/2016 03:36 PM, Jason Merrill wrote:
>> On 06/20/2016 10:17 PM, Martin Sebor wrote:
>>> +      && tree_int_cst_equal (lhs, null_pointer_node)
>>> +      && !tree_int_cst_equal (rhs, integer_zero_node))
>>
>> Not integer_zerop?
>>
>>> +                "invalid conversion involving a null pointer");
>> ...
>>> +                "invalid conversion from %qT to %qT",
>>
>> The conversion isn't invalid, it just isn't a constant expression.
>
> (Sorry for the delay following up on this review.  I got busy
> with something else.)
>
> I've adjusted the text of the diagnostics, though the first one
> is also issued for conversions that are invalid even outside
> constexpr, such as those that cast away constness, or those that
> cast to incomplete type.  Without -fpermissve those are already
> diagnosed by this point but I'm not sure how much trouble to go
> to here to avoid diagnosing them again, or at all with
> -fpermissve.
>
>> For
>> the null pointer to pointer conversion, does this properly allow
>> conversion to void* or to base*?
>
> It didn't handle either but does now.  Thank you for calling it
> out.  Surprisingly, a regression run including libstdc++ didn't
> catch it.  I've added tests to exercise it.
>
>>
>>> +        if (integer_zerop (op))
>> ...
>>> +         else if (!integer_zerop (op))
>>
>> The second test seems redundant.
>
> I have removed it.
>
> Martin

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

* Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
  2016-07-06 22:20               ` Martin Sebor
  2016-07-14 15:04                 ` Martin Sebor
@ 2016-07-18 17:51                 ` Jason Merrill
  2016-07-18 22:15                   ` Martin Sebor
  2016-08-02  6:34                 ` Thomas Schwinge
  2 siblings, 1 reply; 18+ messages in thread
From: Jason Merrill @ 2016-07-18 17:51 UTC (permalink / raw)
  To: Martin Sebor, Gcc Patch List

On 07/06/2016 06:20 PM, Martin Sebor wrote:
> @@ -2911,6 +2923,14 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,

>        if (*non_constant_p)

>  	return t;

>

> +      if (integer_zerop (op0))

> +	{

> +	  if (!ctx->quiet)

> +	    error ("dereferencing a null pointer");

> +	  *non_constant_p = true;

> +	  return t;

> +	}

I'm skeptical of checking this here, since *p is valid for null p; &*p 
is even a constant expression.  And removing this hunk doesn't seem to 
break any of your tests.

OK with that hunk removed.

Jason

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

* Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
  2016-07-18 17:51                 ` Jason Merrill
@ 2016-07-18 22:15                   ` Martin Sebor
  2016-07-20 13:52                     ` Jason Merrill
  0 siblings, 1 reply; 18+ messages in thread
From: Martin Sebor @ 2016-07-18 22:15 UTC (permalink / raw)
  To: Jason Merrill, Gcc Patch List

On 07/18/2016 11:51 AM, Jason Merrill wrote:
> On 07/06/2016 06:20 PM, Martin Sebor wrote:
>> @@ -2911,6 +2923,14 @@ cxx_eval_indirect_ref (const constexpr_ctx
>> *ctx, tree t,
>
>>        if (*non_constant_p)
>
>>      return t;
>
>>
>
>> +      if (integer_zerop (op0))
>
>> +    {
>
>> +      if (!ctx->quiet)
>
>> +        error ("dereferencing a null pointer");
>
>> +      *non_constant_p = true;
>
>> +      return t;
>
>> +    }
>
> I'm skeptical of checking this here, since *p is valid for null p; &*p
> is even a constant expression.  And removing this hunk doesn't seem to
> break any of your tests.
>
> OK with that hunk removed.

With it removed the constexpr-nullptr-2.C test fails on line 64:

   constexpr const int *pi0 = &pa2->pa1->pa0->i;   // { dg-error "null 
pointer|not a constant" }

Here, pa2 and pa1 are non-null but pa0 is null.

Martin

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

* Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
  2016-07-18 22:15                   ` Martin Sebor
@ 2016-07-20 13:52                     ` Jason Merrill
  2016-07-20 18:15                       ` Martin Sebor
  0 siblings, 1 reply; 18+ messages in thread
From: Jason Merrill @ 2016-07-20 13:52 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Gcc Patch List

On Mon, Jul 18, 2016 at 6:15 PM, Martin Sebor <msebor@gmail.com> wrote:
> On 07/18/2016 11:51 AM, Jason Merrill wrote:
>>
>> On 07/06/2016 06:20 PM, Martin Sebor wrote:
>>>
>>> @@ -2911,6 +2923,14 @@ cxx_eval_indirect_ref (const constexpr_ctx
>>> *ctx, tree t,
>>>        if (*non_constant_p)
>>>      return t;
>>>
>>> +      if (integer_zerop (op0))
>>> +    {
>>> +      if (!ctx->quiet)
>>> +        error ("dereferencing a null pointer");
>>> +      *non_constant_p = true;
>>> +      return t;
>>> +    }
>>
>> I'm skeptical of checking this here, since *p is valid for null p; &*p
>> is even a constant expression.  And removing this hunk doesn't seem to
>> break any of your tests.
>>
>> OK with that hunk removed.
>
> With it removed the constexpr-nullptr-2.C test fails on line 64:
>
>   constexpr const int *pi0 = &pa2->pa1->pa0->i;   // { dg-error "null
> pointer|not a constant" }
>
> Here, pa2 and pa1 are non-null but pa0 is null.

It doesn't fail for me; that line hits the error in
cxx_eval_component_reference.  I'm only talking about removing the
cxx_eval_indirect_ref hunk.

Jason

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

* Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
  2016-07-20 13:52                     ` Jason Merrill
@ 2016-07-20 18:15                       ` Martin Sebor
  2016-07-20 18:48                         ` Jason Merrill
  0 siblings, 1 reply; 18+ messages in thread
From: Martin Sebor @ 2016-07-20 18:15 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Gcc Patch List

On 07/20/2016 07:52 AM, Jason Merrill wrote:
> On Mon, Jul 18, 2016 at 6:15 PM, Martin Sebor <msebor@gmail.com> wrote:
>> On 07/18/2016 11:51 AM, Jason Merrill wrote:
>>>
>>> On 07/06/2016 06:20 PM, Martin Sebor wrote:
>>>>
>>>> @@ -2911,6 +2923,14 @@ cxx_eval_indirect_ref (const constexpr_ctx
>>>> *ctx, tree t,
>>>>         if (*non_constant_p)
>>>>       return t;
>>>>
>>>> +      if (integer_zerop (op0))
>>>> +    {
>>>> +      if (!ctx->quiet)
>>>> +        error ("dereferencing a null pointer");
>>>> +      *non_constant_p = true;
>>>> +      return t;
>>>> +    }
>>>
>>> I'm skeptical of checking this here, since *p is valid for null p; &*p
>>> is even a constant expression.  And removing this hunk doesn't seem to
>>> break any of your tests.
>>>
>>> OK with that hunk removed.
>>
>> With it removed the constexpr-nullptr-2.C test fails on line 64:
>>
>>    constexpr const int *pi0 = &pa2->pa1->pa0->i;   // { dg-error "null
>> pointer|not a constant" }
>>
>> Here, pa2 and pa1 are non-null but pa0 is null.
>
> It doesn't fail for me; that line hits the error in
> cxx_eval_component_reference.  I'm only talking about removing the
> cxx_eval_indirect_ref hunk.

Sorry, I may have been referring to an older patch.  With the latest
patch, the assertion is on line 75.  It's also not failing, even
though it should be.  The problem is that I had misunderstood how
the vertical bar in DejaGnu directives works.  I thought it meant
that both sides had to match a message on that line, when it means
only one side has to.  I'll need to fix that (how does one match
two messages on the same line?)

But removing the hunk as you suggest does break the intent of the
test.  With it there, we get a descriptive message for the invalid
code below clearly explaining the problem:

$ cat xyz.c && /build/gcc-60760/gcc/xgcc -B /build/gcc-60760/gcc -S 
-Wall -Wextra -Wpedantic -xc++ xyz.c
struct S { const S *p; int i; };

constexpr S s0 = { 0, 0 };
constexpr S s1 = { &s0, 1 };

constexpr int i = s1.p->p->i;
xyz.c:6:28: error: dereferencing a null pointer
  constexpr int i = s1.p->p->i;
                             ^

With the hunk removed, all we get is the generic:

xyz.c:6:28: error: ‘*(const S*)((const S*)s1.S::p)->S::p’ is not a 
constant expression
  constexpr int i = s1.p->p->i;
                             ^

Re-reading your comment above now: "since *p is valid for null p;"
I agree that &*p is valid when p is null.  Unless I missed a case
it is accepted with or without the hunk.  Otherwise, *p is not valid,
and it is also rejected with or without it.

Is there something else you're worried about with the hunk that
makes you want to trade it off for the less informative message?

Martin

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

* Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
  2016-07-20 18:15                       ` Martin Sebor
@ 2016-07-20 18:48                         ` Jason Merrill
  0 siblings, 0 replies; 18+ messages in thread
From: Jason Merrill @ 2016-07-20 18:48 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Gcc Patch List

On Wed, Jul 20, 2016 at 2:15 PM, Martin Sebor <msebor@gmail.com> wrote:
> On 07/20/2016 07:52 AM, Jason Merrill wrote:
>>
>> On Mon, Jul 18, 2016 at 6:15 PM, Martin Sebor <msebor@gmail.com> wrote:
>>>
>>> On 07/18/2016 11:51 AM, Jason Merrill wrote:
>>>>
>>>>
>>>> On 07/06/2016 06:20 PM, Martin Sebor wrote:
>>>>>
>>>>>
>>>>> @@ -2911,6 +2923,14 @@ cxx_eval_indirect_ref (const constexpr_ctx
>>>>> *ctx, tree t,
>>>>>         if (*non_constant_p)
>>>>>       return t;
>>>>>
>>>>> +      if (integer_zerop (op0))
>>>>> +    {
>>>>> +      if (!ctx->quiet)
>>>>> +        error ("dereferencing a null pointer");
>>>>> +      *non_constant_p = true;
>>>>> +      return t;
>>>>> +    }
>>>>
>>>>
>>>> I'm skeptical of checking this here, since *p is valid for null p; &*p
>>>> is even a constant expression.  And removing this hunk doesn't seem to
>>>> break any of your tests.
>>>>
>>>> OK with that hunk removed.
>>>
>>>
>>> With it removed the constexpr-nullptr-2.C test fails on line 64:
>>>
>>>    constexpr const int *pi0 = &pa2->pa1->pa0->i;   // { dg-error "null
>>> pointer|not a constant" }
>>>
>>> Here, pa2 and pa1 are non-null but pa0 is null.
>>
>>
>> It doesn't fail for me; that line hits the error in
>> cxx_eval_component_reference.  I'm only talking about removing the
>> cxx_eval_indirect_ref hunk.
>
>
> Sorry, I may have been referring to an older patch.  With the latest
> patch, the assertion is on line 75.  It's also not failing, even
> though it should be.  The problem is that I had misunderstood how
> the vertical bar in DejaGnu directives works.  I thought it meant
> that both sides had to match a message on that line, when it means
> only one side has to.  I'll need to fix that (how does one match
> two messages on the same line?)
>
> But removing the hunk as you suggest does break the intent of the
> test.  With it there, we get a descriptive message for the invalid
> code below clearly explaining the problem:
>
> $ cat xyz.c && /build/gcc-60760/gcc/xgcc -B /build/gcc-60760/gcc -S -Wall
> -Wextra -Wpedantic -xc++ xyz.c
> struct S { const S *p; int i; };
>
> constexpr S s0 = { 0, 0 };
> constexpr S s1 = { &s0, 1 };
>
> constexpr int i = s1.p->p->i;
> xyz.c:6:28: error: dereferencing a null pointer
>  constexpr int i = s1.p->p->i;
>                             ^
>
> With the hunk removed, all we get is the generic:
>
> xyz.c:6:28: error: ‘*(const S*)((const S*)s1.S::p)->S::p’ is not a constant
> expression
>  constexpr int i = s1.p->p->i;
>                             ^
>
> Re-reading your comment above now: "since *p is valid for null p;"
> I agree that &*p is valid when p is null.  Unless I missed a case
> it is accepted with or without the hunk.  Otherwise, *p is not valid,
> and it is also rejected with or without it.
>
> Is there something else you're worried about with the hunk that
> makes you want to trade it off for the less informative message?

OK, we can keep the hunk, but only when !lval, since that means we
access the value.

Jason

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

* Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
  2016-07-06 22:20               ` Martin Sebor
  2016-07-14 15:04                 ` Martin Sebor
  2016-07-18 17:51                 ` Jason Merrill
@ 2016-08-02  6:34                 ` Thomas Schwinge
  2016-08-02 18:15                   ` Martin Sebor
  2 siblings, 1 reply; 18+ messages in thread
From: Thomas Schwinge @ 2016-08-02  6:34 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Jason Merrill, Gcc Patch List

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

Hi!

On Wed, 6 Jul 2016 16:20:44 -0600, Martin Sebor <msebor@gmail.com> wrote:
> PR c++/60760 - arithmetic on null pointers should not be allowed in constant
>   expressions
> PR c++/71091 - constexpr reference bound to a null pointer dereference
>    accepted
> 
> [...]
> 	* g++.dg/cpp0x/constexpr-cast.C: New test.

In x86_64 GNU/Linux testing, I see that one FAIL for the -m32 multilib:

    +FAIL: g++.dg/cpp0x/constexpr-cast.C  -std=c++11  (test for errors, line 10)
    +FAIL: g++.dg/cpp0x/constexpr-cast.C  -std=c++11  (test for errors, line 11)
    +PASS: g++.dg/cpp0x/constexpr-cast.C  -std=c++11  (test for errors, line 24)
    +FAIL: g++.dg/cpp0x/constexpr-cast.C  -std=c++11 (test for excess errors)
    +XFAIL: g++.dg/cpp0x/constexpr-cast.C  -std=c++11 bug c++/49171 (test for errors, line 8)
    +FAIL: g++.dg/cpp0x/constexpr-cast.C  -std=c++14  (test for errors, line 10)
    +FAIL: g++.dg/cpp0x/constexpr-cast.C  -std=c++14  (test for errors, line 11)
    +PASS: g++.dg/cpp0x/constexpr-cast.C  -std=c++14  (test for errors, line 24)
    +FAIL: g++.dg/cpp0x/constexpr-cast.C  -std=c++14 (test for excess errors)
    +XFAIL: g++.dg/cpp0x/constexpr-cast.C  -std=c++14 bug c++/49171 (test for errors, line 8)
    +UNSUPPORTED: g++.dg/cpp0x/constexpr-cast.C  -std=c++98

    [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:10:22: error: 'reinterpret_cast<void*>(1)' is not a constant-expression
    [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:11:22: error: 'reinterpret_cast<void*>(1u)' is not a constant-expression
    [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:24:26:   in constexpr expansion of 'f<int>()'
    [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:24:27: error: value '4u' of type 'int*' is not a constant expression

For the -m64 multilib, it looks as follows (all PASSes):

    [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:10:47: error: value '1u' of type 'void*' is not a constant expression
    [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:11:22: error: 'reinterpret_cast<void*>(1ul)' is not a constant-expression
    [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:24:26:   in constexpr expansion of 'f<int>()'
    [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:24:27: error: value '4u' of type 'int*' is not a constant expression

For reference:

> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C
> @@ -0,0 +1,24 @@
> +// Test to verify that evaluating reinterpret_cast is diagnosed in
> +// constant expressions.
> +// { dg-do compile { target c++11 } }
> +
> +int i;
> +
> +// The following is accepted due to bug 49171.
> +constexpr void *q = reinterpret_cast<void*>(&i);    // { dg-error "" "bug c++/49171" { xfail *-*-*-* } }
> +
> +constexpr void *r0 = reinterpret_cast<void*>(1);    // { dg-error "not a constant expression" }
> +constexpr void *r1 = reinterpret_cast<void*>(sizeof 'x');  // { dg-error ".reinterpret_cast<void\\*>\\(1ul\\). is not a constant-expression" }
> +
> +template <class T>
> +constexpr bool f ()
> +{
> +#if __cplusplus > 201103L
> +  T *p = reinterpret_cast<T*>(sizeof (T));
> +  return p;
> +#else
> +  return *reinterpret_cast<T*>(sizeof (T));
> +#endif
> +}
> +
> +constexpr bool b = f<int>();   // { dg-error "not a constant expression" }


Grüße
 Thomas

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 472 bytes --]

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

* Re: [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions
  2016-08-02  6:34                 ` Thomas Schwinge
@ 2016-08-02 18:15                   ` Martin Sebor
  0 siblings, 0 replies; 18+ messages in thread
From: Martin Sebor @ 2016-08-02 18:15 UTC (permalink / raw)
  To: Thomas Schwinge; +Cc: Jason Merrill, Gcc Patch List

On 08/02/2016 12:34 AM, Thomas Schwinge wrote:
> Hi!
>
> On Wed, 6 Jul 2016 16:20:44 -0600, Martin Sebor <msebor@gmail.com> wrote:
>> PR c++/60760 - arithmetic on null pointers should not be allowed in constant
>>    expressions
>> PR c++/71091 - constexpr reference bound to a null pointer dereference
>>     accepted
>>
>> [...]
>> 	* g++.dg/cpp0x/constexpr-cast.C: New test.
>
> In x86_64 GNU/Linux testing, I see that one FAIL for the -m32 multilib:
>
>      +FAIL: g++.dg/cpp0x/constexpr-cast.C  -std=c++11  (test for errors, line 10)
>      +FAIL: g++.dg/cpp0x/constexpr-cast.C  -std=c++11  (test for errors, line 11)
>      +PASS: g++.dg/cpp0x/constexpr-cast.C  -std=c++11  (test for errors, line 24)
>      +FAIL: g++.dg/cpp0x/constexpr-cast.C  -std=c++11 (test for excess errors)
>      +XFAIL: g++.dg/cpp0x/constexpr-cast.C  -std=c++11 bug c++/49171 (test for errors, line 8)
>      +FAIL: g++.dg/cpp0x/constexpr-cast.C  -std=c++14  (test for errors, line 10)
>      +FAIL: g++.dg/cpp0x/constexpr-cast.C  -std=c++14  (test for errors, line 11)
>      +PASS: g++.dg/cpp0x/constexpr-cast.C  -std=c++14  (test for errors, line 24)
>      +FAIL: g++.dg/cpp0x/constexpr-cast.C  -std=c++14 (test for excess errors)
>      +XFAIL: g++.dg/cpp0x/constexpr-cast.C  -std=c++14 bug c++/49171 (test for errors, line 8)
>      +UNSUPPORTED: g++.dg/cpp0x/constexpr-cast.C  -std=c++98
>
>      [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:10:22: error: 'reinterpret_cast<void*>(1)' is not a constant-expression
>      [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:11:22: error: 'reinterpret_cast<void*>(1u)' is not a constant-expression
>      [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:24:26:   in constexpr expansion of 'f<int>()'
>      [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:24:27: error: value '4u' of type 'int*' is not a constant expression
>
> For the -m64 multilib, it looks as follows (all PASSes):
>
>      [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:10:47: error: value '1u' of type 'void*' is not a constant expression
>      [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:11:22: error: 'reinterpret_cast<void*>(1ul)' is not a constant-expression
>      [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:24:26:   in constexpr expansion of 'f<int>()'
>      [...]/source-gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C:24:27: error: value '4u' of type 'int*' is not a constant expression

Thanks for pointing it out and for all the detail! I managed to run
into at least two problems with this change: one in the test assuming
that (void*)1 will appear in GCC diagnostics as 1ul, and another in
GCC due to the inconsistent spelling of "constant expression."  Some
errors hyphenate the words, others don't, and depending on which one
triggers a test that assumes one or the other will fail. Let me submit
a patch for this and CC you on it.

Martin

>
> For reference:
>
>> --- /dev/null
>> +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C
>> @@ -0,0 +1,24 @@
>> +// Test to verify that evaluating reinterpret_cast is diagnosed in
>> +// constant expressions.
>> +// { dg-do compile { target c++11 } }
>> +
>> +int i;
>> +
>> +// The following is accepted due to bug 49171.
>> +constexpr void *q = reinterpret_cast<void*>(&i);    // { dg-error "" "bug c++/49171" { xfail *-*-*-* } }
>> +
>> +constexpr void *r0 = reinterpret_cast<void*>(1);    // { dg-error "not a constant expression" }
>> +constexpr void *r1 = reinterpret_cast<void*>(sizeof 'x');  // { dg-error ".reinterpret_cast<void\\*>\\(1ul\\). is not a constant-expression" }
>> +
>> +template <class T>
>> +constexpr bool f ()
>> +{
>> +#if __cplusplus > 201103L
>> +  T *p = reinterpret_cast<T*>(sizeof (T));
>> +  return p;
>> +#else
>> +  return *reinterpret_cast<T*>(sizeof (T));
>> +#endif
>> +}
>> +
>> +constexpr bool b = f<int>();   // { dg-error "not a constant expression" }
>
>
> Grüße
>   Thomas
>

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

end of thread, other threads:[~2016-08-02 18:15 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-05-12 22:34 [PATCH] c++/60760 - arithmetic on null pointers should not be allowed in constant expressions Martin Sebor
2016-05-17 19:44 ` Jason Merrill
     [not found]   ` <574E0A41.9070204@gmail.com>
2016-06-01 14:36     ` Jason Merrill
2016-06-01 18:44       ` Martin Sebor
2016-06-01 19:20         ` Jason Merrill
2016-06-02  2:50           ` Martin Sebor
2016-06-02 15:05             ` Jason Merrill
2016-06-20 19:17           ` Martin Sebor
2016-06-23 21:36             ` Jason Merrill
2016-07-06 22:20               ` Martin Sebor
2016-07-14 15:04                 ` Martin Sebor
2016-07-18 17:51                 ` Jason Merrill
2016-07-18 22:15                   ` Martin Sebor
2016-07-20 13:52                     ` Jason Merrill
2016-07-20 18:15                       ` Martin Sebor
2016-07-20 18:48                         ` Jason Merrill
2016-08-02  6:34                 ` Thomas Schwinge
2016-08-02 18:15                   ` Martin Sebor

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