From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 34354 invoked by alias); 17 Mar 2016 21:17:09 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Received: (qmail 34336 invoked by uid 89); 17 Mar 2016 21:17:08 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.8 required=5.0 tests=AWL,BAYES_00,FREEMAIL_FROM,KAM_ASCII_DIVIDERS,RCVD_IN_DNSWL_LOW,SPF_PASS autolearn=no version=3.3.2 spammy=sk:backgro, faithful, INTEGER_CST, build4 X-HELO: mail-qg0-f51.google.com Received: from mail-qg0-f51.google.com (HELO mail-qg0-f51.google.com) (209.85.192.51) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-GCM-SHA256 encrypted) ESMTPS; Thu, 17 Mar 2016 21:17:03 +0000 Received: by mail-qg0-f51.google.com with SMTP id u110so83762602qge.3 for ; Thu, 17 Mar 2016 14:17:03 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:subject:to:references:cc:message-id:date :user-agent:mime-version:in-reply-to; bh=9VtSPOauK5N/eDqGvvY1zO5CZhKiUWZkR5vobNDrnZw=; b=dIrtaIwo7TLiaSK0utuWxgcxWPt47VMESIjgFVPicXj5KBzGKRZmzxk+OqwBf5fZ5D FULwNfjkzjxMJP9K7OS5PF6H/ldB3VKnCs7+2dVSK2ZToNCDsJoupikkynYSvj5hpekw 0BoBE2f6w1xOxvN0I5tMrZvUOwCEx7XeRrEA59c/6lhCdbT+XljQR//oML8WFIf64NRX 5rnjAU/kR59Lt6Wc1Y1ehhRBEqS6pjfMfsCTJ/ekLJni3sGip3/BpXlG05CxEOJvkeEv 6qan20g0cIBrevT2RPQVGN5wZ1NZByfQicjB9fs99UInMnc0UUW/U7aAETSgQvPnCaO6 4HFg== X-Gm-Message-State: AD7BkJKCiV8qn8Bgm8yIUHqVnQ1XIAqOqH7USixtl/nfBvH4fLadV3bNSeM3zBFms0eSpQ== X-Received: by 10.140.105.198 with SMTP id c64mr17677976qgf.94.1458249421489; Thu, 17 Mar 2016 14:17:01 -0700 (PDT) Received: from [192.168.0.26] (97-122-182-246.hlrn.qwest.net. [97.122.182.246]) by smtp.gmail.com with ESMTPSA id c108sm4602174qgc.13.2016.03.17.14.16.59 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 17 Mar 2016 14:17:00 -0700 (PDT) From: Martin Sebor Subject: Re: [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression To: Jeff Law , Gcc Patch List , Jason Merrill References: <56E72C33.8000301@gmail.com> <56E9B9C3.5000908@redhat.com> Cc: Marek Polacek , Jakub Jelinek Message-ID: <56EB1ECA.90800@gmail.com> Date: Thu, 17 Mar 2016 21:35:00 -0000 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Thunderbird/38.1.0 MIME-Version: 1.0 In-Reply-To: <56E9B9C3.5000908@redhat.com> Content-Type: multipart/mixed; boundary="------------040103050200060503010803" X-IsSubscribed: yes X-SW-Source: 2016-03/txt/msg01051.txt.bz2 This is a multi-part message in MIME format. --------------040103050200060503010803 Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit Content-length: 3978 >> static tree cxx_eval_constant_expression (const constexpr_ctx *, tree, >> - bool, bool *, bool *, tree * = NULL); >> + bool, bool *, bool *, bool * = NULL, >> + tree * = NULL); > I didn't look deeply, but do you end up fixing all (most) of the callers > of cxx_eval_constant_expression? If so, then you don't need the default > initialization. Thanks for the comments. The patch only modifies about 10 out of the 70 or so calls to the function in the file and the default argument helps avoid making the rest of the changes. (I have some ideas for improving the APIs of these functions that I'd like to run by Jason when we're done with the 6.0 work.) > At a high level, I'm curious your thoughts on emitting these errors out > of the front-end rather than after analysis. I'm thinking in particular > about constant propagation, unreachable code elimination and DCE. The > former can expose cases that are difficult to catch in the front-end, > while the latter two might remove an out-of-range comparison/reference. The difficulty I've run into with detecting these problems in later phases is that some invalid expressions have already been simplified by the front end. The example that applies here (even though this is still the front end) is this: constexpr int* p = 0; constexpr bool b0 = &p[0] == 0; // accepted constexpr bool b1 = &p[1] == 0; // rejected Both b0 and b1 are invalid and should be diagnosed, but only b1 is. b1 isn't because because by the time we see its initializer in constexpr.c it's been transformed into the equivalent of "b1 = (int*)ps" (though we don't see the cast which would also make it invalid). But if we can avoid these early simplifying transformations and retain a more faithful representation of the original source then doing the checking later will likely be simpler and result in detecting more problems with greater consistency and less effort. > diff --git a/gcc/fold-const.c b/gcc/fold-const.c >> index 696b4a6..376aa09 100644 >> --- a/gcc/fold-const.c >> +++ b/gcc/fold-const.c >> @@ -8639,6 +8639,37 @@ fold_comparison (location_t loc, enum tree_code >> code, tree type, >> base1 = build_fold_addr_expr_loc (loc, base1); >> return fold_build2_loc (loc, code, type, base0, base1); >> } >> + >> + /* Comparison between an ordinary (non-weak) symbol and a null >> + pointer can be eliminated since such sybols must have a non >> + null address. */ > Hmm, I thought we already had code to do this somewhere. It looks like > it's moved around quite a bit. I think you want to be using > symtab_node::nonzero_address to determine if a given symbol must bind to > a nonzero address. Thanks for the hint. I had looked for existing functions but couldn't find one that worked. decl_with_nonnull_addr_p() in c-common.c looked promising but it's not accessible here and it doesn't do the right thing when HAS_DECL_ASSEMBLER_NAME_P() is false (it ICEs). There are a few functions in the fold-const.c itself that make use of symtab_node::nonzero_address(), either directly or indirectly (tree_expr_nonzero_p and tree_expr_nonzero_warnv_p) but none is usable in this context (they don't handle VAR_DECL, and adding that handling appears more involved than the current approach). In the end, I added a new function, maybe_nonzero_address(), that calls symtab_node::nonzero_address(), and that I factored out of tree_single_nonzero_warnv_p() that I was then able to use in fold_comparison(). I've made a few other small adjustments to the patch to avoid one false positive, and a few test cases, and tweak the expected diagnostics now that Marek has fixed 70194. I've also wrote myself a small sed script to replace blocks of 8 spaces with tabs and ran the patch through it. I'll integrate it into my workflow so I hopefully don't have to worry about this ever again. Martin --------------040103050200060503010803 Content-Type: text/x-patch; name="gcc-67376.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="gcc-67376.patch" Content-length: 42279 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) --------------040103050200060503010803--