[v2] c++: P2280R4, Using unknown refs in constant expr [PR106650]

Message ID ZWeJYOzSY21/ErRW@redhat.com
State Committed
Commit 99d114c15523e0bfe7a89ef1947f82eb5ff0260b
Headers
Series [v2] c++: P2280R4, Using unknown refs in constant expr [PR106650] |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gcc_check--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 warning Patch is already merged

Commit Message

Marek Polacek Nov. 29, 2023, 6:56 p.m. UTC
  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


base-commit: 634cf26c94de620e66aa124b8ec4d6c2be4b74b2
  

Comments

Jason Merrill Nov. 29, 2023, 8:40 p.m. UTC | #1
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
  

Patch

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" }