PR c++/67376 - [5/6 regression] Comparison with pointer to past-the-end of array fails inside constant expression PR c++/70170 - [6 regression] bogus not a constant expression error comparing pointer to array to null PR c++/70172 - incorrect reinterpret_cast from integer to pointer error on invalid constexpr initialization PR c++/60760 - arithmetic on null pointers should not be allowed in constant expressions PR c++/70228 - insufficient detail in diagnostics for a constexpr out of bounds array subscript gcc/testsuite/ChangeLog: 2016-03-17 Martin Sebor PR c++/67376 PR c++/70170 PR c++/70172 PR c++/60760 PR c++/70228 * g++.dg/cpp0x/constexpr-array-ptr10.C: New test. * g++.dg/cpp0x/constexpr-array-ptr9.C: New test. * g++.dg/cpp0x/constexpr-array5.C: Adjust text of expected diagnostic. * g++.dg/cpp0x/constexpr-nullptr.C: Add test cases. * g++.dg/cpp0x/constexpr-string.C: Same. * g++.dg/cpp0x/constexpr-wstring2.C: Same. * g++.dg/cpp0x/pr65398.C: Same. * g++.dg/ext/constexpr-vla1.C: Same. * g++.dg/ext/constexpr-vla2.C: Same. * g++.dg/ext/constexpr-vla3.C: Same. * g++.dg/ubsan/pr63956.C: Same. gcc/cp/ChangeLog: 2016-03-17 Martin Sebor PR c++/67376 PR c++/70170 PR c++/70172 PR c++/60760 PR c++/70228 * constexpr.c (cxx_eval_binary_expression): Add argument. (cxx_eval_component_reference): Same. (cxx_eval_constant_expression): Same. (cxx_eval_indirect_ref): Same. (cxx_eval_outermost_constant_expr): Same. (diag_array_subscript): New function. (cxx_eval_call_expression): Adjust. (cxx_eval_conditional_expression): Same. (cxx_eval_array_reference): Detect null pointers. (cxx_eval_statement_list): Adjust. gcc/ChangeLog: 2016-03-17 Martin Sebor PR c++/67376 * fold-const.c (maybe_nonzero_address): New function. (fold_comparison): Call it. Fold equality and relational expressions involving null pointers. (tree_single_nonzero_warnv_p): Call maybe_nonzero_address. Index: gcc/cp/constexpr.c =================================================================== --- gcc/cp/constexpr.c (revision 234306) +++ gcc/cp/constexpr.c (working copy) @@ -918,7 +918,8 @@ struct constexpr_ctx { static GTY (()) hash_table *constexpr_call_table; static tree cxx_eval_constant_expression (const constexpr_ctx *, tree, - bool, bool *, bool *, tree * = NULL); + bool, bool *, bool *, bool * = NULL, + tree * = NULL); /* Compute a hash value for a constexpr call representation. */ @@ -1390,7 +1391,7 @@ cxx_eval_call_expression (const constexp tree jump_target = NULL_TREE; cxx_eval_constant_expression (ctx, body, lval, non_constant_p, overflow_p, - &jump_target); + NULL, &jump_target); if (DECL_CONSTRUCTOR_P (fun)) /* This can be null for a subobject constructor call, in @@ -1607,20 +1608,21 @@ cxx_eval_unary_expression (const constex static tree cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t, bool /*lval*/, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + bool *nullptr_p) { tree r = NULL_TREE; tree orig_lhs = TREE_OPERAND (t, 0); tree orig_rhs = TREE_OPERAND (t, 1); tree lhs, rhs; lhs = cxx_eval_constant_expression (ctx, orig_lhs, /*lval*/false, - non_constant_p, overflow_p); + non_constant_p, overflow_p, nullptr_p); /* Don't VERIFY_CONSTANT here, it's unnecessary and will break pointer subtraction. */ if (*non_constant_p) return t; rhs = cxx_eval_constant_expression (ctx, orig_rhs, /*lval*/false, - non_constant_p, overflow_p); + non_constant_p, overflow_p, nullptr_p); if (*non_constant_p) return t; @@ -1642,6 +1644,15 @@ cxx_eval_binary_expression (const conste || null_member_pointer_value_p (rhs))) r = constant_boolean_node (!is_code_eq, type); } + if (code == POINTER_PLUS_EXPR && !*non_constant_p + && tree_int_cst_equal (lhs, null_pointer_node)) + { + if (!ctx->quiet) + error ("arithmetic involving null pointer %qE", lhs); + if (nullptr_p) + *nullptr_p = true; + return t; + } if (r == NULL_TREE) r = fold_binary_loc (loc, code, type, lhs, rhs); @@ -1682,11 +1693,11 @@ cxx_eval_conditional_expression (const c return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2), lval, non_constant_p, overflow_p, - jump_target); + NULL, jump_target); return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), lval, non_constant_p, overflow_p, - jump_target); + NULL, jump_target); } /* Returns less than, equal to, or greater than zero if KEY is found to be @@ -1815,6 +1826,30 @@ find_array_ctor_elt (tree ary, tree dind return -1; } +/* Under the control of CTX, issue a detailed diagnostic for + an out-of-bounds subscript INDEX into the expression ARRAY. */ + +static void +diag_array_subscript (const constexpr_ctx *ctx, tree array, tree index) +{ + if (!ctx->quiet) + { + tree arraytype = TREE_TYPE (array); + + /* Convert the unsigned array subscript to a signed integer to avoid + printing huge numbers for small negative values. */ + tree sidx = fold_convert (ssizetype, index); + if (DECL_P (array)) + { + error ("array subscript value %qE is outside the bounds " + "of array %qD of type %qT", sidx, array, arraytype); + inform (DECL_SOURCE_LOCATION (array), "declared here"); + } + else + error ("array subscript value %qE is outside the bounds " + "of array type %qT", sidx, arraytype); + } +} /* Subroutine of cxx_eval_constant_expression. Attempt to reduce a reference to an array slot. */ @@ -1839,11 +1874,26 @@ cxx_eval_array_reference (const constexp false, non_constant_p, overflow_p); VERIFY_CONSTANT (index); + + tree arytype = TREE_TYPE (ary); + tree nelts = array_type_nelts_top (arytype); + /* For VLAs, the number of elements won't be an integer constant. */ + nelts = cxx_eval_constant_expression (ctx, nelts, false, non_constant_p, + overflow_p); + VERIFY_CONSTANT (nelts); + + if (tree_int_cst_lt (nelts, index)) + { + diag_array_subscript (ctx, ary, index); + *non_constant_p = true; + return t; + } + if (lval && ary == oldary && index == oldidx) return t; else if (lval) return build4 (ARRAY_REF, TREE_TYPE (t), ary, index, NULL, NULL); - elem_type = TREE_TYPE (TREE_TYPE (ary)); + elem_type = TREE_TYPE (arytype); if (TREE_CODE (ary) == CONSTRUCTOR) len = CONSTRUCTOR_NELTS (ary); else if (TREE_CODE (ary) == STRING_CST) @@ -1863,21 +1913,14 @@ cxx_eval_array_reference (const constexp if (!tree_fits_shwi_p (index) || (i = tree_to_shwi (index)) < 0) { - if (!ctx->quiet) - error ("negative array subscript"); + diag_array_subscript (ctx, ary, index); *non_constant_p = true; return t; } - tree nelts = array_type_nelts_top (TREE_TYPE (ary)); - /* For VLAs, the number of elements won't be an integer constant. */ - nelts = cxx_eval_constant_expression (ctx, nelts, false, non_constant_p, - overflow_p); - VERIFY_CONSTANT (nelts); if (!tree_int_cst_lt (index, nelts)) { - if (!ctx->quiet) - error ("array subscript out of bound"); + diag_array_subscript (ctx, ary, index); *non_constant_p = true; return t; } @@ -1935,7 +1978,8 @@ cxx_eval_array_reference (const constexp static tree cxx_eval_component_reference (const constexpr_ctx *ctx, tree t, bool lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + bool *nullptr_p) { unsigned HOST_WIDE_INT i; tree field; @@ -1944,7 +1988,14 @@ cxx_eval_component_reference (const cons tree orig_whole = TREE_OPERAND (t, 0); tree whole = cxx_eval_constant_expression (ctx, orig_whole, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + nullptr_p); + if (nullptr_p && *nullptr_p) + { + if (!ctx->quiet) + error ("%qE dereferences a null pointer", orig_whole); + } + if (TREE_CODE (whole) == PTRMEM_CST) whole = cplus_expand_constant (whole); if (whole == orig_whole) @@ -2655,7 +2706,8 @@ cxx_fold_indirect_ref (location_t loc, t static tree cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t, bool lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + bool *nullptr_p) { tree orig_op0 = TREE_OPERAND (t, 0); bool empty_base = false; @@ -2680,6 +2732,9 @@ cxx_eval_indirect_ref (const constexpr_c tree op0 = cxx_eval_constant_expression (ctx, orig_op0, /*lval*/false, non_constant_p, overflow_p); + if (nullptr_p && tree_int_cst_equal (op0, null_pointer_node)) + *nullptr_p = true; + /* Don't VERIFY_CONSTANT here. */ if (*non_constant_p) return t; @@ -3149,7 +3204,7 @@ cxx_eval_statement_list (const constexpr } r = cxx_eval_constant_expression (ctx, stmt, false, non_constant_p, overflow_p, - jump_target); + NULL, jump_target); if (*non_constant_p) break; if (returns (jump_target) || breaks (jump_target)) @@ -3285,8 +3340,10 @@ cxx_eval_pointer_plus_expression (const static tree cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, bool lval, - bool *non_constant_p, bool *overflow_p, - tree *jump_target) + bool *non_constant_p, + bool *overflow_p, + bool *nullptr_p, + tree *jump_target) { constexpr_ctx new_ctx; tree r = t; @@ -3300,10 +3357,21 @@ cxx_eval_constant_expression (const cons { if (TREE_OVERFLOW (t) && (!flag_permissive || ctx->quiet)) *overflow_p = true; + + if (TREE_CODE (t) == INTEGER_CST + && TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE + && !integer_zerop (t)) + { + if (!ctx->quiet) + error ("null pointer arithmetic in %qE", t); + if (nullptr_p) + *nullptr_p = true; + } return t; } - switch (TREE_CODE (t)) + tree_code tcode = TREE_CODE (t); + switch (tcode) { case RESULT_DECL: if (lval) @@ -3392,7 +3460,8 @@ cxx_eval_constant_expression (const cons { init = cxx_eval_constant_expression (ctx, init, false, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + nullptr_p); /* Don't share a CONSTRUCTOR that might be changed. */ init = unshare_expr (init); ctx->values->put (r, init); @@ -3432,7 +3501,8 @@ cxx_eval_constant_expression (const cons initialization of a temporary. */ r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), false, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + nullptr_p); if (!*non_constant_p) /* Adjust the type of the result to the type of the temporary. */ r = adjust_temp_type (TREE_TYPE (t), r); @@ -3454,14 +3524,16 @@ cxx_eval_constant_expression (const cons case SCOPE_REF: r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + nullptr_p); break; case RETURN_EXPR: if (TREE_OPERAND (t, 0) != NULL_TREE) r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + nullptr_p); *jump_target = t; break; @@ -3472,7 +3544,8 @@ cxx_eval_constant_expression (const cons else { r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), false, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + nullptr_p); ctx->values->put (t, r); if (ctx->save_exprs) ctx->save_exprs->add (t); @@ -3489,18 +3562,18 @@ cxx_eval_constant_expression (const cons r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval, non_constant_p, overflow_p, - jump_target); + nullptr_p, jump_target); break; case TRY_FINALLY_EXPR: r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval, non_constant_p, overflow_p, - jump_target); + nullptr_p, jump_target); if (!*non_constant_p) /* Also evaluate the cleanup. */ cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), true, non_constant_p, overflow_p, - jump_target); + nullptr_p, jump_target); break; /* These differ from cxx_eval_unary_expression in that this doesn't @@ -3509,7 +3582,7 @@ cxx_eval_constant_expression (const cons case MEM_REF: case INDIRECT_REF: r = cxx_eval_indirect_ref (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, nullptr_p); break; case ADDR_EXPR: @@ -3517,7 +3590,8 @@ cxx_eval_constant_expression (const cons tree oldop = TREE_OPERAND (t, 0); tree op = cxx_eval_constant_expression (ctx, oldop, /*lval*/true, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + nullptr_p); /* Don't VERIFY_CONSTANT here. */ if (*non_constant_p) return t; @@ -3560,17 +3634,17 @@ cxx_eval_constant_expression (const cons || TREE_CODE (op1) == EMPTY_CLASS_EXPR) r = cxx_eval_constant_expression (ctx, op0, lval, non_constant_p, overflow_p, - jump_target); + nullptr_p, jump_target); else { /* Check that the LHS is constant and then discard it. */ cxx_eval_constant_expression (ctx, op0, true, non_constant_p, overflow_p, - jump_target); + nullptr_p, jump_target); op1 = TREE_OPERAND (t, 1); r = cxx_eval_constant_expression (ctx, op1, lval, non_constant_p, overflow_p, - jump_target); + nullptr_p, jump_target); } } break; @@ -3621,7 +3695,7 @@ cxx_eval_constant_expression (const cons case RANGE_EXPR: case COMPLEX_EXPR: r = cxx_eval_binary_expression (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, nullptr_p); break; /* fold can introduce non-IF versions of these; still treat them as @@ -3659,7 +3733,7 @@ cxx_eval_constant_expression (const cons return t; } r = cxx_eval_component_reference (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, nullptr_p); break; case BIT_FIELD_REF: @@ -3712,12 +3786,12 @@ cxx_eval_constant_expression (const cons case NOP_EXPR: case UNARY_PLUS_EXPR: { - enum tree_code tcode = TREE_CODE (t); tree oldop = TREE_OPERAND (t, 0); tree op = cxx_eval_constant_expression (ctx, oldop, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + nullptr_p); if (*non_constant_p) return t; tree type = TREE_TYPE (t); @@ -3738,15 +3812,32 @@ cxx_eval_constant_expression (const cons return t; } } - if (POINTER_TYPE_P (type) - && TREE_CODE (op) == INTEGER_CST - && !integer_zerop (op)) - { - if (!ctx->quiet) - error_at (EXPR_LOC_OR_LOC (t, input_location), - "reinterpret_cast from integer to pointer"); - *non_constant_p = true; - return t; + if (POINTER_TYPE_P (type) && TREE_CODE (op) == INTEGER_CST) + { + const char *msg = NULL; + if (integer_zerop (op)) + { + if (nullptr_p) + *nullptr_p = true; + + tree t1 = TREE_TYPE (type); + tree t2 = TREE_TYPE (op); + if (TREE_TYPE (t2)) + t2 = TREE_TYPE (t2); + if (!same_type_ignoring_top_level_qualifiers_p (t1, t2)) + msg = "invalid conversion involving a null pointer"; + } + else + msg = "reinterpret_cast from integer to pointer"; + + if (msg) + { + if (!ctx->quiet) + error_at (EXPR_LOC_OR_LOC (t, input_location), msg); + + *non_constant_p = true; + return t; + } } if (op == oldop && tcode != UNARY_PLUS_EXPR) /* We didn't fold at the top so we could check for ptr-int @@ -3779,7 +3870,7 @@ cxx_eval_constant_expression (const cons return cxx_eval_constant_expression (ctx, BIND_EXPR_BODY (t), lval, non_constant_p, overflow_p, - jump_target); + nullptr_p, jump_target); case PREINCREMENT_EXPR: case POSTINCREMENT_EXPR: @@ -3828,7 +3919,7 @@ cxx_eval_constant_expression (const cons tree ctor = lval ? ctx->object : ctx->ctor; return cxx_eval_constant_expression (ctx, ctor, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, nullptr_p); } break; @@ -3935,8 +4026,9 @@ cxx_eval_outermost_constant_expr (tree t r = TARGET_EXPR_INITIAL (r); } - r = cxx_eval_constant_expression (&ctx, r, - false, &non_constant_p, &overflow_p); + bool nullptr_p = false; + r = cxx_eval_constant_expression (&ctx, r, false, + &non_constant_p, &overflow_p, &nullptr_p); verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p); Index: gcc/fold-const.c =================================================================== --- gcc/fold-const.c (revision 234306) +++ gcc/fold-const.c (working copy) @@ -8339,6 +8339,20 @@ pointer_may_wrap_p (tree base, tree offs return total.to_uhwi () > (unsigned HOST_WIDE_INT) size; } +/* Return a positive integer when the symbol DECL is known to have + a nonzero address, zero when it's known not to (e.g., it's a weak + symbol), and a negative integer when the symbol is not yet in the + symbol table and so whether or not its address is zero is unknown. */ +static int +maybe_nonzero_address (tree decl) +{ + if (DECL_P (decl) && decl_in_symtab_p (decl)) + if (struct symtab_node *symbol = symtab_node::get_create (decl)) + return symbol->nonzero_address (); + + return -1; +} + /* Subroutine of fold_binary. This routine performs all of the transformations that are common to the equality/inequality operators (EQ_EXPR and NE_EXPR) and the ordering operators @@ -8639,6 +8653,38 @@ fold_comparison (location_t loc, enum tr base1 = build_fold_addr_expr_loc (loc, base1); return fold_build2_loc (loc, code, type, base0, base1); } + + /* Comparison between an ordinary (non-weak) symbol and a null + pointer can be eliminated since such sybols must have a non + null address. */ + else if (DECL_P (base0) + && maybe_nonzero_address (base0) > 0 + // && (!HAS_DECL_ASSEMBLER_NAME_P (base0) || !DECL_WEAK (base0)) + /* Avoid folding references to struct members at offset 0 to + prevent tests like '&ptr->firstmember == 0' from getting + eliminated. When ptr is null, although the -> expression + is strictly speaking invalid, GCC retains it as a matter + of QoI. See PR c/44555. */ + && (TREE_CODE (op0) != ADDR_EXPR + || TREE_CODE (TREE_OPERAND (op0, 0)) != COMPONENT_REF + || compare_tree_int (DECL_FIELD_OFFSET ((TREE_OPERAND + (TREE_OPERAND (op0, 0), 1))), 0)) + && TREE_CODE (arg1) == INTEGER_CST + && compare_tree_int (arg1, 0) == 0) + { + switch (code) + { + case GE_EXPR: + case EQ_EXPR: + case LE_EXPR: + return boolean_false_node; + case GT_EXPR: + case LT_EXPR: + case NE_EXPR: + return boolean_true_node; + default: gcc_unreachable (); + } + } } /* Transform comparisons of the form X +- C1 CMP Y +- C2 to @@ -13509,16 +13555,9 @@ tree_single_nonzero_warnv_p (tree t, boo /* For objects in symbol table check if we know they are non-zero. Don't do anything for variables and functions before symtab is built; it is quite possible that they will be declared weak later. */ - if (DECL_P (base) && decl_in_symtab_p (base)) - { - struct symtab_node *symbol; - - symbol = symtab_node::get_create (base); - if (symbol) - return symbol->nonzero_address (); - else - return false; - } + int nonzero_addr = maybe_nonzero_address (base); + if (nonzero_addr >= 0) + return nonzero_addr; /* Function local objects are never NULL. */ if (DECL_P (base) Index: gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr10.C =================================================================== --- gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr10.C (revision 0) +++ gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr10.C (working copy) @@ -0,0 +1,154 @@ +// PR c++/67376 - [5/6 regression] Comparison with pointer to past-the-end +// of array fails inside constant expression +// This test verifies the aspect of the bug raised in comment #10, +// specifically comparing pointers to null. The basic regression test +// is in g++.dg/cpp0x/constexpr-67376.C. +// Note also that while the description of the bug talks about pointers +// pointing past the end of arrays but the prolem is more general than +// that and involves all constexpr object pointers. + +// { dg-do compile { target c++11 } } +// { dg-additional-options "-Wall -Wextra" } + +namespace A { + +extern int i; + +constexpr int *p0 = &i; + +constexpr bool b0 = p0; // { dg-warning "address of .A::i." } +constexpr bool b1 = p0 == 0; // { dg-warning "address of .A::i." } +constexpr bool b2 = p0 != 0; // { dg-warning "address of .A::i." } +constexpr bool b3 = p0 < 0; // { dg-warning "ordered comparison" } +constexpr bool b4 = p0 <= 0; // { dg-warning "ordered comparison" } +constexpr bool b5 = p0 > 0; // { dg-warning "ordered comparison" } +constexpr bool b6 = p0 >= 0; // { dg-warning "ordered comparison" } + +constexpr bool b7 = !p0; // { dg-warning "address of .A::i." } +constexpr bool b8 = 0 == p0; // { dg-warning "address of .A::i." } +constexpr bool b9 = 0 != p0; // { dg-warning "address of .A::i." } +constexpr bool b10 = 0 < p0; // { dg-warning "ordered comparison" } +constexpr bool b11 = 0 <= p0; // { dg-warning "ordered comparison" } +constexpr bool b12 = 0 > p0; // { dg-warning "ordered comparison" } +constexpr bool b13 = 0 >= p0; // { dg-warning "ordered comparison" } + +} + +namespace B { + +// PR c++/70170 - [6 regression] bogus not a constant expression error +// comparing pointer to array to null +// PR c++/70172 - incorrect reinterpret_cast from integer to pointer +// error on invalid constexpr initialization + +struct S { int a, b[1]; } s; + +constexpr S *p0 = &s; +constexpr S *p1 = nullptr; + +constexpr int *q0 = p0->b; // { dg-bogus "reinterpret_cast from integer to pointer" } +constexpr int *r0 = p1->b; // { dg-error "null pointer|constant expression" } + +constexpr bool b0 = p0; // { dg-warning "address of .B::s." } +constexpr bool b1 = p0 == 0; // { dg-warning "address of .B::s." } +constexpr bool b2 = p0 != 0; // { dg-warning "address of .B::s." } +constexpr bool b3 = p0 < 0; // { dg-warning "ordered comparison" } +constexpr bool b4 = p0 <= 0; // { dg-warning "ordered comparison" } +constexpr bool b5 = p0 > 0; // { dg-warning "ordered comparison" } +constexpr bool b6 = p0 >= 0; // { dg-warning "ordered comparison" } + +constexpr bool b7 = !p0; // { dg-warning "address of .B::s." } +constexpr bool b8 = 0 == p0; // { dg-warning "address of .B::s." } +constexpr bool b9 = 0 != p0; // { dg-warning "address of .B::s." } +constexpr bool b10 = 0 < p0; // { dg-warning "ordered comparison" } +constexpr bool b11 = 0 <= p0; // { dg-warning "ordered comparison" } +constexpr bool b12 = 0 > p0; // { dg-warning "ordered comparison" } +constexpr bool b13 = 0 >= p0; // { dg-warning "ordered comparison" } + +} + +namespace C { + +struct A { int i; const A *pa1; const A *pa0; }; + +constexpr A a1 = { 0, 0, 0 }; +constexpr A a2 = { 1, &a1, 0 }; + +constexpr const A *pa2 = &a2; +constexpr int i0 = pa2->i; +constexpr int i1 = pa2->pa1->i; +constexpr int i2 = pa2->pa1->pa0->i; // { dg-error "null pointer|not a constant" } + +constexpr const A *pa3 = &*pa2->pa1->pa0; +constexpr const A *pa4 = pa2->pa1->pa0 + 1; // { dg-error "null pointer|not a constant" } + +constexpr const int *pi0 = &pa2->pa1->pa0->i; // { dg-error "null pointer|not a constant" } + +constexpr const A *pa5 = 0; +constexpr const int *pi1 = &pa5->i; // { dg-error "null pointer|not a constant" } + +} + +namespace WeakRefTest1 { + +extern __attribute__ ((weak)) int i; + +constexpr int *p0 = &i; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wextra" +// Suppress warning: ordered comparison of pointer with integer zero + +constexpr bool b0 = p0; // { dg-error "not a constant expression" } +constexpr bool b1 = p0 == 0; // { dg-error "not a constant expression" } +constexpr bool b2 = p0 != 0; // { dg-error "not a constant expression" } +constexpr bool b4 = p0 <= 0; // { dg-error "not a constant expression" } +constexpr bool b5 = p0 > 0; // { dg-error "not a constant expression" } + +constexpr bool b7 = !p0; // { dg-error "not a constant expression" } +constexpr bool b8 = 0 == p0; // { dg-error "not a constant expression" } +constexpr bool b9 = 0 != p0; // { dg-error "not a constant expression" } +constexpr bool b10 = 0 < p0; // { dg-error "not a constant expression" } +constexpr bool b13 = 0 >= p0; // { dg-error "not a constant expression" } + +// The following are accepted as constant expressions due to bug c++/70196. +constexpr bool b3 = p0 < 0; +constexpr bool b6 = p0 >= 0; +constexpr bool b11 = 0 <= p0; +constexpr bool b12 = 0 > p0; + +#pragma GCC diagnostic pop + +} + +namespace WeakRefTest2 { + +extern __attribute__ ((weak)) int i; + +constexpr int *p1 = &i + 1; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wextra" +// Suppress warning: ordered comparison of pointer with integer zero + +constexpr bool b0 = p1; // { dg-error "not a constant expression" } +constexpr bool b1 = p1 == 0; // { dg-error "not a constant expression" } +constexpr bool b2 = p1 != 0; // { dg-error "not a constant expression" } +constexpr bool b4 = p1 <= 0; // { dg-error "not a constant expression" } +constexpr bool b5 = p1 > 0; // { dg-error "not a constant expression" } + +constexpr bool b7 = !p1; // { dg-error "not a constant expression" } +constexpr bool b8 = 0 == p1; // { dg-error "not a constant expression" } +constexpr bool b9 = 0 != p1; // { dg-error "not a constant expression" } +constexpr bool b10 = 0 < p1; // { dg-error "not a constant expression" } +constexpr bool b13 = 0 >= p1; // { dg-error "not a constant expression" } + +// The following are accepted as constant expressions due to bug c++/70196. +// constexpr bool b3 = p1 < 0; +// constexpr bool b6 = p1 >= 0; +// constexpr bool b11 = 0 <= p1; +// constexpr bool b12 = 0 > p1; + +#pragma GCC diagnostic pop + +} Index: gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr9.C =================================================================== --- gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr9.C (revision 0) +++ gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr9.C (working copy) @@ -0,0 +1,57 @@ +// PR c++/67376 - [5/6 regression] Comparison with pointer to past-the-end +// of array fails inside constant expression +// { dg-do compile { target c++11 } } + +int a [2]; + +constexpr const int* pa[] = { + a, + a + 0, + a + 1, + a + 2, + &a [0], + &a [0] + 0, + &a [0] + 1, + &a [0] + 2, + &a [1], + &a [1] - 1, + &a [1] + 0, + &a [1] + 1, + &a [2] - 2, + &a [2] - 1, + &a [2] + 0 +}; + +#define Assert(e) static_assert ((e), #e) + +Assert (!(a == 0)); +Assert (!(a == (int*)0)); +Assert (!(a == nullptr)); + +Assert (a != 0); +Assert (a != (int*)0); +Assert (a != nullptr); + +Assert (!(0 == a)); +Assert (!((int*)0 == a)); +Assert (!(nullptr == a)); + +Assert (0 != a); +Assert ((int*)0 != a); +Assert (nullptr != a); + +bool constexpr test_eq (unsigned inx) +{ + return inx ? pa [inx - 1] == 0 && 0 == pa [inx - 1] + && test_eq (inx - 1) : pa [inx] == 0 && 0 == pa [inx]; +} + +Assert (!test_eq (sizeof pa / sizeof *pa)); + +bool constexpr test_ne (unsigned inx) +{ + return inx ? pa [inx - 1] != 0 && 0 != pa [inx - 1] + && test_ne (inx - 1) : pa [inx] != 0 && 0 != pa [inx]; +} + +Assert (test_ne (sizeof pa / sizeof *pa)); Index: gcc/testsuite/g++.dg/cpp0x/constexpr-array5.C =================================================================== --- gcc/testsuite/g++.dg/cpp0x/constexpr-array5.C (revision 234306) +++ gcc/testsuite/g++.dg/cpp0x/constexpr-array5.C (working copy) @@ -3,7 +3,7 @@ // Reliable ICE constexpr int n[3] = {}; -constexpr int k = n[-1]; // { dg-error "negative" } +constexpr int k = n[-1]; // { dg-error "array subscript" } // Some random byte -constexpr char c = "foo"[-1000]; // { dg-error "negative" } +constexpr char c = "foo"[-1000]; // { dg-error "array subscript" } Index: gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr.C =================================================================== --- gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr.C (revision 234306) +++ gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr.C (working copy) @@ -1,6 +1,152 @@ +// PR c++/60760 - arithmetic on null pointers should not be allowed +// in constant expressions // { dg-do compile { target c++11 } } +// { dg-additional-options "-Wno-pointer-arith" } + +namespace N { constexpr int zero() { return 0; } void* ptr1 = zero(); // { dg-error "int" } constexpr void* ptr2 = zero(); // { dg-error "int" } + +} + +#define Assert(e) static_assert (e, #e) + + +// Generate a null poiinter. +constexpr int* null () { return 0; } + +namespace SimpleTests { + +constexpr int* p0 = nullptr; +constexpr int* q0 = p0; +constexpr int* r0 = null (); + +// Adding or subtracting zero from a null pointer is valid in C++. +constexpr int* p1 = p0 + 0; +constexpr int* p2 = p0 - 0; +constexpr int* p3 = 0 + p0; + +// While the text of the C++ standard still doesn't allow it, CWG +// issue 232 implies that dererencing a null pointer is intended +// to be permitted in contexts where the result isn't evaluated. +// For compatibility with C that should at a minimum include +// expressions like &*p that are valid there. +constexpr int* p4 = &*p0; +constexpr int* p5 = p0 + 1; // { dg-error "null pointer|not a constant" } +constexpr int* p6 = 1 + p0; // { dg-error "null pointer|not a constant" } +constexpr int* p7 = p0 - 1; // { dg-error "null pointer|not a constant" } +constexpr int* p8 = &p0 [0]; +constexpr int* p9 = &0 [p0]; + +constexpr int* p10 = null () + 2; // { dg-error "null pointer|not a constant" } +constexpr int* p11 = 3 + null (); // { dg-error "null pointer|not a constant" } +constexpr int* p12 = null () - 4; // { dg-error "null pointer|not a constant" } +constexpr int* p13 = &null ()[4]; // { dg-error "null pointer|not a constant" } +constexpr int* p14 = &3[null ()]; // { dg-error "null pointer|not a constant" } + +constexpr int* p15 = &null ()[0]; +constexpr int* p16 = &0[null ()]; + +Assert (p15 == null ()); +Assert (&*p15 == &*null ()); +Assert (&p15 [0] == &null () [0]); +Assert (&null () [0] == &0 [null ()]); +Assert (&0 [null ()] == &null () [0]); +Assert (&null () [0] == p0); +Assert (p0 == &null () [0]); +Assert (&null () [0] == p0); + +constexpr int* q1 = q0 + 0; +constexpr int* q2 = q0 - 0; +constexpr int* q3 = q0 + 1; // { dg-error "null pointer|not a constant" } +constexpr int* q4 = q0 + 2; // { dg-error "null pointer|not a constant" } +constexpr int* q5 = &q0 [0]; + +// Subtracting null pointers from one another is valid. +Assert (0 == (p0 - (int*)0)); +Assert (0 == (p0 - static_cast(0))); +Assert (0 == (p0 - (int*)nullptr)); +Assert (0 == (p0 - static_cast(nullptr))); +Assert (0 == (p0 - p0)); +Assert (0 == (p0 - q0)); +Assert (0 == (p0 - r0)); +Assert (0 == ((int*)0 - p0)); +Assert (0 == (static_cast(0) - p0)); +Assert (0 == ((int*)nullptr - p0)); +Assert (0 == ( static_cast(nullptr) - p0)); +Assert (0 == ( q0 - p0)); +Assert (0 == ( r0 - p0)); + +} + +namespace IndirectTests { + +struct S { int i, j; struct SA { struct SB { int *pi; } sb; } sa; }; + +constexpr S* ps = (S*)0; + +// Comparing null pointers is valid. +Assert ( (ps == ps)); +Assert (!(ps != ps)); +Assert (!(ps < ps)); +Assert ( (ps <= ps)); +Assert (!(ps > ps)); +Assert ( (ps >= ps)); + +Assert ( (ps == (S*)0)); +Assert (!(ps != (S*)0)); +Assert (!(ps < (S*)0)); +Assert ( (ps <= (S*)0)); +Assert (!(ps > (S*)0)); +Assert ( (ps >= (S*)0)); + +constexpr S* ps1 = ps; +constexpr S* ps2 = ps1; + +// The following aren't diagnosed due to a bug. +// constexpr int* pi0 = &((S*)0)->i; +// constexpr int* pi1 = &((S*)nullptr)->i; + +constexpr int* pj0 = &((S*)0)->j; // { dg-error "null pointer|not a constant" } +constexpr int* pj1 = &((S*)nullptr)->j; // { dg-error "null pointer|not a constant" } + +constexpr int* psi = &ps->i; // { dg-error "null pointer|not a constant" } +constexpr int* psj = &ps->j; // { dg-error "null pointer|not a constant" } + +constexpr int* ps1i = &ps1->i; // { dg-error "null pointer|not a constant" } +constexpr int* ps2i = &ps1->i; // { dg-error "null pointer|not a constant" } + +constexpr int* ps1j = &ps1->j; // { dg-error "null pointer|not a constant" } +constexpr int* ps2j = &ps1->j; // { dg-error "null pointer|not a constant" } + +} + +namespace FunctionTests { + +typedef void Func (); + +// Arithmetic on member function pointers is diagnosed with -Wpointer-arith. +// With constexpr, only zero may be added or subtracted. +constexpr Func *pf0 = 0; +constexpr Func *pf1 = pf0 + 0; // triggers -Wpointer-arith +constexpr Func *pf2 = pf0 - 0; // triggers -Wpointer-arith +constexpr Func *pf3 = 0 + pf0; // triggers -Wpointer-arith +constexpr Func *pf4 = pf0 + 1; // { dg-error "null pointer|not a constant" } +constexpr Func *pf5 = 2 + pf0; // { dg-error "null pointer|not a constant" } +constexpr Func *pf6 = pf0 - 3; // { dg-error "null pointer|not a constant" } + +struct S; +typedef void (S::*MemFuncPtr)(); + +// Arithmetic on member function pointers is rejected with a hard error. +constexpr MemFuncPtr pmf0 = nullptr; +constexpr MemFuncPtr pmf1 = pmf0 + 0; // { dg-error "invalid operands" } +constexpr MemFuncPtr pmf2 = 0 + pmf0; // { dg-error "invalid operands" } +constexpr MemFuncPtr pmf3 = pmf0 + 1; // { dg-error "invalid operands" } +constexpr MemFuncPtr pmf4 = 1 + pmf0; // { dg-error "invalid operands" } +constexpr MemFuncPtr pmf5 = pmf0 - 1; // { dg-error "invalid operands" } + +} Index: gcc/testsuite/g++.dg/cpp0x/constexpr-string.C =================================================================== --- gcc/testsuite/g++.dg/cpp0x/constexpr-string.C (revision 234306) +++ gcc/testsuite/g++.dg/cpp0x/constexpr-string.C (working copy) @@ -2,4 +2,4 @@ constexpr char c1 = "hi"[1]; constexpr char c2 = "hi"[2]; -constexpr char c3 = "hi"[3]; // { dg-error "out of bound" } +constexpr char c3 = "hi"[3]; // { dg-error "array subscript" } Index: gcc/testsuite/g++.dg/cpp0x/constexpr-tuple.C =================================================================== --- gcc/testsuite/g++.dg/cpp0x/constexpr-tuple.C (revision 234306) +++ gcc/testsuite/g++.dg/cpp0x/constexpr-tuple.C (working copy) @@ -66,30 +66,30 @@ class background_hello public: background_hello() { - __builtin_printf("default ctor called, this=%p\n", this); + __builtin_printf("%s, this=%p, c=%i\n", __PRETTY_FUNCTION__, this, c); ++c; } background_hello(const background_hello &) { - __builtin_printf("copy ctor called\n"); + __builtin_printf("%s, this=%p, c=%i\n", __PRETTY_FUNCTION__, this, c); ++c; } background_hello(background_hello &&) { - __builtin_printf("move ctor called\n"); + __builtin_printf("%s, this=%p, c=%i\n", __PRETTY_FUNCTION__, this, c); ++c; } void operator ()() const { - __builtin_printf("void background_hello::operator()() called, this=%p\n", this); + __builtin_printf("%s, this=%p, c=%i\n", __PRETTY_FUNCTION__, this, c); } ~background_hello() { - __builtin_printf("destructor called, this=%p\n", this); + __builtin_printf("%s, this=%p, c=%i\n", __PRETTY_FUNCTION__, this, c); --c; } Index: gcc/testsuite/g++.dg/cpp0x/constexpr-wstring2.C =================================================================== --- gcc/testsuite/g++.dg/cpp0x/constexpr-wstring2.C (revision 234306) +++ gcc/testsuite/g++.dg/cpp0x/constexpr-wstring2.C (working copy) @@ -1,6 +1,6 @@ // PR c++/48570 // { dg-do compile { target c++11 } } -constexpr wchar_t c1 = L"hi"[3]; // { dg-error "out of bound" } -constexpr char16_t c2 = u"hi"[3]; // { dg-error "out of bound" } -constexpr char32_t c3 = U"hi"[3]; // { dg-error "out of bound" } +constexpr wchar_t c1 = L"hi"[3]; // { dg-error "array subscript" } +constexpr char16_t c2 = u"hi"[3]; // { dg-error "array subscript" } +constexpr char32_t c3 = U"hi"[3]; // { dg-error "array subscript" } Index: gcc/testsuite/g++.dg/cpp0x/pr65398.C =================================================================== --- gcc/testsuite/g++.dg/cpp0x/pr65398.C (revision 234306) +++ gcc/testsuite/g++.dg/cpp0x/pr65398.C (working copy) @@ -12,17 +12,17 @@ constexpr char c5 = *(&s[2] + 0); constexpr char c6 = *(&s[0] + 2); constexpr char c7 = *(&s[2] + 1); -constexpr char d1 = *(&s[4] - 0); // { dg-error "array subscript out of bound" } +constexpr char d1 = *(&s[4] - 0); // { dg-error "array subscript" } constexpr char d2 = *(&s[4] - 1); constexpr char d3 = *(&s[4] - 2); constexpr char d4 = *(&s[4] - 3); constexpr char d5 = *(&s[4] - 4); -constexpr char d6 = *(&s[4] - 5); // { dg-error "negative array subscript" } +constexpr char d6 = *(&s[4] - 5); // { dg-error "array subscript" } /* Don't accept invalid stuff. */ -constexpr char e1 = *(&s[5] - 1); // { dg-error "is not a constant expression" } -constexpr char e2 = *(&s[5] - 2); // { dg-error "is not a constant expression" } -constexpr char e3 = *(&s[5] - 3); // { dg-error "is not a constant expression" } +constexpr char e1 = *(&s[5] - 1); // { dg-error "array subscript" } +constexpr char e2 = *(&s[5] - 2); // { dg-error "array subscript" } +constexpr char e3 = *(&s[5] - 3); // { dg-error "array subscript" } SA (c1 == 'a'); SA (c2 == 'b'); @@ -45,17 +45,17 @@ constexpr int i5 = *(&l[2] + 0); constexpr int i6 = *(&l[0] + 2); constexpr int i7 = *(&l[2] + 1); -constexpr char j1 = *(&l[4] - 0); // { dg-error "array subscript out of bound" } +constexpr char j1 = *(&l[4] - 0); // { dg-error "array subscript" } constexpr char j2 = *(&l[4] - 1); constexpr char j3 = *(&l[4] - 2); constexpr char j4 = *(&l[4] - 3); constexpr char j5 = *(&l[4] - 4); -constexpr char j6 = *(&l[4] - 5); // { dg-error "negative array subscript" } +constexpr char j6 = *(&l[4] - 5); // { dg-error "array subscript" } /* Don't accept invalid stuff. */ -constexpr char k1 = *(&l[5] - 1); // { dg-error "is not a constant expression" } -constexpr char k2 = *(&l[5] - 2); // { dg-error "is not a constant expression" } -constexpr char k3 = *(&l[5] - 3); // { dg-error "is not a constant expression" } +constexpr char k1 = *(&l[5] - 1); // { dg-error "array subscript" } +constexpr char k2 = *(&l[5] - 2); // { dg-error "array subscript" } +constexpr char k3 = *(&l[5] - 3); // { dg-error "array subscript" } SA (i1 == 'c'); SA (i2 == 'd'); Index: gcc/testsuite/g++.dg/ext/constexpr-vla1.C =================================================================== --- gcc/testsuite/g++.dg/ext/constexpr-vla1.C (revision 234306) +++ gcc/testsuite/g++.dg/ext/constexpr-vla1.C (working copy) @@ -27,4 +27,4 @@ fn_not_ok (int n) } constexpr int n1 = fn_ok (3); -constexpr int n2 = fn_not_ok (3); // { dg-error "array subscript out of bound" } +constexpr int n2 = fn_not_ok (3); // { dg-error "array subscript" } Index: gcc/testsuite/g++.dg/ext/constexpr-vla2.C =================================================================== --- gcc/testsuite/g++.dg/ext/constexpr-vla2.C (revision 234306) +++ gcc/testsuite/g++.dg/ext/constexpr-vla2.C (working copy) @@ -18,4 +18,4 @@ fn_ok (int n) } constexpr int i1 = fn_ok (3); -constexpr int i2 = fn_bad (3); // { dg-error "array subscript out of bound" } +constexpr int i2 = fn_bad (3); // { dg-error "array subscript" } Index: gcc/testsuite/g++.dg/ext/constexpr-vla3.C =================================================================== --- gcc/testsuite/g++.dg/ext/constexpr-vla3.C (revision 234306) +++ gcc/testsuite/g++.dg/ext/constexpr-vla3.C (working copy) @@ -11,4 +11,4 @@ foo (int n) return z; } -constexpr int n = foo (3); // { dg-error "array subscript out of bound" } +constexpr int n = foo (3); // { dg-error "array subscript" } Index: gcc/testsuite/g++.dg/ubsan/pr63956.C =================================================================== --- gcc/testsuite/g++.dg/ubsan/pr63956.C (revision 234306) +++ gcc/testsuite/g++.dg/ubsan/pr63956.C (working copy) @@ -86,7 +86,7 @@ fn5 (const int *a, int b) constexpr int m1[4] = { 1, 2, 3, 4 }; constexpr int m2 = fn5 (m1, 3); -constexpr int m3 = fn5 (m1, 4); // { dg-error "array subscript out of bound" } +constexpr int m3 = fn5 (m1, 4); // { dg-error "array subscript" } constexpr int fn6 (const int &a, int b) @@ -106,7 +106,7 @@ fn7 (const int *a, int b) constexpr int n1 = 7; constexpr int n2 = fn7 (&n1, 5); -constexpr int n3 = fn7 ((const int *) 0, 8); // { dg-error "is not a constant expression" } +constexpr int n3 = fn7 ((const int *) 0, 8); // { dg-error "null pointer" } constexpr int fn8 (int i) @@ -116,7 +116,7 @@ fn8 (int i) } constexpr int o1 = fn8 (9); -constexpr int o2 = fn8 (10); // { dg-error "array subscript out of bound" } +constexpr int o2 = fn8 (10); // { dg-error "array subscript" } constexpr int fn9 (int a, int b)