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

Reply via email to