From patchwork Tue Mar 28 11:32:28 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nathaniel Shead X-Patchwork-Id: 67004 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id B2EA1385558F for ; Tue, 28 Mar 2023 11:34:40 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org B2EA1385558F DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1680003280; bh=Y76swjtdjLFmqY6LXi70kPY7z7Fm7/EQKejPNRHE2uQ=; h=To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=IK3jCzG3MKLnBmFhwlfNym5hy/HXIVR29GGTTuSywK5KHNKvJaXW2wICRLrU1vxLv ddDFMOmAk8mCld9OgH1lb4HMVNnHrHGpMgKzvmjwfpXWEc3IElbsPymvumCGydpino k0E8vBtUC2D2qYXMgsxJuIdEC6Tc5y7NFDefpngg= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-pl1-x62a.google.com (mail-pl1-x62a.google.com [IPv6:2607:f8b0:4864:20::62a]) by sourceware.org (Postfix) with ESMTPS id CC4123858CDA for ; Tue, 28 Mar 2023 11:33:09 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org CC4123858CDA Received: by mail-pl1-x62a.google.com with SMTP id w4so11311365plg.9 for ; Tue, 28 Mar 2023 04:33:09 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680003188; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Y76swjtdjLFmqY6LXi70kPY7z7Fm7/EQKejPNRHE2uQ=; b=SA9RDaIdtWVvxuvrslxkgnLwL11Y8zDIegj9hn5Xd6Y3ph1cm+s2o4sSd4thN06Wh2 7Q2j2LJvuzpl9iTeB69KtHKjs6hc5Md0u4aIAN4fq3bhX/g0I9mJiFoyDypBpC93bN4x RABG8xGp2iCB/dX6PsBYSV5N9iMV4kJ7Vd5XVaLjs0YinjIzzwRCzAT81JZMFkEib1I0 Oe5WHWL6kodaS59/vPToksaK9WxssLyWtTtuM+KL2rLT/g0jqo1K80WUPSeoVikNrjxN NzHUJusl032349A1rVtHMht8t1vyvz9lClw0QF9DFXUtylMmFswbZ7nGVf8QqYkmciC/ B7CA== X-Gm-Message-State: AO0yUKU32p2bqBFXqzesGspkG4TdIR5Ky9aFLDteCNsBrCzAAwmnwTzE 35sXEf5GMaD0sAysnz/gmD9f0yHGKjg= X-Google-Smtp-Source: AK7set8p7o8J1yl2HQR/JPX/euu25TisZaL/l6LqfkZhU9UM9nun0YQ7W11g31UILGhOQVRHAzjzJw== X-Received: by 2002:a05:6a20:bb2f:b0:d9:f086:e751 with SMTP id fc47-20020a056a20bb2f00b000d9f086e751mr11589333pzb.26.1680003188394; Tue, 28 Mar 2023 04:33:08 -0700 (PDT) Received: from Thaum.localdomain (125-209-144-50.tpgi.com.au. [125.209.144.50]) by smtp.gmail.com with ESMTPSA id e17-20020a62ee11000000b0062ce765b7afsm6097512pfi.162.2023.03.28.04.33.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 28 Mar 2023 04:33:08 -0700 (PDT) To: gcc-patches@gcc.gnu.org Cc: Nathaniel Shead Subject: [PATCH 1/3] c++: Track lifetimes in constant evaluation [PR70331, PR96630, PR98675] Date: Tue, 28 Mar 2023 22:32:28 +1100 Message-Id: <20230328113230.19975-2-nathanieloshead@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230328113230.19975-1-nathanieloshead@gmail.com> References: <20230328113230.19975-1-nathanieloshead@gmail.com> MIME-Version: 1.0 X-Spam-Status: No, score=-12.5 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Nathaniel Shead via Gcc-patches From: Nathaniel Shead Reply-To: Nathaniel Shead Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" This adds rudimentary lifetime tracking in C++ constexpr contexts, allowing the compiler to report errors with using values after their backing has gone out of scope. We don't yet handle other ways of ending lifetimes (e.g. explicit destructor calls). PR c++/70331 PR c++/96630 PR c++/98675 gcc/cp/ChangeLog: * constexpr.cc (constexpr_global_ctx::put_value): Mark value as in lifetime. (constexpr_global_ctx::remove_value): Mark value as expired. (cxx_eval_call_expression): Remove comment that is no longer applicable. (non_const_var_error): Add check for expired values. (cxx_eval_constant_expression): Add checks for expired values. Forget local variables at end of bind expressions. Forget temporaries at end of cleanup points. * module.cc (trees_out::core_bools): Write out the new flag. (trees_in::core_bools): Read in the new flag. gcc/ChangeLog: * tree-core.h (struct tree_decl_common): New flag to check if value lifetime has expired. * tree.h (DECL_EXPIRED): Access the new flag. * print-tree.cc (print_node): Print the new flag. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/constexpr-ice20.C: Update error raised by test. * g++.dg/cpp1y/constexpr-lifetime1.C: New test. * g++.dg/cpp1y/constexpr-lifetime2.C: New test. * g++.dg/cpp1y/constexpr-lifetime3.C: New test. * g++.dg/cpp1y/constexpr-lifetime4.C: New test. * g++.dg/cpp1y/constexpr-lifetime5.C: New test. Signed-off-by: Nathaniel Shead --- gcc/cp/constexpr.cc | 66 +++++++++++++++---- gcc/cp/module.cc | 2 + gcc/print-tree.cc | 4 ++ gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C | 2 +- .../g++.dg/cpp1y/constexpr-lifetime1.C | 13 ++++ .../g++.dg/cpp1y/constexpr-lifetime2.C | 20 ++++++ .../g++.dg/cpp1y/constexpr-lifetime3.C | 13 ++++ .../g++.dg/cpp1y/constexpr-lifetime4.C | 11 ++++ .../g++.dg/cpp1y/constexpr-lifetime5.C | 11 ++++ gcc/tree-core.h | 5 +- gcc/tree.h | 6 ++ 11 files changed, 138 insertions(+), 15 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime1.C create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime2.C create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime3.C create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime4.C create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 3de60cfd0f8..28e3f891fb8 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -1185,10 +1185,17 @@ public: void put_value (tree t, tree v) { bool already_in_map = values.put (t, v); + if (!already_in_map && DECL_P (t)) + DECL_EXPIRED (t) = false; if (!already_in_map && modifiable) modifiable->add (t); } - void remove_value (tree t) { values.remove (t); } + void remove_value (tree t) + { + if (DECL_P (t)) + DECL_EXPIRED (t) = true; + values.remove (t); + } }; /* Helper class for constexpr_global_ctx. In some cases we want to avoid @@ -3157,10 +3164,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, for (tree save_expr : save_exprs) ctx->global->remove_value (save_expr); - /* Remove the parms/result from the values map. Is it worth - bothering to do this when the map itself is only live for - one constexpr evaluation? If so, maybe also clear out - other vars from call, maybe in BIND_EXPR handling? */ + /* Remove the parms/result from the values map. */ ctx->global->remove_value (res); for (tree parm = parms; parm; parm = TREE_CHAIN (parm)) ctx->global->remove_value (parm); @@ -5708,6 +5712,13 @@ non_const_var_error (location_t loc, tree r, bool fundef_p) inform (DECL_SOURCE_LOCATION (r), "allocated here"); return; } + if (DECL_EXPIRED (r)) + { + if (constexpr_error (loc, fundef_p, "accessing object outside its " + "lifetime")) + inform (DECL_SOURCE_LOCATION (r), "declared here"); + return; + } if (!constexpr_error (loc, fundef_p, "the value of %qD is not usable in " "a constant expression", r)) return; @@ -6995,7 +7006,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, else { if (!ctx->quiet) - error ("%qE is not a constant expression", t); + non_const_var_error (loc, t, /*fundef_p*/false); *non_constant_p = true; } break; @@ -7048,6 +7059,13 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, r = build_constructor (TREE_TYPE (t), NULL); TREE_CONSTANT (r) = true; } + else if (DECL_EXPIRED (t)) + { + if (!ctx->quiet) + non_const_var_error (loc, r, /*fundef_p*/false); + *non_constant_p = true; + break; + } else if (ctx->strict) r = decl_really_constant_value (t, /*unshare_p=*/false); else @@ -7093,7 +7111,15 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, else { if (!ctx->quiet) - error ("%qE is not a constant expression", t); + { + if (DECL_EXPIRED (r)) + { + error_at (loc, "accessing object outside its lifetime"); + inform (DECL_SOURCE_LOCATION (r), "declared here"); + } + else + error_at (loc, "%qE is not a constant expression", t); + } *non_constant_p = true; } break; @@ -7315,17 +7341,28 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, auto_vec cleanups; vec *prev_cleanups = ctx->global->cleanups; ctx->global->cleanups = &cleanups; - r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), + + auto_vec save_exprs; + constexpr_ctx new_ctx = *ctx; + new_ctx.save_exprs = &save_exprs; + + r = cxx_eval_constant_expression (&new_ctx, TREE_OPERAND (t, 0), lval, non_constant_p, overflow_p, jump_target); + ctx->global->cleanups = prev_cleanups; unsigned int i; tree cleanup; /* Evaluate the cleanups. */ FOR_EACH_VEC_ELT_REVERSE (cleanups, i, cleanup) - cxx_eval_constant_expression (ctx, cleanup, vc_discard, + cxx_eval_constant_expression (&new_ctx, cleanup, vc_discard, non_constant_p, overflow_p); + + /* Forget SAVE_EXPRs and TARGET_EXPRs created by this + full-expression. */ + for (tree save_expr : save_exprs) + ctx->global->remove_value (save_expr); } break; @@ -7831,10 +7868,13 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, non_constant_p, overflow_p, jump_target); case BIND_EXPR: - return cxx_eval_constant_expression (ctx, BIND_EXPR_BODY (t), - lval, - non_constant_p, overflow_p, - jump_target); + r = cxx_eval_constant_expression (ctx, BIND_EXPR_BODY (t), + lval, + non_constant_p, overflow_p, + jump_target); + for (tree decl = BIND_EXPR_VARS (t); decl; decl = DECL_CHAIN (decl)) + ctx->global->remove_value (decl); + return r; case PREINCREMENT_EXPR: case POSTINCREMENT_EXPR: diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index ac2fe66b080..e8b001424a0 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -5435,6 +5435,7 @@ trees_out::core_bools (tree t) WB (t->decl_common.decl_read_flag); WB (t->decl_common.decl_nonshareable_flag); WB (t->decl_common.decl_not_flexarray); + WB (t->decl_common.decl_expired_flag); } if (CODE_CONTAINS_STRUCT (code, TS_DECL_WITH_VIS)) @@ -5580,6 +5581,7 @@ trees_in::core_bools (tree t) RB (t->decl_common.decl_read_flag); RB (t->decl_common.decl_nonshareable_flag); RB (t->decl_common.decl_not_flexarray); + RB (t->decl_common.decl_expired_flag); } if (CODE_CONTAINS_STRUCT (code, TS_DECL_WITH_VIS)) diff --git a/gcc/print-tree.cc b/gcc/print-tree.cc index 1f3afcbbc86..ce15f4508d7 100644 --- a/gcc/print-tree.cc +++ b/gcc/print-tree.cc @@ -495,6 +495,10 @@ print_node (FILE *file, const char *prefix, tree node, int indent, && DECL_BY_REFERENCE (node)) fputs (" passed-by-reference", file); + if ((code == VAR_DECL || code == PARM_DECL || code == RESULT_DECL) + && DECL_EXPIRED (node)) + fputs (" expired", file); + if (CODE_CONTAINS_STRUCT (code, TS_DECL_WITH_VIS) && DECL_DEFER_OUTPUT (node)) fputs (" defer-output", file); diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C index e2d4853a284..ebaa95e5324 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C @@ -4,4 +4,4 @@ typedef bool (*Function)(int); constexpr bool check(int x, Function p) { return p(x); } // { dg-message "in .constexpr. expansion of" } -static_assert(check(2, check), ""); // { dg-error "conversion|constant|in .constexpr. expansion of" } +static_assert(check(2, check), ""); // { dg-error "conversion|constant|lifetime|in .constexpr. expansion of" } diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime1.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime1.C new file mode 100644 index 00000000000..43aa7c974c1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime1.C @@ -0,0 +1,13 @@ +// PR c++/96630 +// { dg-do compile { target c++14 } } + +struct S { + int x = 0; + constexpr const int& get() const { return x; } +}; + +constexpr const int& test() { + auto local = S{}; // { dg-message "note: declared here" } + return local.get(); +} +constexpr int x = test(); // { dg-error "accessing object outside its lifetime" } diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime2.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime2.C new file mode 100644 index 00000000000..22cd919fcda --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime2.C @@ -0,0 +1,20 @@ +// PR c++/98675 +// { dg-do compile { target c++14 } } + +struct S { + int x = 0; + constexpr const int& get() const { return x; } +}; + +constexpr int error() { + const auto& local = S{}.get(); // { dg-message "note: declared here" } + return local; +} +constexpr int x = error(); // { dg-error "accessing object outside its lifetime" } + +constexpr int ok() { + // temporary should only be destroyed after end of full-expression + auto local = S{}.get(); + return local; +} +constexpr int y = ok(); diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime3.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime3.C new file mode 100644 index 00000000000..6329f8cf6c6 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime3.C @@ -0,0 +1,13 @@ +// PR c++/70331 +// { dg-do compile { target c++14 } } + +constexpr int f(int i) { + int *p = &i; + if (i == 0) { + int j = 123; // { dg-message "note: declared here" } + p = &j; + } + return *p; +} + +constexpr int i = f(0); // { dg-error "accessing object outside its lifetime" } diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime4.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime4.C new file mode 100644 index 00000000000..181a1201663 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime4.C @@ -0,0 +1,11 @@ +// { dg-do compile { target c++14 } } + +constexpr const double& test() { + const double& local = 3.0; // { dg-message "note: declared here" } + return local; +} + +static_assert(test() == 3.0, ""); // { dg-error "constant|accessing object outside its lifetime" } + +// no deference, shouldn't error +static_assert((test(), true), ""); diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C new file mode 100644 index 00000000000..a4bc71d890a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C @@ -0,0 +1,11 @@ +// { dg-do compile { target c++14 } } +// { dg-options "-Wno-return-local-addr" } + +constexpr const int& id(int x) { return x; } + +constexpr bool test() { + const int& y = id(3); + return y == 3; +} + +constexpr bool x = test(); // { dg-error "" } diff --git a/gcc/tree-core.h b/gcc/tree-core.h index fd2be57b78c..f814b427a38 100644 --- a/gcc/tree-core.h +++ b/gcc/tree-core.h @@ -1834,7 +1834,10 @@ struct GTY(()) tree_decl_common { /* In FIELD_DECL, this is DECL_NOT_FLEXARRAY. */ unsigned int decl_not_flexarray : 1; - /* 13 bits unused. */ + /* In VAR_DECL, PARM_DECL, or RESULT_DECL, this is DECL_EXPIRED. */ + unsigned int decl_expired_flag : 1; + + /* 12 bits unused. */ /* UID for points-to sets, stable over copying from inlining. */ unsigned int pt_uid; diff --git a/gcc/tree.h b/gcc/tree.h index 91375f9652f..194d49ed1f7 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -2697,6 +2697,12 @@ extern tree vector_element_bits_tree (const_tree); ??? Need to figure out some way to check this isn't a PARM_DECL. */ #define DECL_INITIAL(NODE) (DECL_COMMON_CHECK (NODE)->decl_common.initial) +/* Used in a VAR_DECL, PARM_DECL, or RESULT_DECL to indicate whether + this declaration is currently in lifetime for constant evaluation + purposes. */ +#define DECL_EXPIRED(NODE) \ + (DECL_COMMON_CHECK(NODE)->decl_common.decl_expired_flag) + /* Holds the size of the datum, in bits, as a tree expression. Need not be constant and may be null. May be less than TYPE_SIZE for a C++ FIELD_DECL representing a base class subobject with its From patchwork Tue Mar 28 11:32:29 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nathaniel Shead X-Patchwork-Id: 67005 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 989AF3850208 for ; Tue, 28 Mar 2023 11:34:46 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 989AF3850208 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1680003286; bh=on6EJVT8JRdKCBbK6+vcjXgEic7lAUzwm3OxazHxFQ4=; h=To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=EIlm4YTtCrSBNoDIRk9Qfd5jfdMUGHUzRMWQry83d0UI2u82QbN8RqYBf3DZktTff xtspwy/9qclt0x/M7532ocgziCK2kJy2ZkUf8GPLq6B3aYAcCQB3bAnWMF7ZnIGVOB UTqa94iBe8quPUb/Tq4qn9oGnMBKhhl9KLw1Tfik= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-pf1-x42f.google.com (mail-pf1-x42f.google.com [IPv6:2607:f8b0:4864:20::42f]) by sourceware.org (Postfix) with ESMTPS id 63E693858C53 for ; Tue, 28 Mar 2023 11:33:11 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 63E693858C53 Received: by mail-pf1-x42f.google.com with SMTP id cm5so1922378pfb.0 for ; Tue, 28 Mar 2023 04:33:11 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680003190; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=on6EJVT8JRdKCBbK6+vcjXgEic7lAUzwm3OxazHxFQ4=; b=ZCXSQmySvSqYBGqM+gqxQTeh6Blb5oaqwNcI3Fi5BEfjQB180LAgEVxunClwK65ACx q/PUKxi5E7wNP0sGMFzqeQJcfaNXKr0yOakQ0AaLwXTwX9/KVeEjvYxVpNYCGWjT/19p dSO6j7rvklY9kO4S/ZyzJeLBFps72kwmzs8sgqdoZQ2+wYNyH28P2AMgnYJgJ7hAuFc9 2Fa1V7FeVq4pnh9Ttj2RsFo5XayXSHOtsYXEDFtm3IFIPmGgHH9MaKYp48SmyC5AlqIC wg2Q5vO2+YjeMGIFYMBxAeVUb3uI6FPrFlDmQaEWggr1qxstMedH6f4PG0bMhsdKikZx v3dw== X-Gm-Message-State: AAQBX9ed8sdHci7+p9BnqVeaNCN/GWG17Ys9SzBji0yyOgZzuya8QKfm M1Iaj1w0i+fp1ovE4aZWmbTl4P0tyeo= X-Google-Smtp-Source: AKy350brUUhPUCzLmzhoOgVOe4foZxBxKtTQLBuhcWbPiFuqHaruANd/zok0VcQigh5lt5ERjz18zQ== X-Received: by 2002:aa7:954a:0:b0:625:cb74:9e01 with SMTP id w10-20020aa7954a000000b00625cb749e01mr14710413pfq.25.1680003190156; Tue, 28 Mar 2023 04:33:10 -0700 (PDT) Received: from Thaum.localdomain (125-209-144-50.tpgi.com.au. [125.209.144.50]) by smtp.gmail.com with ESMTPSA id e17-20020a62ee11000000b0062ce765b7afsm6097512pfi.162.2023.03.28.04.33.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 28 Mar 2023 04:33:09 -0700 (PDT) To: gcc-patches@gcc.gnu.org Cc: Nathaniel Shead Subject: [PATCH 2/3] c++: Improve constexpr error for dangling local variables Date: Tue, 28 Mar 2023 22:32:29 +1100 Message-Id: <20230328113230.19975-3-nathanieloshead@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230328113230.19975-1-nathanieloshead@gmail.com> References: <20230328113230.19975-1-nathanieloshead@gmail.com> MIME-Version: 1.0 X-Spam-Status: No, score=-12.6 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Nathaniel Shead via Gcc-patches From: Nathaniel Shead Reply-To: Nathaniel Shead Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" Currently, when typeck discovers that a return statement will refer to a local variable it rewrites to return a null pointer. This causes the error messages for using the return value in a constant expression to be unhelpful, especially for reference return values. This patch removes this "optimisation". Relying on this raises a warning by default and causes UB anyway, so there should be no issue in doing so. We also suppress additional warnings from later passes that detect this as a dangling pointer, since we've already indicated this anyway. gcc/cp/ChangeLog: * semantics.cc (finish_return_stmt): Suppress dangling pointer reporting on return statement if already reported. * typeck.cc (check_return_expr): Don't set return expression to zero for dangling addresses. gcc/testsuite/ChangeLog: * g++.dg/cpp1y/constexpr-lifetime5.C: Test reported message is correct. * g++.dg/warn/Wreturn-local-addr-6.C: Remove check for return value optimisation. Signed-off-by: Nathaniel Shead --- gcc/cp/semantics.cc | 5 ++++- gcc/cp/typeck.cc | 5 +++-- gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C | 4 ++-- gcc/testsuite/g++.dg/warn/Wreturn-local-addr-6.C | 3 --- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 87c2e8a7111..14b4b7f4ce1 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -1246,7 +1246,10 @@ finish_return_stmt (tree expr) r = build_stmt (input_location, RETURN_EXPR, expr); if (no_warning) - suppress_warning (r, OPT_Wreturn_type); + { + suppress_warning (r, OPT_Wreturn_type); + suppress_warning (r, OPT_Wdangling_pointer_); + } r = maybe_cleanup_point_expr_void (r); r = add_stmt (r); diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc index afb956087ce..a7d642e2029 100644 --- a/gcc/cp/typeck.cc +++ b/gcc/cp/typeck.cc @@ -11235,8 +11235,9 @@ check_return_expr (tree retval, bool *no_warning) else if (!processing_template_decl && maybe_warn_about_returning_address_of_local (retval, loc) && INDIRECT_TYPE_P (valtype)) - retval = build2 (COMPOUND_EXPR, TREE_TYPE (retval), retval, - build_zero_cst (TREE_TYPE (retval))); + /* Suppress the Wdangling-pointer warning in the return statement + that would otherwise occur. */ + *no_warning = true; } if (processing_template_decl) diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C index a4bc71d890a..ad3ef579f63 100644 --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C @@ -1,11 +1,11 @@ // { dg-do compile { target c++14 } } // { dg-options "-Wno-return-local-addr" } -constexpr const int& id(int x) { return x; } +constexpr const int& id(int x) { return x; } // { dg-message "note: declared here" } constexpr bool test() { const int& y = id(3); return y == 3; } -constexpr bool x = test(); // { dg-error "" } +constexpr bool x = test(); // { dg-error "accessing object outside its lifetime" } diff --git a/gcc/testsuite/g++.dg/warn/Wreturn-local-addr-6.C b/gcc/testsuite/g++.dg/warn/Wreturn-local-addr-6.C index fae8b7e766f..ec8e241d83e 100644 --- a/gcc/testsuite/g++.dg/warn/Wreturn-local-addr-6.C +++ b/gcc/testsuite/g++.dg/warn/Wreturn-local-addr-6.C @@ -24,6 +24,3 @@ return_addr_local_as_intref (void) return (const intptr_t&)a; // { dg-warning "\\\[-Wreturn-local-addr]" } */ } - -/* Verify that the return value has been replaced with zero: - { dg-final { scan-tree-dump-times "return 0;" 2 "optimized" } } */ From patchwork Tue Mar 28 11:32:30 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nathaniel Shead X-Patchwork-Id: 67003 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 9BE41385B510 for ; Tue, 28 Mar 2023 11:33:48 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 9BE41385B510 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1680003228; bh=vleFHMmV3itUzzf8kBbGtec+ScuqkLd/sn2EK9zGhp8=; h=To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=E8pYN6kZJ/4EddYKWvpEfnnGPL7ZK+5BJ8q0itmabhIeakKtNtHVQWrwlj32HIfdc c6CrafA8LSdESvig8auPR5l+RmdEINHlB+c7AxjiJ3vVLMYJ5gyeQ89quVSwElT76I 24IQZCJ/qpLpP297XtApB8cMyFwmld9LSYEK0qQE= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-pj1-x1031.google.com (mail-pj1-x1031.google.com [IPv6:2607:f8b0:4864:20::1031]) by sourceware.org (Postfix) with ESMTPS id E87C23858C54 for ; Tue, 28 Mar 2023 11:33:13 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org E87C23858C54 Received: by mail-pj1-x1031.google.com with SMTP id qe8-20020a17090b4f8800b0023f07253a2cso12141278pjb.3 for ; Tue, 28 Mar 2023 04:33:13 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680003192; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=vleFHMmV3itUzzf8kBbGtec+ScuqkLd/sn2EK9zGhp8=; b=S2egZVyVSMBcNnLAhn827VCpfHrhRrCb4xywRi8c2ojrJD17/pO2cHPp+CP9hnB+Ap nad6R3BOTbLJeB1BXeNPcD3OJ+n7zrnAKig2f87YSdIliucdChm4huFw0Bu0teJDa4rp x84/dvwNpb8FtdRR+rmxk6eRbR3xM9GUMtXTg+7bsNGl0vxLYO3/tNUOwqvdiCzLmWl7 /sS6iXdwS0LZNzoxhWWG+XcW+P8tmmwPRcTsKxMIED53TSux9ZFtkbYfonz1Enj7xqnG tajGv4AFhOhFkX+C3oqVWAd8lUh9nUkEbHRmJ7WaFt5FYm5ELfTaWM9kC9Enjkb/Iqoj dXFQ== X-Gm-Message-State: AO0yUKVg7PXBTcOQBId23XmGRJFbnZ4im3dI88XPFtDaPog7ueHdvtIr yoaKsVyy5VRt/YZuHva/vVK05dEFm9Y= X-Google-Smtp-Source: AK7set+H8RAP36pAgb/X/TB+rGq7PdSogcLe43etRSPQG/7Rk2zJRY4E3pqwxNCh42XIwiEAQkoeYQ== X-Received: by 2002:a05:6a20:38a4:b0:d9:adc3:6a71 with SMTP id n36-20020a056a2038a400b000d9adc36a71mr14304271pzf.1.1680003192037; Tue, 28 Mar 2023 04:33:12 -0700 (PDT) Received: from Thaum.localdomain (125-209-144-50.tpgi.com.au. [125.209.144.50]) by smtp.gmail.com with ESMTPSA id e17-20020a62ee11000000b0062ce765b7afsm6097512pfi.162.2023.03.28.04.33.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 28 Mar 2023 04:33:11 -0700 (PDT) To: gcc-patches@gcc.gnu.org Cc: Nathaniel Shead Subject: [PATCH 3/3] c++: Improve location information in constexpr evaluation Date: Tue, 28 Mar 2023 22:32:30 +1100 Message-Id: <20230328113230.19975-4-nathanieloshead@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230328113230.19975-1-nathanieloshead@gmail.com> References: <20230328113230.19975-1-nathanieloshead@gmail.com> MIME-Version: 1.0 X-Spam-Status: No, score=-12.6 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Nathaniel Shead via Gcc-patches From: Nathaniel Shead Reply-To: Nathaniel Shead Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" This patch caches the current expression's location information in the constexpr_global_ctx struct, which allows subexpressions that have lost location information to still provide accurate diagnostics. Also rewrites a number of 'error' calls as 'error_at' to provide more specific location information. The primary effect of this change is that many errors within evaluation of a constexpr function will now point at the offending expression (with expansion tracing information) rather than just the outermost call. gcc/cp/ChangeLog: * constexpr.cc (constexpr_global_ctx): New field for cached tree location, defaulting to input_location. (cxx_eval_internal_function): Fall back to ctx->global->loc rather than input_location. (modifying_const_object_error): Likewise. (cxx_eval_dynamic_cast_fn): Likewise. (eval_and_check_array_index): Likewise. (cxx_eval_array_reference): Likewise. (cxx_eval_bit_field_ref): Likewise. (cxx_eval_component_reference): Likewise. (cxx_eval_indirect_ref): Likewise. (cxx_eval_store_expression): Likewise. (cxx_eval_increment_expression): Likewise. (cxx_eval_loop_expr): Likewise. (cxx_eval_binary_expression): Likewise. (cxx_eval_constant_expression): Cache location of trees for use in errors, and prefer it instead of input_location. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/constexpr-48089.C: Updated diagnostic locations. * g++.dg/cpp0x/constexpr-diag3.C: Likewise. * g++.dg/cpp0x/constexpr-ice20.C: Likewise. * g++.dg/cpp1y/constexpr-89481.C: Likewise. * g++.dg/cpp1y/constexpr-lifetime1.C: Likewise. * g++.dg/cpp1y/constexpr-lifetime2.C: Likewise. * g++.dg/cpp1y/constexpr-lifetime3.C: Likewise. * g++.dg/cpp1y/constexpr-lifetime4.C: Likewise. * g++.dg/cpp1y/constexpr-lifetime5.C: Likewise. * g++.dg/cpp1y/constexpr-union5.C: Likewise. * g++.dg/cpp1y/pr68180.C: Likewise. * g++.dg/cpp1z/constexpr-lambda6.C: Likewise. * g++.dg/cpp2a/bit-cast11.C: Likewise. * g++.dg/cpp2a/bit-cast12.C: Likewise. * g++.dg/cpp2a/bit-cast14.C: Likewise. * g++.dg/cpp2a/constexpr-98122.C: Likewise. * g++.dg/cpp2a/constexpr-dynamic17.C: Likewise. * g++.dg/cpp2a/constexpr-init1.C: Likewise. * g++.dg/cpp2a/constexpr-new12.C: Likewise. * g++.dg/cpp2a/constexpr-new3.C: Likewise. * g++.dg/ext/constexpr-vla2.C: Likewise. * g++.dg/ext/constexpr-vla3.C: Likewise. * g++.dg/ubsan/pr63956.C: Likewise. libstdc++/ChangeLog: * testsuite/25_algorithms/equal/constexpr_neg.cc: Updated diagnostics locations. Signed-off-by: Nathaniel Shead --- gcc/cp/constexpr.cc | 83 +++++++++++-------- gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C | 10 +-- gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C | 2 +- gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C | 4 +- gcc/testsuite/g++.dg/cpp1y/constexpr-89481.C | 3 +- .../g++.dg/cpp1y/constexpr-lifetime1.C | 1 + .../g++.dg/cpp1y/constexpr-lifetime2.C | 4 +- .../g++.dg/cpp1y/constexpr-lifetime3.C | 4 +- .../g++.dg/cpp1y/constexpr-lifetime4.C | 2 +- .../g++.dg/cpp1y/constexpr-lifetime5.C | 4 +- gcc/testsuite/g++.dg/cpp1y/constexpr-union5.C | 4 +- gcc/testsuite/g++.dg/cpp1y/pr68180.C | 4 +- .../g++.dg/cpp1z/constexpr-lambda6.C | 4 +- gcc/testsuite/g++.dg/cpp2a/bit-cast11.C | 10 +-- gcc/testsuite/g++.dg/cpp2a/bit-cast12.C | 10 +-- gcc/testsuite/g++.dg/cpp2a/bit-cast14.C | 14 ++-- gcc/testsuite/g++.dg/cpp2a/constexpr-98122.C | 4 +- .../g++.dg/cpp2a/constexpr-dynamic17.C | 5 +- gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C | 5 +- gcc/testsuite/g++.dg/cpp2a/constexpr-new12.C | 6 +- gcc/testsuite/g++.dg/cpp2a/constexpr-new3.C | 10 +-- gcc/testsuite/g++.dg/ext/constexpr-vla2.C | 4 +- gcc/testsuite/g++.dg/ext/constexpr-vla3.C | 4 +- gcc/testsuite/g++.dg/ubsan/pr63956.C | 4 +- .../25_algorithms/equal/constexpr_neg.cc | 7 +- 25 files changed, 111 insertions(+), 101 deletions(-) diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 28e3f891fb8..31884220b4e 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -1165,10 +1165,12 @@ public: hash_set *modifiable; /* Number of heap VAR_DECL deallocations. */ unsigned heap_dealloc_count; + /* Current location in case subtree has no location information. */ + location_t loc; /* Constructor. */ constexpr_global_ctx () : constexpr_ops_count (0), cleanups (NULL), modifiable (nullptr), - heap_dealloc_count (0) {} + heap_dealloc_count (0), loc (input_location) {} tree get_value (tree t) { @@ -2108,7 +2110,7 @@ cxx_eval_internal_function (const constexpr_ctx *ctx, tree t, default: if (!ctx->quiet) - error_at (cp_expr_loc_or_input_loc (t), + error_at (cp_expr_loc_or_loc (t, ctx->global->loc), "call to internal function %qE", t); *non_constant_p = true; return t; @@ -2123,7 +2125,7 @@ cxx_eval_internal_function (const constexpr_ctx *ctx, tree t, if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST) { - location_t loc = cp_expr_loc_or_input_loc (t); + location_t loc = cp_expr_loc_or_loc (t, ctx->global->loc); tree type = TREE_TYPE (TREE_TYPE (t)); tree result = fold_binary_loc (loc, opcode, type, fold_convert_loc (loc, type, arg0), @@ -2159,9 +2161,9 @@ clear_no_implicit_zero (tree ctor) EXPR is the MODIFY_EXPR expression performing the modification. */ static void -modifying_const_object_error (tree expr, tree obj) +modifying_const_object_error (const constexpr_ctx* ctx, tree expr, tree obj) { - location_t loc = cp_expr_loc_or_input_loc (expr); + location_t loc = cp_expr_loc_or_loc (expr, ctx->global->loc); auto_diagnostic_group d; error_at (loc, "modifying a const object %qE is not allowed in " "a constant expression", TREE_OPERAND (expr, 0)); @@ -2353,7 +2355,7 @@ cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call, tree obj = CALL_EXPR_ARG (call, 0); tree type = CALL_EXPR_ARG (call, 2); HOST_WIDE_INT hint = int_cst_value (CALL_EXPR_ARG (call, 3)); - location_t loc = cp_expr_loc_or_input_loc (call); + location_t loc = cp_expr_loc_or_loc (call, ctx->global->loc); /* Get the target type of the dynamic_cast. */ gcc_assert (TREE_CODE (type) == ADDR_EXPR); @@ -3651,7 +3653,7 @@ cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t, && integer_zerop (lhs) && !integer_zerop (rhs)) { if (!ctx->quiet) - error ("arithmetic involving a null pointer in %qE", lhs); + error_at (loc, "arithmetic involving a null pointer in %qE", lhs); *non_constant_p = true; return t; } @@ -4144,7 +4146,7 @@ eval_and_check_array_index (const constexpr_ctx *ctx, tree t, bool allow_one_past, bool *non_constant_p, bool *overflow_p) { - location_t loc = cp_expr_loc_or_input_loc (t); + location_t loc = cp_expr_loc_or_loc (t, ctx->global->loc); tree ary = TREE_OPERAND (t, 0); t = TREE_OPERAND (t, 1); tree index = cxx_eval_constant_expression (ctx, t, vc_prvalue, @@ -4182,6 +4184,7 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t, value_cat lval, bool *non_constant_p, bool *overflow_p) { + location_t loc = cp_expr_loc_or_loc (t, ctx->global->loc); tree oldary = TREE_OPERAND (t, 0); tree ary = cxx_eval_constant_expression (ctx, oldary, lval, @@ -4269,7 +4272,7 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t, building; if there's no initializer for this element yet, that's an error. */ if (!ctx->quiet) - error ("accessing uninitialized array element"); + error_at (loc, "accessing uninitialized array element"); *non_constant_p = true; return t; } @@ -4318,13 +4321,14 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t, tree whole = cxx_eval_constant_expression (ctx, orig_whole, lval, non_constant_p, overflow_p); + location_t loc = cp_expr_loc_or_loc (whole, ctx->global->loc); if (*non_constant_p) return t; if (INDIRECT_REF_P (whole) && integer_zerop (TREE_OPERAND (whole, 0))) { if (!ctx->quiet) - error ("dereferencing a null pointer in %qE", orig_whole); + error_at (loc, "dereferencing a null pointer in %qE", orig_whole); *non_constant_p = true; return t; } @@ -4343,7 +4347,7 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t, if (TREE_CODE (whole) != CONSTRUCTOR) { if (!ctx->quiet) - error ("%qE is not a constant expression", orig_whole); + error_at (loc, "%qE is not a constant expression", orig_whole); *non_constant_p = true; return t; } @@ -4351,7 +4355,7 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t, && DECL_MUTABLE_P (part)) { if (!ctx->quiet) - error ("mutable %qD is not usable in a constant expression", part); + error_at (loc, "mutable %qD is not usable in a constant expression", part); *non_constant_p = true; return t; } @@ -4381,10 +4385,10 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t, { constructor_elt *cep = CONSTRUCTOR_ELT (whole, 0); if (cep->value == NULL_TREE) - error ("accessing uninitialized member %qD", part); + error_at (loc, "accessing uninitialized member %qD", part); else - error ("accessing %qD member instead of initialized %qD member in " - "constant expression", part, cep->index); + error_at (loc, "accessing %qD member instead of initialized %qD member " + "in constant expression", part, cep->index); } *non_constant_p = true; return t; @@ -4403,7 +4407,7 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t, building; if there's no initializer for this member yet, that's an error. */ if (!ctx->quiet) - error ("accessing uninitialized member %qD", part); + error_at (loc, "accessing uninitialized member %qD", part); *non_constant_p = true; return t; } @@ -4431,6 +4435,7 @@ cxx_eval_bit_field_ref (const constexpr_ctx *ctx, tree t, tree whole = cxx_eval_constant_expression (ctx, orig_whole, lval, non_constant_p, overflow_p); + location_t loc = cp_expr_loc_or_loc (t, ctx->global->loc); tree start, field, value; unsigned HOST_WIDE_INT i; @@ -4443,7 +4448,7 @@ cxx_eval_bit_field_ref (const constexpr_ctx *ctx, tree t, && TREE_CODE (whole) != CONSTRUCTOR) { if (!ctx->quiet) - error ("%qE is not a constant expression", orig_whole); + error_at (loc, "%qE is not a constant expression", orig_whole); *non_constant_p = true; } if (*non_constant_p) @@ -4455,7 +4460,7 @@ cxx_eval_bit_field_ref (const constexpr_ctx *ctx, tree t, TREE_OPERAND (t, 1), TREE_OPERAND (t, 2))) return r; if (!ctx->quiet) - error ("%qE is not a constant expression", orig_whole); + error_at (loc, "%qE is not a constant expression", orig_whole); *non_constant_p = true; return t; } @@ -5604,6 +5609,7 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t, value_cat lval, bool *non_constant_p, bool *overflow_p) { + location_t loc = cp_expr_loc_or_loc (t, ctx->global->loc); tree orig_op0 = TREE_OPERAND (t, 0); bool empty_base = false; @@ -5634,7 +5640,7 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t, if (!lval && integer_zerop (op0)) { if (!ctx->quiet) - error ("dereferencing a null pointer"); + error_at (loc, "dereferencing a null pointer"); *non_constant_p = true; return t; } @@ -5653,8 +5659,7 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t, (TREE_TYPE (TREE_TYPE (sub)), TREE_TYPE (t))); /* DR 1188 says we don't have to deal with this. */ if (!ctx->quiet) - error_at (cp_expr_loc_or_input_loc (t), - "accessing value of %qE through a %qT glvalue in a " + error_at (loc, "accessing value of %qE through a %qT glvalue in a " "constant expression", build_fold_indirect_ref (sub), TREE_TYPE (t)); *non_constant_p = true; @@ -5901,6 +5906,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, value_cat lval, bool *non_constant_p, bool *overflow_p) { + location_t loc = cp_expr_loc_or_loc (t, ctx->global->loc); constexpr_ctx new_ctx = *ctx; tree init = TREE_OPERAND (t, 1); @@ -6025,7 +6031,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, /* A constant-expression cannot modify objects from outside the constant-expression. */ if (!ctx->quiet) - error ("modification of %qE is not a constant expression", object); + error_at (loc, "modification of %qE is not a constant expression", object); *non_constant_p = true; return t; } @@ -6123,7 +6129,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, if (cxx_dialect < cxx20) { if (!ctx->quiet) - error_at (cp_expr_loc_or_input_loc (t), + error_at (cp_expr_loc_or_loc (t, ctx->global->loc), "change of the active member of a union " "from %qD to %qD", CONSTRUCTOR_ELT (*valp, 0)->index, @@ -6136,7 +6142,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, /* Diagnose changing the active union member while the union is in the process of being initialized. */ if (!ctx->quiet) - error_at (cp_expr_loc_or_input_loc (t), + error_at (cp_expr_loc_or_loc (t, ctx->global->loc), "change of the active member of a union " "from %qD to %qD during initialization", CONSTRUCTOR_ELT (*valp, 0)->index, @@ -6219,7 +6225,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, if (fail) { if (!ctx->quiet) - modifying_const_object_error (t, const_object_being_modified); + modifying_const_object_error (ctx, t, const_object_being_modified); *non_constant_p = true; return t; } @@ -6376,6 +6382,8 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t, tree offset = TREE_OPERAND (t, 1); gcc_assert (TREE_CONSTANT (offset)); + location_t loc = cp_expr_loc_or_loc (t, ctx->global->loc); + /* OFFSET is constant, but perhaps not constant enough. We need to e.g. bash FLOAT_EXPRs to REAL_CSTs. */ offset = fold_simple (offset); @@ -6423,8 +6431,7 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t, VERIFY_CONSTANT (mod); /* Storing the modified value. */ - tree store = build2_loc (cp_expr_loc_or_loc (t, input_location), - MODIFY_EXPR, type, op, mod); + tree store = build2_loc (loc, MODIFY_EXPR, type, op, mod); mod = cxx_eval_constant_expression (ctx, store, lval, non_constant_p, overflow_p); ggc_free (store); @@ -6597,6 +6604,7 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t, bool *non_constant_p, bool *overflow_p, tree *jump_target) { + location_t loc = cp_expr_loc_or_loc (t, ctx->global->loc); constexpr_ctx new_ctx = *ctx; tree local_target; if (!jump_target) @@ -6686,7 +6694,7 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t, if (++count >= constexpr_loop_limit) { if (!ctx->quiet) - error_at (cp_expr_loc_or_input_loc (t), + error_at (loc, "% loop iteration count exceeds limit of %d " "(use %<-fconstexpr-loop-limit=%> to increase the limit)", constexpr_loop_limit); @@ -6945,7 +6953,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, return t; } - location_t loc = cp_expr_loc_or_input_loc (t); + /* Track current location, propagating down from parent calls + in case this expression has no location information. */ + location_t loc = cp_expr_loc_or_loc (t, ctx->global->loc); + ctx->global->loc = loc; STRIP_ANY_LOCATION_WRAPPER (t); @@ -6968,8 +6979,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, && !integer_zerop (t)) { if (!ctx->quiet) - error ("value %qE of type %qT is not a constant expression", - t, TREE_TYPE (t)); + error_at (loc, "value %qE of type %qT is not a constant expression", + t, TREE_TYPE (t)); *non_constant_p = true; } @@ -7217,8 +7228,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, if (!ctx->quiet) { auto_diagnostic_group d; - error ("temporary of non-literal type %qT in a " - "constant expression", type); + error_at (loc, "temporary of non-literal type %qT in a " + "constant expression", type); explain_non_literal_class (type); } *non_constant_p = true; @@ -8020,8 +8031,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, if (function_concept_p (tmpl)) { if (!ctx->quiet) - error_at (cp_expr_loc_or_input_loc (t), - "function concept must be called"); + error_at (loc, "function concept must be called"); r = error_mark_node; break; } @@ -8116,6 +8126,9 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, break; } + /* Reset current location in case it was modified in child calls. */ + ctx->global->loc = loc; + if (r == error_mark_node) *non_constant_p = true; diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C index 4574eb83ff7..11630f26ffe 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C @@ -10,11 +10,11 @@ // R() is well-formed because i is initialized before j. struct s { - constexpr s() : v(v) { } + constexpr s() : v(v) { } // { dg-error "accessing uninitialized member" } int v; }; -constexpr s bang; // { dg-error "|" } +constexpr s bang; // { dg-message "in .constexpr. expansion" } struct R { int i,j; @@ -26,14 +26,14 @@ constexpr R r; // { dg-bogus "" } // Ill-formed (no diagnostic required) struct T { int i; - constexpr int f() { return i; } + constexpr int f() { return i; } // { dg-error "accessing uninitialized member" } constexpr T(): i(0) { } - constexpr T(const T& t) : i(f()) { } // { dg-message "" } + constexpr T(const T& t) : i(f()) { } // { dg-message "in .constexpr. expansion" } }; constexpr T t1; // Ill-formed (diagnostic required) -constexpr T t2(t1); // { dg-message "" } +constexpr T t2(t1); // { dg-message "in .constexpr. expansion" } // Well-formed struct U { diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C index 5eedf42ba36..50c676c56cd 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C @@ -16,7 +16,7 @@ int main() struct complex // { dg-message "no .constexpr. constructor" "" { target { ! implicit_constexpr } } } { complex(double r, double i) : re(r), im(i) { } - constexpr double real() const { return re; } // { dg-error "not a literal type" "" { target c++11_only } } + constexpr double real() const { return re; } // { dg-error "not a literal type|not usable in a constant expression" "" { target { ! implicit_constexpr } } } double imag() const { return im; } private: diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C index ebaa95e5324..e4e3bf865cd 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C @@ -2,6 +2,6 @@ // { dg-do compile { target c++11 } } typedef bool (*Function)(int); -constexpr bool check(int x, Function p) { return p(x); } // { dg-message "in .constexpr. expansion of" } +constexpr bool check(int x, Function p) { return p(x); } // { dg-error "lifetime" } -static_assert(check(2, check), ""); // { dg-error "conversion|constant|lifetime|in .constexpr. expansion of" } +static_assert(check(2, check), ""); // { dg-error "conversion|constant|in .constexpr. expansion of" } diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-89481.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-89481.C index 8ac4ef0fd36..6f8f6a8038e 100644 --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-89481.C +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-89481.C @@ -6,7 +6,7 @@ foo () { union U { long long a; int b[2]; } u { 5LL }; u.b[1] = 4; // { dg-error "change of the active member of a union from" "" { target c++17_down } } - return u.b[0]; + return u.b[0]; // { dg-error "accessing uninitialized array element" "" { target c++2a } } } constexpr int @@ -19,6 +19,5 @@ bar () static_assert (foo () == 0, ""); // { dg-error "non-constant condition for static assertion" } // { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1 } - // { dg-error "accessing uninitialized array element" "" { target c++2a } .-2 } static_assert (bar () == 4, ""); // { dg-error "non-constant condition for static assertion" "" { target c++17_down } } // { dg-message "in 'constexpr' expansion of" "" { target c++17_down } .-1 } diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime1.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime1.C index 43aa7c974c1..f79f1611d5f 100644 --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime1.C +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime1.C @@ -11,3 +11,4 @@ constexpr const int& test() { return local.get(); } constexpr int x = test(); // { dg-error "accessing object outside its lifetime" } + diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime2.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime2.C index 22cd919fcda..2f5ae8db6d5 100644 --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime2.C +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime2.C @@ -8,9 +8,9 @@ struct S { constexpr int error() { const auto& local = S{}.get(); // { dg-message "note: declared here" } - return local; + return local; // { dg-error "accessing object outside its lifetime" } } -constexpr int x = error(); // { dg-error "accessing object outside its lifetime" } +constexpr int x = error(); // { dg-message "in .constexpr. expansion" } constexpr int ok() { // temporary should only be destroyed after end of full-expression diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime3.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime3.C index 6329f8cf6c6..53785521d05 100644 --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime3.C +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime3.C @@ -7,7 +7,7 @@ constexpr int f(int i) { int j = 123; // { dg-message "note: declared here" } p = &j; } - return *p; + return *p; // { dg-error "accessing object outside its lifetime" } } -constexpr int i = f(0); // { dg-error "accessing object outside its lifetime" } +constexpr int i = f(0); // { dg-message "in .constexpr. expansion" } diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime4.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime4.C index 181a1201663..4302da1eddc 100644 --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime4.C +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime4.C @@ -5,7 +5,7 @@ constexpr const double& test() { return local; } -static_assert(test() == 3.0, ""); // { dg-error "constant|accessing object outside its lifetime" } +static_assert(test() == 3.0, ""); // { dg-error "non-constant condition|accessing object outside its lifetime" } // no deference, shouldn't error static_assert((test(), true), ""); diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C index ad3ef579f63..a12920c8fba 100644 --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C @@ -5,7 +5,7 @@ constexpr const int& id(int x) { return x; } // { dg-message "note: declared he constexpr bool test() { const int& y = id(3); - return y == 3; + return y == 3; // { dg-error "accessing object outside its lifetime" } } -constexpr bool x = test(); // { dg-error "accessing object outside its lifetime" } +constexpr bool x = test(); // { dg-message "in .constexpr. expansion" } diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-union5.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-union5.C index 55fe9fa2f0b..3d76345d564 100644 --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-union5.C +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-union5.C @@ -8,8 +8,8 @@ union U { }; constexpr int foo(U *up) { - up->a++; + up->a++; // { dg-error "accessing uninitialized member" } return {42}; } -extern constexpr U u = {}; // { dg-error "accessing uninitialized member" } +extern constexpr U u = {}; // { dg-message "in .constexpr. expansion" } diff --git a/gcc/testsuite/g++.dg/cpp1y/pr68180.C b/gcc/testsuite/g++.dg/cpp1y/pr68180.C index 9e6e5e984f9..8de1ef3936b 100644 --- a/gcc/testsuite/g++.dg/cpp1y/pr68180.C +++ b/gcc/testsuite/g++.dg/cpp1y/pr68180.C @@ -6,11 +6,11 @@ typedef float __attribute__( ( vector_size( 16 ) ) ) float32x4_t; constexpr float32x4_t fill(float x) { float32x4_t v{0}; constexpr auto vs = sizeof(v)/sizeof(v[0]); - for (auto i=0U; i(a); + dynamic_cast(a); // { dg-error "accessing uninitialized member" } } -constexpr D d; // { dg-error "accessing uninitialized member" } -// { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1 } +constexpr D d; // { dg-message "in 'constexpr' expansion of" } diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C index e56ecfed48a..b4e39b6f928 100644 --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C @@ -52,11 +52,10 @@ constexpr int fn5 () { struct S { int a = 9; int b; } s; - return s.b; + return s.b; // { dg-error "accessing uninitialized member" } } -constexpr int b = fn5 (); // { dg-error "accessing uninitialized member" } -// { dg-message "in .constexpr. expansion of" "" { target *-*-* } .-1 } +constexpr int b = fn5 (); // { dg-message "in .constexpr. expansion of" } constexpr int fn6 () diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new12.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new12.C index 5a3d06a5fab..832782e1427 100644 --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-new12.C +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-new12.C @@ -17,11 +17,11 @@ struct B : A { constexpr int foo () { - A *a = new B (); + A *a = new B (); // { dg-message "allocated here" } a->a = 4; delete a; - int r = a->foo (); + int r = a->foo (); // { dg-error "constant expression" } return r; } -constexpr auto a = foo (); // { dg-error "constant expression" } +constexpr auto a = foo (); // { dg-message "in .constexpr. expansion" } diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new3.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new3.C index 70b841208f8..3ba440fec53 100644 --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-new3.C +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-new3.C @@ -45,11 +45,10 @@ constexpr bool f5 () { int *p = new int; // { dg-message "allocated here" } - return *p == 1; + return *p == 1; // { dg-error "the content of uninitialized storage is not usable in a constant expression" } } -constexpr auto v5 = f5 (); // { dg-error "the content of uninitialized storage is not usable in a constant expression" } - // { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1 } +constexpr auto v5 = f5 (); // { dg-message "in 'constexpr' expansion of" } constexpr bool f6 () @@ -57,11 +56,10 @@ f6 () int *p = new int (2); // { dg-message "allocated here" } int *q = p; delete p; - return *q == 2; + return *q == 2; // { dg-error "use of allocated storage after deallocation in a constant expression" } } -constexpr auto v6 = f6 (); // { dg-error "use of allocated storage after deallocation in a constant expression" } - // { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1 } +constexpr auto v6 = f6 (); // { dg-message "in 'constexpr' expansion of" } constexpr int * f7 () diff --git a/gcc/testsuite/g++.dg/ext/constexpr-vla2.C b/gcc/testsuite/g++.dg/ext/constexpr-vla2.C index d4ea7c58c0d..e09a27af3de 100644 --- a/gcc/testsuite/g++.dg/ext/constexpr-vla2.C +++ b/gcc/testsuite/g++.dg/ext/constexpr-vla2.C @@ -4,7 +4,7 @@ constexpr int fn_bad (int n) { - __extension__ int a [n] = { 0 }; + __extension__ int a [n] = { 0 }; // { dg-error "array subscript" } int z = a [0] + (n ? fn_bad (n - 1) : 0); // { dg-message "in .constexpr. expansion of " } return z; } @@ -18,4 +18,4 @@ fn_ok (int n) } constexpr int i1 = fn_ok (3); -constexpr int i2 = fn_bad (3); // { dg-error "array subscript|in .constexpr. expansion of " } +constexpr int i2 = fn_bad (3); // { dg-message "in .constexpr. expansion of " } diff --git a/gcc/testsuite/g++.dg/ext/constexpr-vla3.C b/gcc/testsuite/g++.dg/ext/constexpr-vla3.C index 538b576a825..6f9daa1897f 100644 --- a/gcc/testsuite/g++.dg/ext/constexpr-vla3.C +++ b/gcc/testsuite/g++.dg/ext/constexpr-vla3.C @@ -4,11 +4,11 @@ constexpr int foo (int n) { - __extension__ int a[n] = { 1, 2, 3, 4, 5, 6 }; + __extension__ int a[n] = { 1, 2, 3, 4, 5, 6 }; // { dg-error "array subscript" } int z = 0; for (int i = 0; i <= n; ++i) z += a[i]; return z; } -constexpr int n = foo (3); // { dg-error "array subscript|in .constexpr. expansion of " } +constexpr int n = foo (3); // { dg-message "in .constexpr. expansion of " } diff --git a/gcc/testsuite/g++.dg/ubsan/pr63956.C b/gcc/testsuite/g++.dg/ubsan/pr63956.C index 3a1596e6e2e..0771732ef00 100644 --- a/gcc/testsuite/g++.dg/ubsan/pr63956.C +++ b/gcc/testsuite/g++.dg/ubsan/pr63956.C @@ -100,13 +100,13 @@ constexpr int fn7 (const int *a, int b) { if (b != 3) - return fn6 (*a, b); + return fn6 (*a, b); // { dg-error "null pointer" } return 7; } constexpr int n1 = 7; constexpr int n2 = fn7 (&n1, 5); -constexpr int n3 = fn7 ((const int *) 0, 8); // { dg-error "null pointer|in .constexpr. expansion of " } +constexpr int n3 = fn7 ((const int *) 0, 8); // { dg-message "in .constexpr. expansion of " } constexpr int fn8 (int i) diff --git a/libstdc++-v3/testsuite/25_algorithms/equal/constexpr_neg.cc b/libstdc++-v3/testsuite/25_algorithms/equal/constexpr_neg.cc index 34ca5c4805c..fd89ac0e166 100644 --- a/libstdc++-v3/testsuite/25_algorithms/equal/constexpr_neg.cc +++ b/libstdc++-v3/testsuite/25_algorithms/equal/constexpr_neg.cc @@ -32,7 +32,7 @@ test01() return outa; } -static_assert(test01()); // { dg-error "outside the bounds" } +static_assert(test01()); // { dg-error "non-constant condition" } constexpr bool test02() @@ -44,7 +44,8 @@ test02() return outa; } -static_assert(test02()); // { dg-error "outside the bounds" } +static_assert(test02()); // { dg-error "non-constant condition" } -// { dg-prune-output "non-constant condition" } +// Errors occuring within internals: +// { dg-error "outside the bounds of array" "" { target *-*-* } 0 } // { dg-prune-output "in 'constexpr'" }