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 <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 PR c++/106650 gcc/cp/ChangeLog: * constexpr.cc (cxx_eval_constant_expression): 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/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 | 2 + .../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 | 25 +++++++++ .../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 +++++++++++++++++++ 8 files changed, 134 insertions(+), 4 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..d5e487801cc 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -7378,6 +7378,8 @@ 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 (!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..4be729c2301 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ref13.C @@ -0,0 +1,25 @@ +// 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; +} + +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" } +} 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..36866306684 --- /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 usable" } base-commit: 231bb992592a9e1bd7ce6583131acb1874c8e34e -- 2.41.0