From: Marek Polacek Date: Fri, 17 Nov 2023 19:48:44 +0000 (-0500) Subject: c++: P2280R4, Using unknown refs in constant expr [PR106650] X-Git-Tag: basepoints/gcc-15~4160 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=99d114c15523e0bfe7a89ef1947f82eb5ff0260b;p=thirdparty%2Fgcc.git c++: P2280R4, Using unknown refs in constant expr [PR106650] 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. --- diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 344107d494b4..b17e176adedc 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 1c0651203147..d212665e51fd 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 7c3ce66b4c91..f45001449467 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 000000000000..f26027552a0d --- /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 76973638d5f4..d5327c2dcb2f 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 963881b5ad6d..5cb99675fd5f 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 2f48dae47465..47a49f584195 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 000000000000..82771814a800 --- /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 000000000000..ca7343781419 --- /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 000000000000..2ea865f8d5a4 --- /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" }