From: Jason Merrill <jason@redhat.com>
To: Marek Polacek <polacek@redhat.com>
Cc: GCC Patches <gcc-patches@gcc.gnu.org>
Subject: Re: [PATCH v2] c++: P2280R4, Using unknown refs in constant expr [PR106650]
Date: Wed, 29 Nov 2023 15:40:24 -0500 [thread overview]
Message-ID: <841b9c41-9efd-4bac-bba0-b960b131de7f@redhat.com> (raw)
In-Reply-To: <ZWeJYOzSY21/ErRW@redhat.com>
On 11/29/23 13:56, Marek Polacek wrote:
> 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
>>> <https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2280r4.html#the-this-pointer>
>>>
>>> Most importantly, the proposal makes this compile:
>>>
>>> template <typename T, size_t N>
>>> 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?
OK.
> -- >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
> <https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2280r4.html#the-this-pointer>
>
> Most importantly, the proposal makes this compile:
>
> template <typename T, size_t N>
> 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) <case PARM_DECL>: Allow
> reference to unknown/this as per P2280.
> <case VAR_DECL>: 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
>
> 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 <typename T, size_t N>
> +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 <typename T, size_t N>
> +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<typename> 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 <type_traits>
> +
> +template <typename T, typename U>
> +constexpr bool is_type(U &&)
> +{
> + return std::is_same_v<T, std::decay_t<U>>;
> +}
> +
> +auto visitor = [](auto&& v) {
> + if constexpr(is_type<int>(v)) {
> + // ...
> + } else if constexpr(is_type<char>(v)) {
> + // ...
> + }
> +};
> +
> +void
> +g (int i)
> +{
> + visitor (i);
> + constexpr bool b = is_type<int>(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 <bool V>
> +struct Widget {
> + struct Config {
> + static constexpr bool value = V;
> + } config;
> +
> + void f() {
> + if constexpr (config.value) {
> + // ...
> + }
> + }
> +};
> +
> +void
> +g ()
> +{
> + Widget<false> 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 <typeinfo>
> +
> +using size_t = decltype(sizeof(42));
> +
> +template <typename T, size_t N>
> +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" }
>
> base-commit: 634cf26c94de620e66aa124b8ec4d6c2be4b74b2
prev parent reply other threads:[~2023-11-29 20:40 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-11-17 21:46 [PATCH] " Marek Polacek
2023-11-20 21:29 ` Jason Merrill
2023-11-29 18:56 ` [PATCH v2] " Marek Polacek
2023-11-29 20:40 ` Jason Merrill [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=841b9c41-9efd-4bac-bba0-b960b131de7f@redhat.com \
--to=jason@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=polacek@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).