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.
@@ -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);
new file mode 100644
@@ -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" }
+
+}
@@ -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)