From patchwork Wed Nov 29 18:56:32 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Polacek X-Patchwork-Id: 80982 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 4314F387102F for ; Wed, 29 Nov 2023 18:57:15 +0000 (GMT) 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.129.124]) by sourceware.org (Postfix) with ESMTPS id 1F8353858299 for ; Wed, 29 Nov 2023 18:56:48 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 1F8353858299 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 1F8353858299 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1701284218; cv=none; b=mq3SUAMPSU7wlenX8LndFU0JYa9spsfImV2Wui35cZEkJfY2uIKonHof3Q4T1GyeP6BAUj2yCO3I4bKj4fx9P9a+gs8E7Z4n/wyVgZ+7zqTCEO2pfLgXVRNVOWNVvfSNWhCY0WBFLkQFxxj2Xxj8H+F2rjtMU7v/GexHFOuP8fs= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1701284218; c=relaxed/simple; bh=IVOX6EVbKDR/YqgbVWSSYRVU6w3xnUE2RIgcamS2/hY=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=h0BAVwC4tQMsjIiQCLNdYGKOHf0QXvpoXdZISzxqVkmz3FfQy9Vkr6ExjIF7G5QdODHMeAADAaDM/jfjv+l2dRTZmNU5hygRDvpC7QSJXuLbcL3k1YLhIlHW5xjLBJRXH3Yi1w1/CFsqRax9001TDn1QMIKUUkzhENZHQ6mZ/UM= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1701284207; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=V+N3Ju+M6qHzHmS8wtSKofT/7tMsUgrX+hgyQbWtcS0=; b=FLwwZFdDA+Db7hXqKB2vETdlBmKgr8Dd9zjTWeuREw46V+NnrlFPmj8C+TOVRDXVoC2591 B2aOr03qlV5fHU7LL+Dgk/8mZVk4jNwZ5x47upqEDc8bNCPz01Qge4zxb6T42PIIh6Xwmd Wtc8yTAO4zfU9c+J+IYz+SYxUkRu0y8= Received: from mail-oo1-f71.google.com (mail-oo1-f71.google.com [209.85.161.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-507-nLh-ogRBPEKmjjcN1CynKg-1; Wed, 29 Nov 2023 13:56:36 -0500 X-MC-Unique: nLh-ogRBPEKmjjcN1CynKg-1 Received: by mail-oo1-f71.google.com with SMTP id 006d021491bc7-58d527787abso167500eaf.1 for ; Wed, 29 Nov 2023 10:56:36 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701284195; x=1701888995; h=user-agent: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=V+N3Ju+M6qHzHmS8wtSKofT/7tMsUgrX+hgyQbWtcS0=; b=wSihz8S4G/ZW8Q2GidaiACuiqDrgyJwungCGU1cL2dtxSrqXrWsN6+qhqJqEnuROfB LM6PrmnivouA6eLHm4jHID5eI4ifmbYT7mS1N5WrvcSfLMZnXyVcP68bxAUWgF7yabUt WqzvSxaXQ7WQdD2j7HIQlC7OhQES2f1SlH+E0DxyAwEIOH62FM9vaTgfogfOWJ60ektc G92y80ir7LSANMnnp4Dfx/Y8L+o+Dgj1S6vIM9JmFb1v7ER881atfv2YXXpqzzNP/L34 Alq9AWNh3LrEYSLTw/SCzrIFxQ3fkuFYRByZpVhYbaR781884Ff1C/C+RxiATsS78QqK HOZw== X-Gm-Message-State: AOJu0Yw9F4DBctm8kcM594unWH+vLOkp5Aokrd0cjMWiP8Olfu38FZJr eMd8g6o3cT3wld+4h79sJ2yEwkums9bv47tNLpdv7ntiNDF5Sr1TqIgryicYBJ4/yhporFaVeSm Q207grtS/hjMRzgXd3FM7IY4nIw== X-Received: by 2002:a05:6358:52cf:b0:16e:12c:16b8 with SMTP id z15-20020a05635852cf00b0016e012c16b8mr25474884rwz.7.1701284195380; Wed, 29 Nov 2023 10:56:35 -0800 (PST) X-Google-Smtp-Source: AGHT+IGXsG9+4uOlEyBWn5kH6vEW2woIpzpwj45pLyku52mVt+MNaFIx+/2lN4RetC6awYQwR+/Skw== X-Received: by 2002:a05:6358:52cf:b0:16e:12c:16b8 with SMTP id z15-20020a05635852cf00b0016e012c16b8mr25474857rwz.7.1701284194842; Wed, 29 Nov 2023 10:56:34 -0800 (PST) Received: from redhat.com (2603-7000-9500-34a5-0000-0000-0000-1db4.res6.spectrum.com. [2603:7000:9500:34a5::1db4]) by smtp.gmail.com with ESMTPSA id x7-20020ac84a07000000b00419c40a0d70sm5808792qtq.54.2023.11.29.10.56.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 29 Nov 2023 10:56:34 -0800 (PST) Date: Wed, 29 Nov 2023 13:56:32 -0500 From: Marek Polacek To: Jason Merrill Cc: GCC Patches Subject: [PATCH v2] c++: P2280R4, Using unknown refs in constant expr [PR106650] Message-ID: References: <20231117214610.173872-1-polacek@redhat.com> MIME-Version: 1.0 In-Reply-To: User-Agent: Mutt/2.2.9 (2022-11-12) X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Disposition: inline X-Spam-Status: No, score=-12.3 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_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, 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.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org On Mon, Nov 20, 2023 at 04:29:33PM -0500, Jason Merrill wrote: > On 11/17/23 16:46, Marek Polacek wrote: > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? > > > > -- >8 -- > > This patch is an attempt to implement (part of?) P2280, Using unknown > > pointers and references in constant expressions. (Note that R4 seems to > > only allow References to unknown/Accesses via this, but not Pointers to > > unknown.) > > Indeed. That seems a bit arbitrary to me, but there it is. > > We were rejecting the testcase before because cxx_bind_parameters_in_call > was trying to perform an lvalue->rvalue conversion on the reference itself; > this isn't really a thing in the language, but worked to implement the > reference bullet that the paper removes. Your approach to fixing that makes > sense to me. > > We should do the same for VAR_DECL references, e.g. > > extern int (&r)[42]; > constexpr int i = array_size (r); Argh, right. > You also need to allow (implict or explicit) use of 'this', as in: > > struct A > { > constexpr int f() { return 42; } > void g() { constexpr int i = f(); } > }; Ah, I thought that already worked, but not so. Apology apology. > > This patch works to the extent that the test case added in [expr.const] > > works as expected, as well as the test in > > > > > > Most importantly, the proposal makes this compile: > > > > template > > constexpr auto array_size(T (&)[N]) -> size_t { > > return N; > > } > > > > void check(int const (¶m)[3]) { > > constexpr auto s = array_size(param); > > static_assert (s == 3); > > } > > > > and I think it would be a pity not to have it in GCC 14. > > > > What still doesn't work (and I don't know if it should) is the test in $3.2: > > > > struct A2 { constexpr int f() { return 0; } }; > > struct B2 : virtual A2 {}; > > void f2(B2 &b) { constexpr int k = b.f(); } > > > > where we say > > error: '* & b' is not a constant expression > > It seems like that is supposed to work, the problem is accessing the vtable > to perform the conversion. I have WIP to recognize that conversion better > in order to fix PR53288; this testcase can wait for that fix. Great. Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? -- >8 -- This patch is an attempt to implement (part of?) P2280, Using unknown pointers and references in constant expressions. (Note that R4 seems to only allow References to unknown/Accesses via this, but not Pointers to unknown.) This patch works to the extent that the test case added in [expr.const] works as expected, as well as the test in Most importantly, the proposal makes this compile: template constexpr auto array_size(T (&)[N]) -> size_t { return N; } void check(int const (¶m)[3]) { constexpr auto s = array_size(param); static_assert (s == 3); } and I think it would be a pity not to have it in GCC 14. What still doesn't work is the test in $3.2: struct A2 { constexpr int f() { return 0; } }; struct B2 : virtual A2 {}; void f2(B2 &b) { constexpr int k = b.f(); } where we say error: '* & b' is not a constant expression This will be fixed in the future. PR c++/106650 gcc/cp/ChangeLog: * constexpr.cc (cxx_eval_constant_expression) : Allow reference to unknown/this as per P2280. : Allow reference to unknown as per P2280. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/constexpr-array-ptr6.C: Remove dg-error. * g++.dg/cpp0x/constexpr-ref12.C: Likewise. * g++.dg/cpp0x/constexpr-ref2.C: Adjust dg-error. * g++.dg/cpp0x/noexcept34.C: Remove dg-error. * g++.dg/cpp1y/lambda-generic-const10.C: Likewise. * g++.dg/cpp0x/constexpr-ref13.C: New test. * g++.dg/cpp1z/constexpr-ref1.C: New test. * g++.dg/cpp1z/constexpr-ref2.C: New test. * g++.dg/cpp2a/constexpr-ref1.C: New test. --- gcc/cp/constexpr.cc | 8 ++- .../g++.dg/cpp0x/constexpr-array-ptr6.C | 2 +- gcc/testsuite/g++.dg/cpp0x/constexpr-ref12.C | 4 +- gcc/testsuite/g++.dg/cpp0x/constexpr-ref13.C | 41 ++++++++++++++ gcc/testsuite/g++.dg/cpp0x/constexpr-ref2.C | 4 +- gcc/testsuite/g++.dg/cpp0x/noexcept34.C | 8 +-- .../g++.dg/cpp1y/lambda-generic-const10.C | 2 +- gcc/testsuite/g++.dg/cpp1z/constexpr-ref1.C | 26 +++++++++ gcc/testsuite/g++.dg/cpp1z/constexpr-ref2.C | 23 ++++++++ gcc/testsuite/g++.dg/cpp2a/constexpr-ref1.C | 54 +++++++++++++++++++ 10 files changed, 161 insertions(+), 11 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-ref13.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/constexpr-ref1.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/constexpr-ref2.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/constexpr-ref1.C base-commit: 634cf26c94de620e66aa124b8ec4d6c2be4b74b2 diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 344107d494b..b17e176aded 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -7336,7 +7336,9 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, if (TREE_CODE (r) == TARGET_EXPR && TREE_CODE (TARGET_EXPR_INITIAL (r)) == CONSTRUCTOR) r = TARGET_EXPR_INITIAL (r); - if (DECL_P (r)) + if (DECL_P (r) + /* P2280 allows references to unknown. */ + && !(VAR_P (t) && TYPE_REF_P (TREE_TYPE (t)))) { if (!ctx->quiet) non_const_var_error (loc, r, /*fundef_p*/false); @@ -7378,6 +7380,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, r = build_constructor (TREE_TYPE (t), NULL); TREE_CONSTANT (r) = true; } + else if (TYPE_REF_P (TREE_TYPE (t))) + /* P2280 allows references to unknown... */; + else if (is_this_parameter (t)) + /* ...as well as the this pointer. */; else { if (!ctx->quiet) diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr6.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr6.C index 1c065120314..d212665e51f 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr6.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr6.C @@ -12,7 +12,7 @@ constexpr auto sz_d = size(array_double); static_assert(sz_d == 3, "Array size failure"); void f(bool (¶m)[2]) { - static_assert(size(param) == 2, "Array size failure"); // { dg-error "" } + static_assert(size(param) == 2, "Array size failure"); short data[] = {-1, 2, -45, 6, 88, 99, -345}; static_assert(size(data) == 7, "Array size failure"); } diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ref12.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ref12.C index 7c3ce66b4c9..f4500144946 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ref12.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ref12.C @@ -40,7 +40,7 @@ void f(a ap, a& arp) static_assert (g(ar2),""); // { dg-error "constant" } static_assert (h(ar2),""); // { dg-error "constant" } - static_assert (arp.g(),""); // { dg-error "constant" } - static_assert (g(arp),""); // { dg-error "constant" } + static_assert (arp.g(),""); + static_assert (g(arp),""); static_assert (h(arp),""); // { dg-error "constant" } } diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ref13.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ref13.C new file mode 100644 index 00000000000..f26027552a0 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ref13.C @@ -0,0 +1,41 @@ +// P2280R4 - Using unknown pointers and references in constant expressions +// PR c++/106650 +// { dg-do compile { target c++11 } } + +using size_t = decltype(sizeof(42)); + +template +constexpr auto array_size(T (&)[N]) -> size_t { + return N; +} + +extern int (&r)[42]; +constexpr int i = array_size (r); + +void check(int const (¶m)[3]) { + int local[] = {1, 2, 3}; + constexpr auto s0 = array_size(local); + constexpr auto s1 = array_size(param); +} + +template +constexpr size_t array_size_ptr(T (*)[N]) { + return N; +} + +void check_ptr(int const (*param)[3]) { + constexpr auto s2 = array_size_ptr(param); // { dg-error "not a constant" } +} + +struct A +{ + constexpr int f() { return 42; } + void g() { constexpr int i = f(); } + void g2() { constexpr int i = this->f(); } +}; + +struct B { + constexpr static bool b = false; + void g() noexcept(b) { } + void g2() noexcept(this->b) { } +}; diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ref2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ref2.C index 76973638d5f..d5327c2dcb2 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ref2.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ref2.C @@ -4,8 +4,8 @@ extern int *p; constexpr int& ri = *p; // { dg-error "p" } -extern constexpr int &er; // { dg-error "not a definition" } -constexpr int& ri2 = er; // { dg-error "er" } +extern constexpr int &er; // { dg-error "not a definition|not a constant" } +constexpr int& ri2 = er; void f(int j) { diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept34.C b/gcc/testsuite/g++.dg/cpp0x/noexcept34.C index 963881b5ad6..5cb99675fd5 100644 --- a/gcc/testsuite/g++.dg/cpp0x/noexcept34.C +++ b/gcc/testsuite/g++.dg/cpp0x/noexcept34.C @@ -7,13 +7,13 @@ template struct A { constexpr int f () { return 0; } bool b = true; - void g () noexcept (f()) { } // { dg-error ".this. is not a constant" } - void g2 () noexcept (this->f()) { } // { dg-error ".this. is not a constant" } + void g () noexcept (f()) { } + void g2 () noexcept (this->f()) { } void g3 () noexcept (b) { } // { dg-error "use of .this. in a constant expression|use of parameter|.this. is not a constant" } void g4 (int i) noexcept (i) { } // { dg-error "use of parameter" } - void g5 () noexcept (A::f()) { } // { dg-error ".this. is not a constant" } + void g5 () noexcept (A::f()) { } void g6 () noexcept (foo(b)) { } // { dg-error "use of .this. in a constant expression|use of parameter|.this. is not a constant" } - void g7 () noexcept (int{f()}) { } // { dg-error ".this. is not a constant" } + void g7 () noexcept (int{f()}) { } }; int main () diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const10.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const10.C index 2f48dae4746..47a49f58419 100644 --- a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const10.C +++ b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const10.C @@ -11,7 +11,7 @@ int main() constexpr auto x = f(); //ok, call constexpr const non-static method [](auto const &f) { - constexpr auto x = f(); // { dg-error "" } + constexpr auto x = f(); }(f); [&]() { diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-ref1.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-ref1.C new file mode 100644 index 00000000000..82771814a80 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-ref1.C @@ -0,0 +1,26 @@ +// P2280R4 - Using unknown pointers and references in constant expressions +// PR c++/106650 +// { dg-do compile { target c++17 } } + +#include + +template +constexpr bool is_type(U &&) +{ + return std::is_same_v>; +} + +auto visitor = [](auto&& v) { + if constexpr(is_type(v)) { + // ... + } else if constexpr(is_type(v)) { + // ... + } +}; + +void +g (int i) +{ + visitor (i); + constexpr bool b = is_type(i); +} diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-ref2.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-ref2.C new file mode 100644 index 00000000000..ca734378141 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-ref2.C @@ -0,0 +1,23 @@ +// P2280R4 - Using unknown pointers and references in constant expressions +// PR c++/106650 +// { dg-do compile { target c++17 } } + +template +struct Widget { + struct Config { + static constexpr bool value = V; + } config; + + void f() { + if constexpr (config.value) { + // ... + } + } +}; + +void +g () +{ + Widget w; + w.f(); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-ref1.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-ref1.C new file mode 100644 index 00000000000..2ea865f8d5a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-ref1.C @@ -0,0 +1,54 @@ +// P2280R4 - Using unknown pointers and references in constant expressions +// PR c++/106650 +// { dg-do compile { target c++20 } } + +#include + +using size_t = decltype(sizeof(42)); + +template +constexpr size_t array_size(T (&)[N]) { + return N; +} + +void use_array(int const (&gold_medal_mel)[2]) { + constexpr auto gold = array_size(gold_medal_mel); // OK +} + +constexpr auto olympic_mile() { + const int ledecky = 1500; + return []{ return ledecky; }; +} +static_assert(olympic_mile()() == 1500); // OK + +struct Swim { + constexpr int phelps() { return 28; } + virtual constexpr int lochte() { return 12; } + int coughlin = 12; +}; + +constexpr int how_many(Swim& swam) { + Swim* p = &swam; + return (p + 1 - 1)->phelps(); +} + +void splash(Swim& swam) { + static_assert(swam.phelps() == 28); // OK + static_assert((&swam)->phelps() == 28); // OK + + Swim* pswam = &swam; + static_assert(pswam->phelps() == 28); // { dg-error "non-constant|not usable" } + + static_assert(how_many(swam) == 28); // OK + static_assert(Swim().lochte() == 12); // OK + + static_assert(swam.lochte() == 12); // { dg-error "non-constant|not a constant" } + + static_assert(swam.coughlin == 12); // { dg-error "non-constant|not a constant" } +} + +extern Swim dc; +extern Swim& trident; + +constexpr auto& sandeno = typeid(dc); // OK, can only be typeid(Swim) +constexpr auto& gallagher = typeid(trident); // { dg-error "not a constant" }