From patchwork Sat Jul 22 15:15:14 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nathaniel Shead X-Patchwork-Id: 124332 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9010:0:b0:3e4:2afc:c1 with SMTP id l16csp855404vqg; Sat, 22 Jul 2023 08:16:34 -0700 (PDT) X-Google-Smtp-Source: APBJJlEC9LlwxIIoo1ejNJH/na6PAokk2uYt5/+1HH0lGmZ3MpTNXibgd2GJRD4jVaiv+ucRdYzU X-Received: by 2002:a05:6402:1514:b0:521:7ab6:b954 with SMTP id f20-20020a056402151400b005217ab6b954mr4194862edw.1.1690038994181; Sat, 22 Jul 2023 08:16:34 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1690038994; cv=none; d=google.com; s=arc-20160816; b=AS3BK31e8EsykQPzqMA71z5kcRCeLTOGlBKS+NDeDC0JHspOlaYEyw3TefLazEp3Ke cJRTPn4J03gS7i1v2Z/Hm4eCerVtdXjR+c19J5CRlMh8OdY5qCkkyOkMf7XvpWrfDime FV7rzrvpqqLbFr2vziTuK277E127s7WdJ0v7Odmxdv4zEBDjkxyq/gUpqzB9hXJrB4Zx WPWsrOtVWUQjNMprg5iWt9FF+/6aYvMLA+eLBuhPuBJOEI+7rwWRasGD6Z9kwdc9a6Ko uzARS1pSMEnOzDKqY3kg227j+8yjA4tbdBPbKAe+UXWP+c2AI4QPFwMNcieWg8eE2kn5 WZyQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:reply-to:from:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence:in-reply-to :content-disposition:mime-version:references:message-id:subject:cc :to:date:dmarc-filter:delivered-to:dkim-signature:dkim-filter; bh=+zqwGxVA0YdNSWIbjWuJuayJCOJ4qyyeFgdf6JrmVG0=; fh=O6SbEp1Lmu8iWDJwmE5AT4hXTvupzW71de0eJ/uUO/o=; b=bFLIXQOmaU2At2TuBKftfsObPaoIr9Mbh8j46fQMKQzIKVOXSsuwqR5SKuKUdIKLkz NcY0A1F8mbwxTEFtxbXUcn3BWB7YgXTC16WhEO8swcK2UBmgh7ZVxLz8B89nQaMvbuXN Bxpw7c1vq/5vTYyTsKBmBc4FzQJ7SNzaw+xUbNy8Y1PG9HRoEY5Qb6W/Nw2zYXk6GImH TmKElEhnmaEozF8mLcqDJCcAAPhncPNIKFgqnrHrInKw2xIOvwmhlwUftO/EbGwucYHC lzNctqPi0fDKo6ZITLT/VopsinuQPwIzeb8Bw4trOqyZ+0DZS8rWx7+sUSX7t0ldkWXm ZYiA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b=GhCttEzW; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gnu.org Received: from server2.sourceware.org (server2.sourceware.org. [2620:52:3:1:0:246e:9693:128c]) by mx.google.com with ESMTPS id f17-20020a50ee91000000b0051e0d7a3fc9si4118015edr.37.2023.07.22.08.16.33 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 22 Jul 2023 08:16:34 -0700 (PDT) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) client-ip=2620:52:3:1:0:246e:9693:128c; Authentication-Results: mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b=GhCttEzW; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gnu.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 996AC3850438 for ; Sat, 22 Jul 2023 15:16:12 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 996AC3850438 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1690038972; bh=+zqwGxVA0YdNSWIbjWuJuayJCOJ4qyyeFgdf6JrmVG0=; h=Date:To:Cc:Subject:References:In-Reply-To:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=GhCttEzWy3AfWTuNpbjXLmZcncJuvG9Z4b/7kZqRCzIN3VL0037E+h2Qsg+roNBUJ /SbYvKb4tn4iNfolgBp2V/XsbSsy6uHp19SqdafzXWzXzL4IghJlttjbZkM9Jq6Jgt +MHIqu3OMVIksNX9IFde32bJd6pERwi6+qa/cMxQ= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-pl1-x630.google.com (mail-pl1-x630.google.com [IPv6:2607:f8b0:4864:20::630]) by sourceware.org (Postfix) with ESMTPS id 8821C386C5B8 for ; Sat, 22 Jul 2023 15:15:20 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 8821C386C5B8 Received: by mail-pl1-x630.google.com with SMTP id d9443c01a7336-1b8ad907ba4so15738955ad.0 for ; Sat, 22 Jul 2023 08:15:20 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1690038919; x=1690643719; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=+zqwGxVA0YdNSWIbjWuJuayJCOJ4qyyeFgdf6JrmVG0=; b=XEXQD0AJhLu8fIHzkkB0cSsjdCRfWumw6aSQyzM2gCzH81NC/U6ZanlIgNTiMAncso gO1PoMArqAYERDTlKU+FG/kke1VI9kF+iDSXyUQL43JXDxNGFMfqw+M+W8jXEVKYHwS5 tXzyhRznWqJNLI48pzUOJMMozKWCE1noRGJuy8xyo3k6Lepsa47nRmslvNXn5dkqjCth 1xmovoz7E/QfkxUXdB+Tqhp1/g7sAjuJWqiuhjqnjyBdHCA0CZIM2XcdTp1O4ZrLIVdH xtK9YWw86DATjDGHJTz93GUlSSvCtVSs8OYaDIFaQneh53fblqnBdZ11ivRUlLEBetFn 0uOg== X-Gm-Message-State: ABy/qLZL5D1dLq08Gady7cFK/ybn8jX7gRMFGTmZakC61efBDIQStw2e mbLlqDnF0nRnuhOIEiIoD7nr8Vn63w0= X-Received: by 2002:a17:90b:33ca:b0:262:d6e9:208b with SMTP id lk10-20020a17090b33ca00b00262d6e9208bmr2989740pjb.4.1690038919041; Sat, 22 Jul 2023 08:15:19 -0700 (PDT) Received: from Thaum.localdomain (59-102-120-25.tpgi.com.au. [59.102.120.25]) by smtp.gmail.com with ESMTPSA id rm10-20020a17090b3eca00b00262e604724dsm5686373pjb.50.2023.07.22.08.15.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 22 Jul 2023 08:15:18 -0700 (PDT) Date: Sun, 23 Jul 2023 01:15:14 +1000 To: gcc-patches@gcc.gnu.org Cc: Jason Merrill , Patrick Palka Subject: [PATCH v5 3/3] c++: Track lifetimes in constant evaluation [PR70331,PR96630,PR98675] Message-ID: References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: X-Spam-Status: No, score=-11.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, T_SCC_BODY_TEXT_LINE 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+ouuuleilei=gmail.com@gcc.gnu.org Sender: "Gcc-patches" X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1772134328151848852 X-GMAIL-MSGID: 1772134328151848852 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 accessing values outside their lifetime (e.g. following explicit destructor calls). PR c++/96630 PR c++/98675 PR c++/70331 gcc/cp/ChangeLog: * constexpr.cc (constexpr_global_ctx::is_outside_lifetime): New function. (constexpr_global_ctx::get_value): Don't return expired values. (constexpr_global_ctx::get_value_ptr): Likewise. (constexpr_global_ctx::remove_value): Mark value outside lifetime. (outside_lifetime_error): New function. (cxx_eval_call_expression): No longer track save_exprs. (cxx_eval_loop_expr): Likewise. (cxx_eval_constant_expression): Add checks for outside lifetime values. Remove local variables at end of bind exprs, and temporaries after cleanup points. gcc/testsuite/ChangeLog: * 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. * g++.dg/cpp1y/constexpr-lifetime6.C: New test. Signed-off-by: Nathaniel Shead --- gcc/cp/constexpr.cc | 128 ++++++++++++------ .../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 ++ .../g++.dg/cpp1y/constexpr-lifetime6.C | 15 ++ 7 files changed, 169 insertions(+), 42 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 create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime6.C diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 2bf2458c3cd..0ab77dcaf62 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -1148,7 +1148,8 @@ enum constexpr_switch_state { class constexpr_global_ctx { /* Values for any temporaries or local variables within the - constant-expression. */ + constant-expression. Objects outside their lifetime have + value 'void_node'. */ hash_map values; public: /* Number of cxx_eval_constant_expression calls (except skipped ones, @@ -1170,17 +1171,28 @@ public: : constexpr_ops_count (0), cleanups (NULL), modifiable (nullptr), heap_dealloc_count (0) {} + bool is_outside_lifetime (tree t) + { + if (tree *p = values.get (t)) + if (*p == void_node) + return true; + return false; + } tree get_value (tree t) { if (tree *p = values.get (t)) - return *p; + if (*p != void_node) + return *p; return NULL_TREE; } tree *get_value_ptr (tree t) { if (modifiable && !modifiable->contains (t)) return nullptr; - return values.get (t); + if (tree *p = values.get (t)) + if (*p != void_node) + return p; + return nullptr; } void put_value (tree t, tree v) { @@ -1188,7 +1200,13 @@ public: 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)) + values.put (t, void_node); + else + values.remove (t); + } }; /* Helper class for constexpr_global_ctx. In some cases we want to avoid @@ -3111,12 +3129,9 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, gcc_assert (!DECL_BY_REFERENCE (res)); ctx->global->put_value (res, NULL_TREE); - /* Track the callee's evaluated SAVE_EXPRs and TARGET_EXPRs so that - we can forget their values after the call. */ - constexpr_ctx ctx_with_save_exprs = *ctx; - auto_vec save_exprs; - ctx_with_save_exprs.save_exprs = &save_exprs; - ctx_with_save_exprs.call = &new_call; + /* Remember the current call we're evaluating. */ + constexpr_ctx call_ctx = *ctx; + call_ctx.call = &new_call; unsigned save_heap_alloc_count = ctx->global->heap_vars.length (); unsigned save_heap_dealloc_count = ctx->global->heap_dealloc_count; @@ -3127,7 +3142,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, non_constant_p, overflow_p); tree jump_target = NULL_TREE; - cxx_eval_constant_expression (&ctx_with_save_exprs, body, + cxx_eval_constant_expression (&call_ctx, body, vc_discard, non_constant_p, overflow_p, &jump_target); @@ -3183,15 +3198,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, cxx_set_object_constness (ctx, new_obj, /*readonly_p=*/true, non_constant_p, overflow_p); - /* Forget the saved values of the callee's SAVE_EXPRs and - TARGET_EXPRs. */ - 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); @@ -5723,6 +5730,25 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t, return r; } +/* Complain about R, a DECL that is accessed outside its lifetime. */ + +static void +outside_lifetime_error (location_t loc, tree r) +{ + if (DECL_NAME (r) == heap_deleted_identifier) + { + /* Provide a more accurate message for deleted variables. */ + error_at (loc, "use of allocated storage after deallocation " + "in a constant expression"); + inform (DECL_SOURCE_LOCATION (r), "allocated here"); + } + else + { + error_at (loc, "accessing object outside its lifetime"); + inform (DECL_SOURCE_LOCATION (r), "declared here"); + } +} + /* Complain about R, a VAR_DECL, not being usable in a constant expression. FUNDEF_P is true if we're checking a constexpr function body. Shared between potential_constant_expression and @@ -6628,7 +6654,6 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t, bool *non_constant_p, bool *overflow_p, tree *jump_target) { - constexpr_ctx new_ctx = *ctx; tree local_target; if (!jump_target) { @@ -6666,14 +6691,12 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t, default: gcc_unreachable (); } - auto_vec save_exprs; - new_ctx.save_exprs = &save_exprs; do { if (count != -1) { if (body) - cxx_eval_constant_expression (&new_ctx, body, vc_discard, + cxx_eval_constant_expression (ctx, body, vc_discard, non_constant_p, overflow_p, jump_target); if (breaks (jump_target)) @@ -6686,7 +6709,7 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t, *jump_target = NULL_TREE; if (expr) - cxx_eval_constant_expression (&new_ctx, expr, vc_prvalue, + cxx_eval_constant_expression (ctx, expr, vc_prvalue, non_constant_p, overflow_p, jump_target); } @@ -6694,7 +6717,7 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t, if (cond) { tree res - = cxx_eval_constant_expression (&new_ctx, cond, vc_prvalue, + = cxx_eval_constant_expression (ctx, cond, vc_prvalue, non_constant_p, overflow_p, jump_target); if (res) @@ -6709,11 +6732,6 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t, gcc_assert (*jump_target); } - /* Forget saved values of SAVE_EXPRs and TARGET_EXPRs. */ - for (tree save_expr : save_exprs) - ctx->global->remove_value (save_expr); - save_exprs.truncate (0); - if (++count >= constexpr_loop_limit) { if (!ctx->quiet) @@ -6731,10 +6749,6 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t, && (!switches (jump_target) || count == 0) && !*non_constant_p); - /* Forget saved values of SAVE_EXPRs and TARGET_EXPRs. */ - for (tree save_expr : save_exprs) - ctx->global->remove_value (save_expr); - return NULL_TREE; } @@ -7079,11 +7093,20 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, else if (t == ctx->object) return ctx->ctor; if (VAR_P (t)) - if (tree v = ctx->global->get_value (t)) + { + if (tree v = ctx->global->get_value (t)) { r = v; break; } + if (ctx->global->is_outside_lifetime (t)) + { + if (!ctx->quiet) + outside_lifetime_error (loc, t); + *non_constant_p = true; + break; + } + } if (ctx->manifestly_const_eval == mce_true) maybe_warn_about_constant_value (loc, t); if (COMPLETE_TYPE_P (TREE_TYPE (t)) @@ -7128,6 +7151,13 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, r = v; else if (lval) /* Defer in case this is only used for its type. */; + else if (ctx->global->is_outside_lifetime (t)) + { + if (!ctx->quiet) + outside_lifetime_error (loc, t); + *non_constant_p = true; + break; + } else if (COMPLETE_TYPE_P (TREE_TYPE (t)) && is_really_empty_class (TREE_TYPE (t), /*ignore_vptr*/false)) { @@ -7367,17 +7397,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; @@ -7893,10 +7934,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/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..2f5ae8db6d5 --- /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; // { 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 + 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..53785521d05 --- /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; // { 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 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..e7e01fe99a7 --- /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; // { dg-error "outside its lifetime" } +} + +constexpr bool x = test(); // { dg-message "in .constexpr. expansion" } diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime6.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime6.C new file mode 100644 index 00000000000..45c11e8384e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime6.C @@ -0,0 +1,15 @@ +// { dg-do compile { target c++14 } } +// { dg-options "-Wno-return-local-addr" } + +struct Empty {}; + +constexpr const Empty& empty() { + return Empty{}; // { dg-message "note: declared here" } +} + +constexpr const Empty& empty_parm(Empty e) { // { dg-message "note: declared here" } + return e; +} + +constexpr Empty a = empty(); // { dg-error "outside its lifetime" } +constexpr Empty b = empty_parm({}); // { dg-error "outside its lifetime" }