public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] c++: P2280R4, Using unknown refs in constant expr [PR106650]
@ 2023-11-17 21:46 Marek Polacek
  2023-11-20 21:29 ` Jason Merrill
  0 siblings, 1 reply; 4+ messages in thread
From: Marek Polacek @ 2023-11-17 21:46 UTC (permalink / raw)
  To: GCC Patches, Jason Merrill

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 (&param)[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 (&param)[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 (&param)[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


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] c++: P2280R4, Using unknown refs in constant expr [PR106650]
  2023-11-17 21:46 [PATCH] c++: P2280R4, Using unknown refs in constant expr [PR106650] Marek Polacek
@ 2023-11-20 21:29 ` Jason Merrill
  2023-11-29 18:56   ` [PATCH v2] " Marek Polacek
  0 siblings, 1 reply; 4+ messages in thread
From: Jason Merrill @ 2023-11-20 21:29 UTC (permalink / raw)
  To: Marek Polacek, GCC Patches

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 (&param)[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


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v2] c++: P2280R4, Using unknown refs in constant expr [PR106650]
  2023-11-20 21:29 ` Jason Merrill
@ 2023-11-29 18:56   ` Marek Polacek
  2023-11-29 20:40     ` Jason Merrill
  0 siblings, 1 reply; 4+ messages in thread
From: Marek Polacek @ 2023-11-29 18:56 UTC (permalink / raw)
  To: Jason Merrill; +Cc: GCC Patches

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 (&param)[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?

-- >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 (&param)[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 (&param)[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 (&param)[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
-- 
2.42.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v2] c++: P2280R4, Using unknown refs in constant expr [PR106650]
  2023-11-29 18:56   ` [PATCH v2] " Marek Polacek
@ 2023-11-29 20:40     ` Jason Merrill
  0 siblings, 0 replies; 4+ messages in thread
From: Jason Merrill @ 2023-11-29 20:40 UTC (permalink / raw)
  To: Marek Polacek; +Cc: GCC Patches

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 (&param)[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 (&param)[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 (&param)[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 (&param)[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


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2023-11-29 20:40 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-11-17 21:46 [PATCH] c++: P2280R4, Using unknown refs in constant expr [PR106650] 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 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).