c++: P2280R4, Using unknown refs in constant expr [PR106650]
Checks
Commit Message
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
base-commit: 231bb992592a9e1bd7ce6583131acb1874c8e34e
Comments
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);
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(); }
};
> 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.
Jason
@@ -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)
@@ -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");
}
@@ -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" }
}
new file mode 100644
@@ -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" }
+}
@@ -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);
[&]() {
new file mode 100644
@@ -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);
+}
new file mode 100644
@@ -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();
+}
new file mode 100644
@@ -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" }