diff options
author | Marek Polacek <polacek@redhat.com> | 2023-11-17 14:48:44 -0500 |
---|---|---|
committer | Marek Polacek <polacek@redhat.com> | 2023-11-29 17:08:16 -0500 |
commit | 99d114c15523e0bfe7a89ef1947f82eb5ff0260b (patch) | |
tree | c5a65f6de92f0d612a381fc44cf832f5fe662cd8 | |
parent | 8b2e510ca3158bac5553e90cd9d856a5db5dafb7 (diff) | |
download | gcc-99d114c15523e0bfe7a89ef1947f82eb5ff0260b.zip gcc-99d114c15523e0bfe7a89ef1947f82eb5ff0260b.tar.gz gcc-99d114c15523e0bfe7a89ef1947f82eb5ff0260b.tar.bz2 |
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
<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.
-rw-r--r-- | gcc/cp/constexpr.cc | 8 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr6.C | 2 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp0x/constexpr-ref12.C | 4 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp0x/constexpr-ref13.C | 41 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp0x/constexpr-ref2.C | 4 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp0x/noexcept34.C | 8 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp1y/lambda-generic-const10.C | 2 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp1z/constexpr-ref1.C | 26 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp1z/constexpr-ref2.C | 23 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/constexpr-ref1.C | 54 |
10 files changed, 161 insertions, 11 deletions
diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 344107d..b17e176 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 1c06512..d212665 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 7c3ce66..f450014 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 0000000..f260275 --- /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 7697363..d5327c2 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 963881b..5cb9967 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 2f48dae..47a49f5 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 0000000..8277181 --- /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 0000000..ca73437 --- /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 0000000..2ea865f --- /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" } |