From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 94827 invoked by alias); 14 Mar 2016 21:25:19 -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 94809 invoked by uid 89); 14 Mar 2016 21:25:18 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-0.8 required=5.0 tests=AWL,BAYES_50,FREEMAIL_FROM,RCVD_IN_DNSWL_LOW,SPF_PASS autolearn=ham version=3.3.2 spammy=Reliable, (unknown), 274, Fold X-HELO: mail-qg0-f53.google.com Received: from mail-qg0-f53.google.com (HELO mail-qg0-f53.google.com) (209.85.192.53) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-GCM-SHA256 encrypted) ESMTPS; Mon, 14 Mar 2016 21:25:13 +0000 Received: by mail-qg0-f53.google.com with SMTP id y89so165853557qge.2 for ; Mon, 14 Mar 2016 14:25:13 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:to:from:subject:cc:message-id:date:user-agent :mime-version; bh=+8DUIIsO7EjjvuNDBX/u3Z7QLQ7M4AUlnJopkyz8gFA=; b=l1ZL7/fL4y4LQb0EgHCAzEJ6UoieQZadd9u7Gfy1d1ujpECmsDqyqrsQL+z8WK8UGE A0uwxramcPOEMSS1ui+EpVQ4YX04w6UBKMKHD+k62RbMX/W0OzFXMvsDxyJaFM437yCJ x6rTfvay5k5xx7k0pCdw/A8kLIbU6prn4I333QmyyT7ALhQh6jk+bQpRmWzemc2QAh4r BcUhBuDbfkSdCS+f0Oh4ieVd3tM3icLYVYt5OP6D4XfDymoYJ7Ie7/R8378LXlQQhJ+C lLKi3YWpKN0HBcMdmnNAg/1ojXTpvbDCkKskOjAy6CUWrvy0YndQhivuxR2PlMfjBVdU IczQ== X-Gm-Message-State: AD7BkJIm4rudP90dU7iTD62mOo292q1EdDV5N8sSExWU7xG7MaciDn5PtGHi/4XC1/wk4g== X-Received: by 10.140.107.70 with SMTP id g64mr33165751qgf.75.1457990711288; Mon, 14 Mar 2016 14:25:11 -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 78sm11224843qgt.1.2016.03.14.14.25.09 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 14 Mar 2016 14:25:10 -0700 (PDT) To: Gcc Patch List , Jason Merrill From: Martin Sebor Subject: [PATCH] c++/67376 Comparison with pointer to past-the-end, of array fails inside constant expression Cc: Marek Polacek , Jakub Jelinek Message-ID: <56E72C33.8000301@gmail.com> Date: Mon, 14 Mar 2016 21:25:00 -0000 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Thunderbird/38.1.0 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="------------040506030807040300080001" X-IsSubscribed: yes X-SW-Source: 2016-03/txt/msg00796.txt.bz2 This is a multi-part message in MIME format. --------------040506030807040300080001 Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit Content-length: 1283 The attached patch fixes the outstanding cases mentioned in comment 10 on bug c++/67376. While testing the fix I uncovered a number of other related problems without which the test would have been incomplete. They include: 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 In addition, I include a fix for the issue below that I also came across while testing the patch and that makes root causing constexpr problems due to out-of-bounds array subscripts easier: PR c++/70228 - insufficient detail in diagnostics for a constexpr out of bounds array subscript In a discussion of bug 70170 between those CC'd Marek posted a prototype patch for match.pd. While the patch seems to do the right thing as far as the bug goes, like my own first attempt at a fix in const-fold.c it caused a couple of regressions (in pr21294.c and in pr44555.c). Since I'm not yet familiar enough with match.pd, in the interest of time I solved those regressions in const-fold.c rather than in match.pd. Tested on x86_64. Martin --------------040506030807040300080001 Content-Type: text/x-patch; name="gcc-67376.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="gcc-67376.patch" Content-length: 38971 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-14 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-ptr11.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-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-14 Martin Sebor PR c++/67376 PR c++/70170 PR c++/70172 PR c++/60760 PR c++/70228 (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. * constexpr.c (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-14 Martin Sebor PR c++/67376 * fold-const.c (fold_comparison): Fold equality and relational expressions involving null pointers. diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 5f97c9d..5ec5034 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -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 constexpr_ctx *ctx, tree t, 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 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; @@ -1642,6 +1644,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); @@ -1682,11 +1693,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 @@ -1815,6 +1826,30 @@ find_array_ctor_elt (tree ary, tree dindex, bool insert = false) 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 constexpr_ctx *ctx, tree t, 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 constexpr_ctx *ctx, tree t, 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 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; @@ -1944,7 +1988,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) @@ -2655,7 +2706,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; @@ -2680,6 +2732,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; @@ -3149,7 +3204,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)) @@ -3285,8 +3340,10 @@ cxx_eval_pointer_plus_expression (const constexpr_ctx *ctx, tree t, 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 constexpr_ctx *ctx, tree t, { 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 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_expr (init); ctx->values->put (r, init); @@ -3432,7 +3501,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); @@ -3454,14 +3524,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; @@ -3472,7 +3544,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); @@ -3489,18 +3562,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 @@ -3509,7 +3582,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: @@ -3517,7 +3590,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; @@ -3560,17 +3634,17 @@ 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); 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 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 @@ -3659,7 +3733,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: @@ -3712,12 +3786,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); @@ -3738,15 +3812,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 @@ -3779,7 +3866,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: @@ -3828,7 +3915,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; @@ -3935,8 +4022,9 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, r = TARGET_EXPR_INITIAL (r); } - r = cxx_eval_constant_expression (&ctx, r, - false, &non_constant_p, &overflow_p); + bool nullptr_p = false; + r = cxx_eval_constant_expression (&ctx, r, false, + &non_constant_p, &overflow_p, &nullptr_p); verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p); diff --git a/gcc/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. */ + else if (DECL_P (base0) + && (!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 diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr10.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr10.C new file mode 100644 index 0000000..b3b68ff --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr10.C @@ -0,0 +1,150 @@ +// 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 -Wno-extra" } + +namespace A { + +extern int i; + +constexpr int *p0 = &i; + +constexpr bool b0 = p0; // { dg-warning "address of .A::i. will always evaluate as .true." } + +// The following don't cause the -Waddress warning due to bug c++/70194. +constexpr bool b1 = p0 == 0; +constexpr bool b2 = p0 != 0; +constexpr bool b3 = p0 < 0; +constexpr bool b4 = p0 <= 0; +constexpr bool b5 = p0 > 0; +constexpr bool b6 = p0 >= 0; + +constexpr bool b7 = !p0; // { dg-warning "address of .A::i. will always evaluate as .true." } + +// The following don't cause the -Waddress warning due to bug c++/70194. +constexpr bool b8 = 0 == p0; +constexpr bool b9 = 0 != p0; +constexpr bool b10 = 0 < p0; +constexpr bool b11 = 0 <= p0; +constexpr bool b12 = 0 > p0; +constexpr bool b13 = 0 >= p0; + +} + +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. will always evaluate as .true." } + +// The following don't cause the -Waddress warning due to bug c++/70194. +constexpr bool b1 = p0 == 0; +constexpr bool b2 = p0 != 0; +constexpr bool b3 = p0 < 0; +constexpr bool b4 = p0 <= 0; +constexpr bool b5 = p0 > 0; +constexpr bool b6 = p0 >= 0; + +constexpr bool b7 = !p0; // { dg-warning "address of .B::s. will always evaluate as .true." } + +// The following don't cause the -Waddress warning due to bug c++/70194. +constexpr bool b8 = 0 == p0; +constexpr bool b9 = 0 != p0; +constexpr bool b10 = 0 < p0; +constexpr bool b11 = 0 <= p0; +constexpr bool b12 = 0 > p0; +constexpr bool b13 = 0 >= p0; + +} + +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; + +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; + +} + +namespace WeakRefTest2 { + +extern __attribute__ ((weak)) int i; + +constexpr int *p1 = &i + 1; + +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; + +} diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr11.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr11.C new file mode 100644 index 0000000..3b28f20 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr11.C @@ -0,0 +1,128 @@ +// 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" } + +// 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* 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(0); +constexpr int i2 = p0 - (int*)nullptr; +constexpr int i3 = p0 - static_cast(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(0) - p0; +constexpr int i9 = (int*)nullptr - p0; +constexpr int i10 = static_cast(nullptr) - p0; +constexpr int i11 = q0 - p0; +constexpr int i12 = r0 - p0; + +} + +namespace IndirectTests { + +struct S { int i, j; struct SA { struct SB { int *pi; } sb; } sa; }; + +constexpr S* ps = (S*)0; + +// Comparing null pointers is valid. +constexpr bool b0 = ps == ps; +constexpr bool b1 = ps != ps; +constexpr bool b2 = ps < ps; +constexpr bool b3 = ps <= ps; +constexpr bool b4 = ps > ps; +constexpr bool b5 = ps >= ps; + +constexpr bool b6 = ps == (S*)0; +constexpr bool b7 = ps != (S*)0; +constexpr bool b8 = ps < (S*)0; +constexpr bool b9 = ps <= (S*)0; +constexpr bool b10 = ps > (S*)0; +constexpr bool b11 = ps >= (S*)0; + +constexpr S* ps1 = ps; +constexpr S* ps2 = ps1; + +// The following aren't diagnosed due to a bug. +// constexpr int* pi0 = &((S*)0)->i; +// constexpr int* pi1 = &((S*)nullptr)->i; + +constexpr int* pj0 = &((S*)0)->j; // { dg-error "null pointer|not a constant" } +constexpr int* pj1 = &((S*)nullptr)->j; // { dg-error "null pointer|not a constant" } + +constexpr int* psi = &ps->i; // { dg-error "null pointer|not a constant" } +constexpr int* psj = &ps->j; // { dg-error "null pointer|not a constant" } + +constexpr int* ps1i = &ps1->i; // { dg-error "null pointer|not a constant" } +constexpr int* ps2i = &ps1->i; // { dg-error "null pointer|not a constant" } + +constexpr int* ps1j = &ps1->j; // { dg-error "null pointer|not a constant" } +constexpr int* ps2j = &ps1->j; // { dg-error "null pointer|not a constant" } + +} + +namespace FunctionTests { + +typedef void Func (); + +// Arithmetic on member function pointers is diagnosed with -Wpointer-arith. +// With constexpr, only zero may be added or subtracted. +constexpr Func *pf0 = 0; +constexpr Func *pf1 = pf0 + 0; // triggers -Wpointer-arith +constexpr Func *pf2 = pf0 - 0; // triggers -Wpointer-arith +constexpr Func *pf3 = 0 + pf0; // triggers -Wpointer-arith +constexpr Func *pf4 = pf0 + 1; // { dg-error "null pointer|not a constant" } +constexpr Func *pf5 = 2 + pf0; // { dg-error "null pointer|not a constant" } +constexpr Func *pf6 = pf0 - 3; // { dg-error "null pointer|not a constant" } + +struct S; +typedef void (S::*MemFuncPtr)(); + +// Arithmetic on member function pointers is rejected with a hard error. +constexpr MemFuncPtr pmf0 = nullptr; +constexpr MemFuncPtr pmf1 = pmf0 + 0; // { dg-error "invalid operands" } +constexpr MemFuncPtr pmf2 = 0 + pmf0; // { dg-error "invalid operands" } +constexpr MemFuncPtr pmf3 = pmf0 + 1; // { dg-error "invalid operands" } +constexpr MemFuncPtr pmf4 = 1 + pmf0; // { dg-error "invalid operands" } +constexpr MemFuncPtr pmf5 = pmf0 - 1; // { dg-error "invalid operands" } + +} diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr9.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr9.C new file mode 100644 index 0000000..c690131 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr9.C @@ -0,0 +1,56 @@ +// PR c++/67376 +// { 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)); diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-array5.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-array5.C index 4605b4b..00dfd6d 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-array5.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-array5.C @@ -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" } diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-string.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-string.C index 0f561a4..41fa466 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-string.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-string.C @@ -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" } diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-wstring2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-wstring2.C index db79a9c..4055e0e 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-wstring2.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-wstring2.C @@ -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" } diff --git a/gcc/testsuite/g++.dg/cpp0x/pr65398.C b/gcc/testsuite/g++.dg/cpp0x/pr65398.C index a4aeba5..bab875c 100644 --- a/gcc/testsuite/g++.dg/cpp0x/pr65398.C +++ b/gcc/testsuite/g++.dg/cpp0x/pr65398.C @@ -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'); diff --git a/gcc/testsuite/g++.dg/ext/constexpr-vla1.C b/gcc/testsuite/g++.dg/ext/constexpr-vla1.C index a5615bb..21eb93d 100644 --- a/gcc/testsuite/g++.dg/ext/constexpr-vla1.C +++ b/gcc/testsuite/g++.dg/ext/constexpr-vla1.C @@ -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" } diff --git a/gcc/testsuite/g++.dg/ext/constexpr-vla2.C b/gcc/testsuite/g++.dg/ext/constexpr-vla2.C index 6cb1f70..6aab184 100644 --- a/gcc/testsuite/g++.dg/ext/constexpr-vla2.C +++ b/gcc/testsuite/g++.dg/ext/constexpr-vla2.C @@ -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" } diff --git a/gcc/testsuite/g++.dg/ext/constexpr-vla3.C b/gcc/testsuite/g++.dg/ext/constexpr-vla3.C index ba4eb50..33fc968 100644 --- a/gcc/testsuite/g++.dg/ext/constexpr-vla3.C +++ b/gcc/testsuite/g++.dg/ext/constexpr-vla3.C @@ -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" } diff --git a/gcc/testsuite/g++.dg/ubsan/pr63956.C b/gcc/testsuite/g++.dg/ubsan/pr63956.C index b265631..98991fd 100644 --- a/gcc/testsuite/g++.dg/ubsan/pr63956.C +++ b/gcc/testsuite/g++.dg/ubsan/pr63956.C @@ -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) --------------040506030807040300080001--