From patchwork Thu Jul 15 21:14:59 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Polacek X-Patchwork-Id: 1505907 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.a=rsa-sha256 header.s=default header.b=lbO7yYKg; dkim-atps=neutral Received: from sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GQnGj26Mzz9sWc for ; Fri, 16 Jul 2021 07:15:55 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id ADEEE398D429 for ; Thu, 15 Jul 2021 21:15:52 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org ADEEE398D429 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1626383752; bh=q1VACrs/+sa1g+5eCm2yqec+bHuP9XBwcUoqxeWx1OU=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=lbO7yYKgHDqo4XPyZKagjxthZsIWraWXjkqxEBpH7fj/A6PB5T50Ry9NB1UcdQQT+ e8yq58Wg9U77wQ/+stQqO8AXVxAQRHnmk3Qdfc18qwnUSkMWV1665bt8246yEpKnJ+ WAG+gvDKJ14TEN5lF6PxHBAQ/znClo3Eoy9ytekU= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id A51863853804 for ; Thu, 15 Jul 2021 21:15:08 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org A51863853804 Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-360-KwkHwy11Ox2t9X1TaNtpQQ-1; Thu, 15 Jul 2021 17:15:07 -0400 X-MC-Unique: KwkHwy11Ox2t9X1TaNtpQQ-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 0C74518D6A41 for ; Thu, 15 Jul 2021 21:15:06 +0000 (UTC) Received: from pdp-11.hsd1.ma.comcast.net (ovpn-114-138.rdu2.redhat.com [10.10.114.138]) by smtp.corp.redhat.com (Postfix) with ESMTP id 9DBC86090F; Thu, 15 Jul 2021 21:15:05 +0000 (UTC) To: Jason Merrill , GCC Patches Subject: [PATCH] c++: Allow constexpr references to non-static vars [PR100976] Date: Thu, 15 Jul 2021 17:14:59 -0400 Message-Id: <20210715211459.966387-1-polacek@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-14.4 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) 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: Marek Polacek via Gcc-patches From: Marek Polacek Reply-To: Marek Polacek Errors-To: gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org Sender: "Gcc-patches" The combination of DR 2481 and DR 2126 should allow us to do void f() { constexpr const int &r = 42; static_assert(r == 42); } because [expr.const]/4.7 now says that "a temporary object of non-volatile const-qualified literal type whose lifetime is extended to that of a variable that is usable in constant expressions" is usable in a constant expression. I think the temporary is supposed to be const-qualified, because Core 2481 says so. I was happy to find out that we already mark the temporary as const + constexpr in set_up_extended_ref_temp. But that wasn't enough to make the test above work: references are traditionally implemented as pointers, so the temporary object will be (const int &)&D.1234, and verify_constant -> reduced_constant_expression_p -> initializer_constant_valid_p_1 doesn't think that's OK -- and rightly so -- the address of a local variable certainly isn't constant. Therefore I'm skipping the verify_constant check in cxx_eval_outermost_constant_expr. (DECL_INITIAL isn't checked because maybe we are still waiting for initialize_local_var to set it.) Then we need to be able to evaluate such a reference. This I do by seeing through the reference in cxx_eval_constant_expression. I can't rely on decl_constant_value to pull out DECL_INITIAL, because the VAR_DECL isn't DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P, and I think we don't need to mess with that if we're keeping this purely in constexpr. I wonder if we should accept void f2() { constexpr int &&r = 42; static_assert(r == 42); } Currently we don't -- CP_TYPE_CONST_NON_VOLATILE_P (type) is false in set_up_extended_ref_temp. Does this make sense? Bootstrapped/regtested on x86_64-pc-linux-gnu. PR c++/100976 DR 2481 gcc/cp/ChangeLog: * constexpr.c (cxx_eval_constant_expression): For a constexpr reference, return its DECL_INITIAL. (cxx_eval_outermost_constant_expr): Don't verify the initializer for a constexpr variable of reference type. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/constexpr-ref2.C: Remove dg-error. * g++.dg/cpp0x/constexpr-temp2.C: New test. * g++.dg/cpp23/constexpr-temp1.C: New test. * g++.dg/cpp23/constexpr-temp2.C: New test. --- gcc/cp/constexpr.c | 29 +++++++++++++-- gcc/testsuite/g++.dg/cpp0x/constexpr-ref2.C | 5 ++- gcc/testsuite/g++.dg/cpp0x/constexpr-temp2.C | 15 ++++++++ gcc/testsuite/g++.dg/cpp23/constexpr-temp1.C | 39 ++++++++++++++++++++ gcc/testsuite/g++.dg/cpp23/constexpr-temp2.C | 23 ++++++++++++ 5 files changed, 106 insertions(+), 5 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-temp2.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-temp1.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-temp2.C base-commit: f364cdffa47af574f90f671b2dcf5afa91442741 diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 31fa5b66865..80b4985d055 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -6180,6 +6180,22 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, return cxx_eval_constant_expression (ctx, r, lval, non_constant_p, overflow_p); } + /* DR 2126 amended [expr.const]/4.7 to say that "a temporary object + of non-volatile const-qualified literal type whose lifetime is + extended to that of a variable that is usable in constant + expressions" is usable in a constant expression. Along with + DR 2481 this means that we should accept + + constexpr const int &r = 42; + static_assert (r == 42); + + Take a shortcut here rather than using decl_constant_value. The + temporary was marked constexpr in set_up_extended_ref_temp. */ + else if (TYPE_REF_P (TREE_TYPE (t)) + && DECL_DECLARED_CONSTEXPR_P (t) + && DECL_INITIAL (t)) + return cxx_eval_constant_expression (ctx, DECL_INITIAL (t), lval, + non_constant_p, overflow_p); /* fall through */ case CONST_DECL: /* We used to not check lval for CONST_DECL, but darwin.c uses @@ -7289,10 +7305,17 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, r = cxx_eval_constant_expression (&ctx, r, false, &non_constant_p, &overflow_p); - if (!constexpr_dtor) - verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p); - else + if (object && VAR_P (object) + && DECL_DECLARED_CONSTEXPR_P (object) + && TYPE_REF_P (TREE_TYPE (object))) + /* Circumvent verify_constant, because it ends up calling + initializer_constant_valid_p which doesn't like taking + the address of a local variable. But that's OK since + DR 2126 + DR 2481, at least in a constexpr context. */; + else if (constexpr_dtor) DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object) = true; + else + verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p); unsigned int i; tree cleanup; diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ref2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ref2.C index 76973638d5f..b7701b849df 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ref2.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ref2.C @@ -9,9 +9,10 @@ constexpr int& ri2 = er; // { dg-error "er" } void f(int j) { + // Used to be erroneous, but allowed since DR 2481. constexpr int i = 42; - constexpr int const& ri = i; // { dg-error "" } + constexpr int const& ri = i; - constexpr int& rj = j; // { dg-error "" } + constexpr int& rj = j; } diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-temp2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-temp2.C new file mode 100644 index 00000000000..e396264cff3 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-temp2.C @@ -0,0 +1,15 @@ +// DR 2126 - Lifetime-extended temporaries in constant expressions +// { dg-do compile { target c++11 } } + +#define SA(X) static_assert ((X),#X) + +typedef const int CI[3]; +constexpr CI &ci = CI{1, 2, 3}; +SA(ci[1] == 2); + +void +g () +{ + constexpr CI &r = CI{1, 2, 3}; + SA(r[1] == 2); +} diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-temp1.C b/gcc/testsuite/g++.dg/cpp23/constexpr-temp1.C new file mode 100644 index 00000000000..0d3f4972563 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-temp1.C @@ -0,0 +1,39 @@ +// PR c++/100976 +// DR 2481 - Cv-qualification of temporary to which a reference is bound +// { dg-do compile { target c++11 } } + +#define SA(X) static_assert ((X),#X) + +struct literal { + int m; + constexpr literal() : m() { } + constexpr literal(int n) : m(n) { } +}; + +void +g () +{ + constexpr const int &r = 42; + constexpr const int &&rr = 42; + constexpr int x = 42; + constexpr static int sx = 42; + constexpr const auto &rx = x; + constexpr const auto &rsx = sx; + SA(r == 42); + SA(rr == 42); + SA(rx == 42); + SA(rsx == 42); + SA(&rx == &x); + SA(&rsx == &sx); + const int *pr = &r; + const int *prr = &rr; +} + +void +f () +{ + constexpr const literal &r = { 42 }; + SA(r.m == 42); + constexpr const literal &&rr = { 42 }; + SA(rr.m == 42); +} diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-temp2.C b/gcc/testsuite/g++.dg/cpp23/constexpr-temp2.C new file mode 100644 index 00000000000..1e1169ac5e1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-temp2.C @@ -0,0 +1,23 @@ +// PR c++/100976 +// DR 2481 - Cv-qualification of temporary to which a reference is bound +// { dg-do compile { target c++11 } } + +#define SA(X) static_assert ((X),#X) + +int +main () +{ + constexpr const volatile int &r = 42; // { dg-error "cannot bind" } + constexpr const volatile int &&rr = 42; + constexpr int x = 42; + constexpr const volatile auto &rx = x; + SA(rr == 42); // { dg-error "non-constant|lvalue-to-rvalue" } + SA(rx == 42); // { dg-error "non-constant|lvalue-to-rvalue" } + + int i = 42; + constexpr const auto &ri = i; + SA(ri == 42); // { dg-error "non-constant|not usable" } + + constexpr const int &missing; // { dg-error "not initialized" } + SA ((bool(missing), true)); +}