[v2] c++: Implement C++23 P2242R3 - Non-literal variables (and labels and gotos) in constexpr functions

Message ID 20211005222456.GC304296@tucnak
State Committed
Headers
Series [v2] c++: Implement C++23 P2242R3 - Non-literal variables (and labels and gotos) in constexpr functions |

Commit Message

Jakub Jelinek Oct. 5, 2021, 10:24 p.m. UTC
  On Tue, Oct 05, 2021 at 05:24:09PM -0400, Jason Merrill wrote:
> That seems correct to me.  If they have static initialization, they are
> initialized.

Ok, I've included those tests in the patch now too.
> > @@ -8736,14 +8765,14 @@ potential_constant_expression_1 (tree t,
> >         tmp = DECL_EXPR_DECL (t);
> >         if (VAR_P (tmp) && !DECL_ARTIFICIAL (tmp))
> >   	{
> > -	  if (TREE_STATIC (tmp))
> > +	  if (TREE_STATIC (tmp) && cxx_dialect < cxx23)
> >   	    {
> >   	      if (flags & tf_error)
> >   		error_at (DECL_SOURCE_LOCATION (tmp), "%qD declared "
> >   			  "%<static%> in %<constexpr%> context", tmp);
> >   	      return false;
> >   	    }
> > -	  else if (CP_DECL_THREAD_LOCAL_P (tmp))
> > +	  else if (CP_DECL_THREAD_LOCAL_P (tmp) && cxx_dialect < cxx23)
> >   	    {
> >   	      if (flags & tf_error)
> >   		error_at (DECL_SOURCE_LOCATION (tmp), "%qD declared "
> 
> Hmm, I wouldn't expect this hunk to be needed; if there's a control path
> that doesn't include these declarations, we shouldn't hit these errors.

You're right, that is unnecessary, if the static/thread_local declaration
is unconditional, we can (but aren't required to) diagnose it even when
the constexpr function is never evaluated in constant expressions.
But, I had to swap the TREE_STATIC and CP_DECL_THREAD_LOCAL_P checks
because the latter implies the former and so static would be always
printed instead of thread_local for thread_local declarations.

> > @@ -9025,7 +9054,7 @@ potential_constant_expression_1 (tree t,
> >       case LABEL_EXPR:
> >         t = LABEL_EXPR_LABEL (t);
> > -      if (DECL_ARTIFICIAL (t))
> > +      if (DECL_ARTIFICIAL (t) || cxx_dialect >= cxx23)
> >   	return true;
> >         else if (flags & tf_error)
> >   	error_at (loc, "label definition is not a constant expression");
> 
> Let's change this message to "only available with..." like the others.

Changed too.

Here is an updated patch (also after IRC chat with Jonathan changed
the __cpp_constexpr value from 202103L that was in the paper to 202110L).
Passes the tests that are modified so far, full bootstrap/regtest
queued after my current one finishes.

2021-10-06  Jakub Jelinek  <jakub@redhat.com>

gcc/c-family/
	* c-cppbuiltin.c (c_cpp_builtins): For -std=c++23 predefine
	__cpp_constexpr to 202110L rather than 201907L.
gcc/cp/
	* parser.c (cp_parser_jump_statement): Implement C++23 P2242R3.
	Allow goto expressions in constexpr function bodies for C++23.
	Adjust error message for older standards to mention it.
	* decl.c (start_decl): Allow static and thread_local declarations
	in constexpr function bodies for C++23.  Adjust error message for
	older standards to mention it.
	* constexpr.c (ensure_literal_type_for_constexpr_object): Allow
	declarations of variables with non-literal type in constexpr function
	bodies for C++23.  Adjust error message for older standards to mention
	it.
	(cxx_eval_constant_expression) <case DECL_EXPR>: Diagnose declarations
	of initialization of static or thread_local vars.
	(cxx_eval_constant_expression) <case GOTO_EXPR>: Diagnose goto
	statements for C++23.
	(potential_constant_expression_1) <case DECL_EXPR>: Swap the
	CP_DECL_THREAD_LOCAL_P and TREE_STATIC checks.
	(potential_constant_expression_1) <case LABEL_EXPR>: Allow labels for
	C++23.  Adjust error message for older standards to mention it.
gcc/testsuite/
	* g++.dg/cpp23/feat-cxx2b.C: Expect __cpp_constexpr 202110L rather
	than 201907L.
	* g++.dg/cpp23/constexpr-nonlit1.C: New test.
	* g++.dg/cpp23/constexpr-nonlit2.C: New test.
	* g++.dg/cpp23/constexpr-nonlit3.C: New test.
	* g++.dg/cpp23/constexpr-nonlit4.C: New test.
	* g++.dg/cpp23/constexpr-nonlit5.C: New test.
	* g++.dg/cpp23/constexpr-nonlit6.C: New test.
	* g++.dg/diagnostic/constexpr1.C: Only expect some diagnostics for
	c++20_down.
	* g++.dg/cpp1y/constexpr-label.C: Likewise.
	* g++.dg/cpp1y/constexpr-neg1.C: Likewise.
	* g++.dg/cpp2a/constexpr-try5.C: Likewise.  Adjust some expected
	wording.
	* g++.dg/cpp2a/constexpr-dtor3.C: Likewise.
	* g++.dg/cpp2a/consteval3.C: Likewise.  Add effective target c++20
	and remove dg-options.


	Jakub
  

Comments

Jason Merrill Oct. 6, 2021, 3:01 a.m. UTC | #1
On 10/5/21 18:24, Jakub Jelinek wrote:
> On Tue, Oct 05, 2021 at 05:24:09PM -0400, Jason Merrill wrote:
>> That seems correct to me.  If they have static initialization, they are
>> initialized.
> 
> Ok, I've included those tests in the patch now too.
>>> @@ -8736,14 +8765,14 @@ potential_constant_expression_1 (tree t,
>>>          tmp = DECL_EXPR_DECL (t);
>>>          if (VAR_P (tmp) && !DECL_ARTIFICIAL (tmp))
>>>    	{
>>> -	  if (TREE_STATIC (tmp))
>>> +	  if (TREE_STATIC (tmp) && cxx_dialect < cxx23)
>>>    	    {
>>>    	      if (flags & tf_error)
>>>    		error_at (DECL_SOURCE_LOCATION (tmp), "%qD declared "
>>>    			  "%<static%> in %<constexpr%> context", tmp);
>>>    	      return false;
>>>    	    }
>>> -	  else if (CP_DECL_THREAD_LOCAL_P (tmp))
>>> +	  else if (CP_DECL_THREAD_LOCAL_P (tmp) && cxx_dialect < cxx23)
>>>    	    {
>>>    	      if (flags & tf_error)
>>>    		error_at (DECL_SOURCE_LOCATION (tmp), "%qD declared "
>>
>> Hmm, I wouldn't expect this hunk to be needed; if there's a control path
>> that doesn't include these declarations, we shouldn't hit these errors.
> 
> You're right, that is unnecessary, if the static/thread_local declaration
> is unconditional, we can (but aren't required to) diagnose it even when
> the constexpr function is never evaluated in constant expressions.
> But, I had to swap the TREE_STATIC and CP_DECL_THREAD_LOCAL_P checks
> because the latter implies the former and so static would be always
> printed instead of thread_local for thread_local declarations.
> 
>>> @@ -9025,7 +9054,7 @@ potential_constant_expression_1 (tree t,
>>>        case LABEL_EXPR:
>>>          t = LABEL_EXPR_LABEL (t);
>>> -      if (DECL_ARTIFICIAL (t))
>>> +      if (DECL_ARTIFICIAL (t) || cxx_dialect >= cxx23)
>>>    	return true;
>>>          else if (flags & tf_error)
>>>    	error_at (loc, "label definition is not a constant expression");
>>
>> Let's change this message to "only available with..." like the others.
> 
> Changed too.
> 
> Here is an updated patch (also after IRC chat with Jonathan changed
> the __cpp_constexpr value from 202103L that was in the paper to 202110L).
> Passes the tests that are modified so far, full bootstrap/regtest
> queued after my current one finishes.

OK.

> 2021-10-06  Jakub Jelinek  <jakub@redhat.com>
> 
> gcc/c-family/
> 	* c-cppbuiltin.c (c_cpp_builtins): For -std=c++23 predefine
> 	__cpp_constexpr to 202110L rather than 201907L.
> gcc/cp/
> 	* parser.c (cp_parser_jump_statement): Implement C++23 P2242R3.
> 	Allow goto expressions in constexpr function bodies for C++23.
> 	Adjust error message for older standards to mention it.
> 	* decl.c (start_decl): Allow static and thread_local declarations
> 	in constexpr function bodies for C++23.  Adjust error message for
> 	older standards to mention it.
> 	* constexpr.c (ensure_literal_type_for_constexpr_object): Allow
> 	declarations of variables with non-literal type in constexpr function
> 	bodies for C++23.  Adjust error message for older standards to mention
> 	it.
> 	(cxx_eval_constant_expression) <case DECL_EXPR>: Diagnose declarations
> 	of initialization of static or thread_local vars.
> 	(cxx_eval_constant_expression) <case GOTO_EXPR>: Diagnose goto
> 	statements for C++23.
> 	(potential_constant_expression_1) <case DECL_EXPR>: Swap the
> 	CP_DECL_THREAD_LOCAL_P and TREE_STATIC checks.
> 	(potential_constant_expression_1) <case LABEL_EXPR>: Allow labels for
> 	C++23.  Adjust error message for older standards to mention it.
> gcc/testsuite/
> 	* g++.dg/cpp23/feat-cxx2b.C: Expect __cpp_constexpr 202110L rather
> 	than 201907L.
> 	* g++.dg/cpp23/constexpr-nonlit1.C: New test.
> 	* g++.dg/cpp23/constexpr-nonlit2.C: New test.
> 	* g++.dg/cpp23/constexpr-nonlit3.C: New test.
> 	* g++.dg/cpp23/constexpr-nonlit4.C: New test.
> 	* g++.dg/cpp23/constexpr-nonlit5.C: New test.
> 	* g++.dg/cpp23/constexpr-nonlit6.C: New test.
> 	* g++.dg/diagnostic/constexpr1.C: Only expect some diagnostics for
> 	c++20_down.
> 	* g++.dg/cpp1y/constexpr-label.C: Likewise.
> 	* g++.dg/cpp1y/constexpr-neg1.C: Likewise.
> 	* g++.dg/cpp2a/constexpr-try5.C: Likewise.  Adjust some expected
> 	wording.
> 	* g++.dg/cpp2a/constexpr-dtor3.C: Likewise.
> 	* g++.dg/cpp2a/consteval3.C: Likewise.  Add effective target c++20
> 	and remove dg-options.
> 
> --- gcc/c-family/c-cppbuiltin.c.jj	2021-10-05 22:27:39.387959561 +0200
> +++ gcc/c-family/c-cppbuiltin.c	2021-10-05 23:47:13.865054265 +0200
> @@ -1052,7 +1052,8 @@ c_cpp_builtins (cpp_reader *pfile)
>   	  cpp_define (pfile, "__cpp_init_captures=201803L");
>   	  cpp_define (pfile, "__cpp_generic_lambdas=201707L");
>   	  cpp_define (pfile, "__cpp_designated_initializers=201707L");
> -	  cpp_define (pfile, "__cpp_constexpr=201907L");
> +	  if (cxx_dialect <= cxx20)
> +	    cpp_define (pfile, "__cpp_constexpr=201907L");
>   	  cpp_define (pfile, "__cpp_constexpr_in_decltype=201711L");
>   	  cpp_define (pfile, "__cpp_conditional_explicit=201806L");
>   	  cpp_define (pfile, "__cpp_consteval=201811L");
> @@ -1071,6 +1072,7 @@ c_cpp_builtins (cpp_reader *pfile)
>   	  /* Set feature test macros for C++23.  */
>   	  cpp_define (pfile, "__cpp_size_t_suffix=202011L");
>   	  cpp_define (pfile, "__cpp_if_consteval=202106L");
> +	  cpp_define (pfile, "__cpp_constexpr=202110L");
>   	}
>         if (flag_concepts)
>           {
> --- gcc/cp/parser.c.jj	2021-10-05 22:27:39.551957300 +0200
> +++ gcc/cp/parser.c	2021-10-05 23:47:13.895053851 +0200
> @@ -14176,9 +14176,11 @@ cp_parser_jump_statement (cp_parser* par
>   
>       case RID_GOTO:
>         if (parser->in_function_body
> -	  && DECL_DECLARED_CONSTEXPR_P (current_function_decl))
> +	  && DECL_DECLARED_CONSTEXPR_P (current_function_decl)
> +	  && cxx_dialect < cxx23)
>   	{
> -	  error ("%<goto%> in %<constexpr%> function");
> +	  error ("%<goto%> in %<constexpr%> function only available with "
> +		 "%<-std=c++2b%> or %<-std=gnu++2b%>");
>   	  cp_function_chain->invalid_constexpr = true;
>   	}
>   
> --- gcc/cp/decl.c.jj	2021-10-05 22:27:39.502957976 +0200
> +++ gcc/cp/decl.c	2021-10-05 23:47:13.916053561 +0200
> @@ -5709,17 +5709,20 @@ start_decl (const cp_declarator *declara
>       }
>   
>     if (current_function_decl && VAR_P (decl)
> -      && DECL_DECLARED_CONSTEXPR_P (current_function_decl))
> +      && DECL_DECLARED_CONSTEXPR_P (current_function_decl)
> +      && cxx_dialect < cxx23)
>       {
>         bool ok = false;
>         if (CP_DECL_THREAD_LOCAL_P (decl))
>   	error_at (DECL_SOURCE_LOCATION (decl),
> -		  "%qD declared %<thread_local%> in %qs function", decl,
> +		  "%qD declared %<thread_local%> in %qs function only "
> +		  "available with %<-std=c++2b%> or %<-std=gnu++2b%>", decl,
>   		  DECL_IMMEDIATE_FUNCTION_P (current_function_decl)
>   		  ? "consteval" : "constexpr");
>         else if (TREE_STATIC (decl))
>   	error_at (DECL_SOURCE_LOCATION (decl),
> -		  "%qD declared %<static%> in %qs function", decl,
> +		  "%qD declared %<static%> in %qs function only available "
> +		  "with %<-std=c++2b%> or %<-std=gnu++2b%>", decl,
>   		  DECL_IMMEDIATE_FUNCTION_P (current_function_decl)
>   		  ? "consteval" : "constexpr");
>         else
> --- gcc/cp/constexpr.c.jj	2021-10-05 22:27:48.009840663 +0200
> +++ gcc/cp/constexpr.c	2021-10-06 00:08:16.809608246 +0200
> @@ -109,14 +109,15 @@ ensure_literal_type_for_constexpr_object
>   	      explain_non_literal_class (type);
>   	      decl = error_mark_node;
>   	    }
> -	  else
> +	  else if (cxx_dialect < cxx23)
>   	    {
>   	      if (!is_instantiation_of_constexpr (current_function_decl))
>   		{
>   		  auto_diagnostic_group d;
>   		  error_at (DECL_SOURCE_LOCATION (decl),
>   			    "variable %qD of non-literal type %qT in "
> -			    "%<constexpr%> function", decl, type);
> +			    "%<constexpr%> function only available with "
> +			    "%<-std=c++2b%> or %<-std=gnu++2b%>", decl, type);
>   		  explain_non_literal_class (type);
>   		  decl = error_mark_node;
>   		}
> @@ -6345,6 +6346,26 @@ cxx_eval_constant_expression (const cons
>   	    r = void_node;
>   	    break;
>   	  }
> +
> +	if (VAR_P (r)
> +	    && (TREE_STATIC (r) || CP_DECL_THREAD_LOCAL_P (r))
> +	    /* Allow __FUNCTION__ etc.  */
> +	    && !DECL_ARTIFICIAL (r))
> +	  {
> +	    gcc_assert (cxx_dialect >= cxx23);
> +	    if (!ctx->quiet)
> +	      {
> +		if (CP_DECL_THREAD_LOCAL_P (r))
> +		  error_at (loc, "control passes through declaration of %qD "
> +				 "with thread storage duration", r);
> +		else
> +		  error_at (loc, "control passes through declaration of %qD "
> +				 "with static storage duration", r);
> +	      }
> +	    *non_constant_p = true;
> +	    break;
> +	  }
> +
>   	if (AGGREGATE_TYPE_P (TREE_TYPE (r))
>   	    || VECTOR_TYPE_P (TREE_TYPE (r)))
>   	  {
> @@ -7049,10 +7070,18 @@ cxx_eval_constant_expression (const cons
>         break;
>   
>       case GOTO_EXPR:
> -      *jump_target = TREE_OPERAND (t, 0);
> -      gcc_assert (breaks (jump_target) || continues (jump_target)
> -		  /* Allow for jumping to a cdtor_label.  */
> -		  || returns (jump_target));
> +      if (breaks (&TREE_OPERAND (t, 0))
> +	  || continues (&TREE_OPERAND (t, 0))
> +	  /* Allow for jumping to a cdtor_label.  */
> +	  || returns (&TREE_OPERAND (t, 0)))
> +	*jump_target = TREE_OPERAND (t, 0);
> +      else
> +	{
> +	  gcc_assert (cxx_dialect >= cxx23);
> +	  if (!ctx->quiet)
> +	    error_at (loc, "%<goto%> is not a constant expression");
> +	  *non_constant_p = true;
> +	}
>         break;
>   
>       case LOOP_EXPR:
> @@ -8736,18 +8765,18 @@ potential_constant_expression_1 (tree t,
>         tmp = DECL_EXPR_DECL (t);
>         if (VAR_P (tmp) && !DECL_ARTIFICIAL (tmp))
>   	{
> -	  if (TREE_STATIC (tmp))
> +	  if (CP_DECL_THREAD_LOCAL_P (tmp))
>   	    {
>   	      if (flags & tf_error)
>   		error_at (DECL_SOURCE_LOCATION (tmp), "%qD declared "
> -			  "%<static%> in %<constexpr%> context", tmp);
> +			  "%<thread_local%> in %<constexpr%> context", tmp);
>   	      return false;
>   	    }
> -	  else if (CP_DECL_THREAD_LOCAL_P (tmp))
> +	  else if (TREE_STATIC (tmp))
>   	    {
>   	      if (flags & tf_error)
>   		error_at (DECL_SOURCE_LOCATION (tmp), "%qD declared "
> -			  "%<thread_local%> in %<constexpr%> context", tmp);
> +			  "%<static%> in %<constexpr%> context", tmp);
>   	      return false;
>   	    }
>   	  else if (!check_for_uninitialized_const_var
> @@ -9025,10 +9054,11 @@ potential_constant_expression_1 (tree t,
>   
>       case LABEL_EXPR:
>         t = LABEL_EXPR_LABEL (t);
> -      if (DECL_ARTIFICIAL (t))
> +      if (DECL_ARTIFICIAL (t) || cxx_dialect >= cxx23)
>   	return true;
>         else if (flags & tf_error)
> -	error_at (loc, "label definition is not a constant expression");
> +	error_at (loc, "label definition in %<constexpr%> function only "
> +		       "available with %<-std=c++2b%> or %<-std=gnu++2b%>");
>         return false;
>   
>       case ANNOTATE_EXPR:
> --- gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C.jj	2021-10-05 22:27:39.594956707 +0200
> +++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C	2021-10-06 00:02:37.480296129 +0200
> @@ -134,8 +134,8 @@
>   
>   #ifndef __cpp_constexpr
>   #  error "__cpp_constexpr"
> -#elif __cpp_constexpr != 201907
> -#  error "__cpp_constexpr != 201907"
> +#elif __cpp_constexpr != 202110
> +#  error "__cpp_constexpr != 202110"
>   #endif
>   
>   #ifndef __cpp_decltype_auto
> --- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit1.C.jj	2021-10-05 23:47:13.934053312 +0200
> +++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit1.C	2021-10-05 23:52:09.879965662 +0200
> @@ -0,0 +1,68 @@
> +// P2242R3
> +// { dg-do compile { target c++14 } }
> +
> +constexpr int
> +foo ()
> +{
> +lab:		// { dg-error "label definition in 'constexpr' function only available with" "" { target c++20_down } }
> +  return 1;
> +}
> +
> +constexpr int
> +bar (int x)
> +{
> +  if (x)
> +    goto lab;	// { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } }
> +  return 1;
> +lab:
> +  return 0;
> +}
> +
> +constexpr int
> +baz (int x)
> +{
> +  if (!x)
> +    return 1;
> +  static int a;	// { dg-error "'a' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } }
> +  return ++a;	// { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } .-1 }
> +}
> +
> +constexpr int
> +qux (int x)
> +{
> +  if (!x)
> +    return 1;
> +  thread_local int a;	// { dg-error "'a' declared 'thread_local' in 'constexpr' function only available with" "" { target c++20_down } }
> +  return ++a;	// { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } .-1 }
> +}
> +
> +constexpr int
> +garply (int x)
> +{
> +  if (!x)
> +    return 1;
> +  extern thread_local int a;	// { dg-error "'a' declared 'thread_local' in 'constexpr' function only available with" "" { target c++20_down } }
> +  return ++a;
> +}
> +
> +struct S { S (); ~S (); int s; };	// { dg-message "'S' is not literal because:" "" { target c++20_down } }
> +					// { dg-message "'S' has a non-trivial destructor" "" { target c++17_down } .-1 }
> +					// { dg-message "'S' does not have 'constexpr' destructor" "" { target { c++20_only } } .-2 }
> +
> +constexpr int
> +corge (int x)
> +{
> +  if (!x)
> +    return 1;
> +  S s;			// { dg-error "variable 's' of non-literal type 'S' in 'constexpr' function only available with" "" { target c++20_down } }
> +  return 0;
> +}
> +
> +#if __cpp_constexpr >= 202110L
> +static_assert (foo ());
> +static_assert (bar (0));
> +static_assert (baz (0));
> +static_assert (qux (0));
> +static_assert (garply (0));
> +static_assert (corge (0));
> +#endif
> --- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit2.C.jj	2021-10-05 23:47:13.934053312 +0200
> +++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit2.C	2021-10-06 00:14:12.586693301 +0200
> @@ -0,0 +1,54 @@
> +// P2242R3
> +// { dg-do compile }
> +// { dg-options "-std=c++2b" }
> +
> +constexpr int
> +foo ()
> +{
> +lab:
> +  return 1;
> +}
> +
> +constexpr int
> +bar (int x)
> +{
> +  if (x)
> +    goto lab;		// { dg-error "'goto' is not a constant expression" }
> +  return 1;
> +lab:
> +  return 0;
> +}
> +
> +constexpr int
> +baz (int x)
> +{
> +  if (!x)
> +    return 1;
> +  static int a;		// { dg-error "control passes through declaration of 'a' with static storage duration" }
> +  return ++a;
> +}
> +
> +constexpr int
> +qux (int x)
> +{
> +  if (!x)
> +    return 1;
> +  thread_local int a;	// { dg-error "control passes through declaration of 'a' with thread storage duration" }
> +  return ++a;
> +}
> +
> +struct S { S (); ~S (); int s; };	// { dg-message "'S::S\\\(\\\)' declared here" }
> +
> +constexpr int
> +corge (int x)
> +{
> +  if (!x)
> +    return 1;
> +  S s;			// { dg-error "call to non-'constexpr' function 'S::S\\\(\\\)'" }
> +  return 0;
> +}
> +
> +constexpr int a = bar (1);	// { dg-message "in 'constexpr' expansion of" }
> +constexpr int b = baz (1);	// { dg-message "in 'constexpr' expansion of" }
> +constexpr int c = qux (1);	// { dg-message "in 'constexpr' expansion of" }
> +constexpr int d = corge (1);	// { dg-message "in 'constexpr' expansion of" }
> --- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C.jj	2021-10-05 23:47:13.934053312 +0200
> +++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C	2021-10-05 23:47:13.934053312 +0200
> @@ -0,0 +1,10 @@
> +// P2242R3
> +// { dg-do compile { target c++14 } }
> +
> +constexpr int
> +foo ()
> +{
> +  goto lab;	// { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } }
> +lab:		// { dg-error "'goto' is not a constant expression" "" { target { c++23 } } .-1 }
> +  return 1;
> +}
> --- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit4.C.jj	2021-10-05 23:58:42.437543278 +0200
> +++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit4.C	2021-10-05 23:55:57.699818980 +0200
> @@ -0,0 +1,57 @@
> +// { dg-do compile }
> +// { dg-options "-std=c++2b" }
> +
> +int qux ();
> +
> +constexpr int
> +foo (int x)
> +{
> +  switch (x)
> +    {
> +      static int v = qux ();
> +    case 12:
> +      return 1;
> +    }
> +  return 0;
> +}
> +
> +constexpr int
> +bar (int x)
> +{
> +  switch (x)
> +    {
> +      thread_local int v = qux ();
> +    case 12:
> +      return 1;
> +    }
> +  return 0;
> +}
> +
> +constexpr int
> +baz (int x)
> +{
> +  switch (x)
> +    {
> +      static const int v = qux ();	// { dg-message "'v' was not initialized with a constant expression" }
> +    case 12:
> +      return v;
> +    }
> +  return 0;
> +}
> +
> +constexpr int
> +corge (int x)
> +{
> +  switch (x)
> +    {
> +      const thread_local int v = qux ();	// { dg-message "'v' was not initialized with a constant expression" }
> +    case 12:
> +      return v;
> +    }
> +  return 0;
> +}
> +
> +constexpr int a = foo (12);
> +constexpr int b = bar (12);
> +constexpr int c = baz (12);		// { dg-error "the value of 'v' is not usable in a constant expression" }
> +constexpr int d = corge (12);		// { dg-error "the value of 'v' is not usable in a constant expression" }
> --- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit5.C.jj	2021-10-05 23:58:47.250476787 +0200
> +++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit5.C	2021-10-05 23:58:12.130961967 +0200
> @@ -0,0 +1,57 @@
> +// { dg-do compile }
> +// { dg-options "-std=c++2b" }
> +
> +int qux ();
> +
> +constexpr int
> +foo (int x)
> +{
> +  switch (x)
> +    {
> +      static const int v = 6;
> +    case 12:
> +      return v;
> +    }
> +  return 0;
> +}
> +
> +constexpr int
> +bar (int x)
> +{
> +  switch (x)
> +    {
> +      thread_local const int v = 7;
> +    case 12:
> +      return 7;
> +    }
> +  return 0;
> +}
> +
> +constexpr int
> +baz (int x)
> +{
> +  switch (x)
> +    {
> +      static int v = 6;	// { dg-message "int v' is not const" }
> +    case 12:
> +      return v;
> +    }
> +  return 0;
> +}
> +
> +constexpr int
> +corge (int x)
> +{
> +  switch (x)
> +    {
> +      thread_local int v = 6;	// { dg-message "int v' is not const" }
> +    case 12:
> +      return v;
> +    }
> +  return 0;
> +}
> +
> +constexpr int a = foo (12);
> +constexpr int b = bar (12);
> +constexpr int c = baz (12);		// { dg-error "the value of 'v' is not usable in a constant expression" }
> +constexpr int d = corge (12);		// { dg-error "the value of 'v' is not usable in a constant expression" }
> --- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C.jj	2021-10-06 00:12:19.927249541 +0200
> +++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C	2021-10-06 00:15:32.680587909 +0200
> @@ -0,0 +1,25 @@
> +// P2242R3
> +// { dg-do compile }
> +// { dg-options "-std=c++2b" }
> +
> +constexpr int
> +foo ()
> +{
> +  goto lab;		// { dg-error "'goto' is not a constant expression" }
> +lab:
> +  return 1;
> +}
> +
> +constexpr int
> +bar ()
> +{
> +  static int a;		// { dg-error "'a' declared 'static' in 'constexpr' context" }
> +  return ++a;
> +}
> +
> +constexpr int
> +baz (int x)
> +{
> +  thread_local int a;	// { dg-error "'a' declared 'thread_local' in 'constexpr' context" }
> +  return ++a;
> +}
> --- gcc/testsuite/g++.dg/diagnostic/constexpr1.C.jj	2020-01-12 11:54:37.155402214 +0100
> +++ gcc/testsuite/g++.dg/diagnostic/constexpr1.C	2021-10-06 00:08:47.990177482 +0200
> @@ -1,5 +1,7 @@
>   // { dg-do compile { target c++11 } }
>   
> -constexpr int foo() { thread_local int i __attribute__((unused)) {}; return 1; }  // { dg-error "40:.i. declared .thread_local." }
> +constexpr int foo() { thread_local int i __attribute__((unused)) {}; return 1; }  // { dg-error "40:.i. declared .thread_local." "" { target c++20_down } }
> +// { dg-error "40:.i. declared .thread_local. in .constexpr. context" "" { target c++23 } .-1 }
>   
> -constexpr int bar() { static int i __attribute__((unused)) {}; return 1; }  // { dg-error "34:.i. declared .static." }
> +constexpr int bar() { static int i __attribute__((unused)) {}; return 1; }  // { dg-error "34:.i. declared .static." "" { target c++20_down } }
> +// { dg-error "34:.i. declared .static. in .constexpr. context" "" { target c++23 } .-1 }
> --- gcc/testsuite/g++.dg/cpp1y/constexpr-label.C.jj	2020-11-22 19:11:44.103588521 +0100
> +++ gcc/testsuite/g++.dg/cpp1y/constexpr-label.C	2021-10-05 23:52:09.879965662 +0200
> @@ -4,6 +4,6 @@
>   constexpr int
>   f ()
>   {
> -x: // { dg-error "label definition is not a constant expression" }
> +x: // { dg-error "label definition in 'constexpr' function only available with" "" { target c++20_down } }
>     return 42;
>   }
> --- gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C.jj	2020-01-12 11:54:37.115402818 +0100
> +++ gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C	2021-10-06 00:01:48.736969524 +0200
> @@ -4,12 +4,12 @@ struct A { A(); };
>   
>   constexpr int f(int i) {
>     static int j = i;		// { dg-error "static" }
> -  thread_local int l = i;	// { dg-error "thread_local" }
> -  goto foo;			// { dg-error "goto" }
> +  thread_local int l = i;	// { dg-error "thread_local" "" { target c++20_down } }
> +  goto foo;			// { dg-error "goto" "" { target c++20_down } }
>    foo:
>     asm("foo");			// { dg-error "asm" "" { target c++17_down } }
>     int k;			// { dg-error "uninitialized" "" { target c++17_down } }
> -  A a;				// { dg-error "non-literal" }
> +  A a;				// { dg-error "non-literal" "" { target c++20_down } }
>     return i;
>   }
>   
> --- gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C.jj	2020-01-12 11:54:37.141402425 +0100
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C	2021-10-06 00:11:41.897774924 +0200
> @@ -5,14 +5,15 @@
>   constexpr int foo ()
>   try {			// { dg-warning "function-try-block body of 'constexpr' function only available with" "" { target c++17_down } }
>     int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } }
> -  static double b = 1.0;// { dg-error "'b' declared 'static' in 'constexpr' function" }
> -  goto l;		// { dg-error "'goto' in 'constexpr' function" }
> +  static double b = 1.0;// { dg-error "'b' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } }
> +			// { dg-error "'b' declared 'static' in 'constexpr' context" "" { target c++23 } .-1 }
> +  goto l;		// { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } }
>     l:;
>     return 0;
>   } catch (...) {
>     long int c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" "" { target c++17_down } }
> -  static float d = 2.0f;// { dg-error "'d' declared 'static' in 'constexpr' function" }
> -  goto l2;		// { dg-error "'goto' in 'constexpr' function" }
> +  static float d = 2.0f;// { dg-error "'d' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } }
> +  goto l2;		// { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } }
>     l2:;
>     return -1;
>   }
> @@ -20,20 +21,21 @@ try {			// { dg-warning "function-try-bl
>   constexpr int bar ()
>   {
>     int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } }
> -  static long double b = 3.0;// { dg-error "'b' declared 'static' in 'constexpr' function" }
> -  goto l;		// { dg-error "'goto' in 'constexpr' function" }
> +  static long double b = 3.0;// { dg-error "'b' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } }
> +			// { dg-error "'b' declared 'static' in 'constexpr' context" "" { target c++23 } .-1 }
> +  goto l;		// { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } }
>     l:;
>     try {			// { dg-warning "'try' in 'constexpr' function only available with" "" { target c++17_down } }
>       short c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" "" { target c++17_down } }
> -    static float d;	// { dg-error "'d' declared 'static' in 'constexpr' function" }
> +    static float d;	// { dg-error "'d' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } }
>   			// { dg-error "uninitialized variable 'd' in 'constexpr' function" "" { target c++17_down } .-1 }
> -    goto l2;		// { dg-error "'goto' in 'constexpr' function" }
> +    goto l2;		// { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } }
>       l2:;
>       return 0;
>     } catch (int) {
>       char e;		// { dg-error "uninitialized variable 'e' in 'constexpr' function" "" { target c++17_down } }
> -    static int f = 5;	// { dg-error "'f' declared 'static' in 'constexpr' function" }
> -    goto l3;		// { dg-error "'goto' in 'constexpr' function" }
> +    static int f = 5;	// { dg-error "'f' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } }
> +    goto l3;		// { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } }
>       l3:;
>       return 1;
>     }
> --- gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C.jj	2021-04-14 10:48:41.330103579 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C	2021-10-05 23:47:13.970052815 +0200
> @@ -180,6 +180,6 @@ f7 ()
>   constexpr int
>   f8 ()
>   {
> -  T t4;			// { dg-error "variable 't4' of non-literal type 'T' in 'constexpr' function" }
> +  T t4;			// { dg-error "variable 't4' of non-literal type 'T' in 'constexpr' function only available with" "" { target c++20_down } }
>     return 0;
>   }
> --- gcc/testsuite/g++.dg/cpp2a/consteval3.C.jj	2020-02-28 17:34:56.393567559 +0100
> +++ gcc/testsuite/g++.dg/cpp2a/consteval3.C	2021-10-06 00:10:50.161489668 +0200
> @@ -1,5 +1,4 @@
> -// { dg-do compile }
> -// { dg-options "-std=c++2a" }
> +// { dg-do compile { target c++20 } }
>   
>   struct S { S () : a (0), b (1) {} int a, b; };
>   int f1 ();		// { dg-message "previous declaration 'int f1\\(\\)'" }
> @@ -57,7 +56,8 @@ template consteval float f12 (float x);
>   consteval int
>   f13 (int x)
>   {
> -  static int a = 5;		// { dg-error "'a' declared 'static' in 'consteval' function" }
> -  thread_local int b = 6;	// { dg-error "'b' declared 'thread_local' in 'consteval' function" }
> +  static int a = 5;		// { dg-error "'a' declared 'static' in 'consteval' function only available with" "" { target c++20_only } }
> +				// { dg-error "'a' declared 'static' in 'constexpr' context" "" { target c++23 } .-1 }
> +  thread_local int b = 6;	// { dg-error "'b' declared 'thread_local' in 'consteval' function only available with" "" { target c++20_only } }
>     return x;
>   }
> 
> 	Jakub
>
  

Patch

--- gcc/c-family/c-cppbuiltin.c.jj	2021-10-05 22:27:39.387959561 +0200
+++ gcc/c-family/c-cppbuiltin.c	2021-10-05 23:47:13.865054265 +0200
@@ -1052,7 +1052,8 @@  c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_init_captures=201803L");
 	  cpp_define (pfile, "__cpp_generic_lambdas=201707L");
 	  cpp_define (pfile, "__cpp_designated_initializers=201707L");
-	  cpp_define (pfile, "__cpp_constexpr=201907L");
+	  if (cxx_dialect <= cxx20)
+	    cpp_define (pfile, "__cpp_constexpr=201907L");
 	  cpp_define (pfile, "__cpp_constexpr_in_decltype=201711L");
 	  cpp_define (pfile, "__cpp_conditional_explicit=201806L");
 	  cpp_define (pfile, "__cpp_consteval=201811L");
@@ -1071,6 +1072,7 @@  c_cpp_builtins (cpp_reader *pfile)
 	  /* Set feature test macros for C++23.  */
 	  cpp_define (pfile, "__cpp_size_t_suffix=202011L");
 	  cpp_define (pfile, "__cpp_if_consteval=202106L");
+	  cpp_define (pfile, "__cpp_constexpr=202110L");
 	}
       if (flag_concepts)
         {
--- gcc/cp/parser.c.jj	2021-10-05 22:27:39.551957300 +0200
+++ gcc/cp/parser.c	2021-10-05 23:47:13.895053851 +0200
@@ -14176,9 +14176,11 @@  cp_parser_jump_statement (cp_parser* par
 
     case RID_GOTO:
       if (parser->in_function_body
-	  && DECL_DECLARED_CONSTEXPR_P (current_function_decl))
+	  && DECL_DECLARED_CONSTEXPR_P (current_function_decl)
+	  && cxx_dialect < cxx23)
 	{
-	  error ("%<goto%> in %<constexpr%> function");
+	  error ("%<goto%> in %<constexpr%> function only available with "
+		 "%<-std=c++2b%> or %<-std=gnu++2b%>");
 	  cp_function_chain->invalid_constexpr = true;
 	}
 
--- gcc/cp/decl.c.jj	2021-10-05 22:27:39.502957976 +0200
+++ gcc/cp/decl.c	2021-10-05 23:47:13.916053561 +0200
@@ -5709,17 +5709,20 @@  start_decl (const cp_declarator *declara
     }
 
   if (current_function_decl && VAR_P (decl)
-      && DECL_DECLARED_CONSTEXPR_P (current_function_decl))
+      && DECL_DECLARED_CONSTEXPR_P (current_function_decl)
+      && cxx_dialect < cxx23)
     {
       bool ok = false;
       if (CP_DECL_THREAD_LOCAL_P (decl))
 	error_at (DECL_SOURCE_LOCATION (decl),
-		  "%qD declared %<thread_local%> in %qs function", decl,
+		  "%qD declared %<thread_local%> in %qs function only "
+		  "available with %<-std=c++2b%> or %<-std=gnu++2b%>", decl,
 		  DECL_IMMEDIATE_FUNCTION_P (current_function_decl)
 		  ? "consteval" : "constexpr");
       else if (TREE_STATIC (decl))
 	error_at (DECL_SOURCE_LOCATION (decl),
-		  "%qD declared %<static%> in %qs function", decl,
+		  "%qD declared %<static%> in %qs function only available "
+		  "with %<-std=c++2b%> or %<-std=gnu++2b%>", decl,
 		  DECL_IMMEDIATE_FUNCTION_P (current_function_decl)
 		  ? "consteval" : "constexpr");
       else
--- gcc/cp/constexpr.c.jj	2021-10-05 22:27:48.009840663 +0200
+++ gcc/cp/constexpr.c	2021-10-06 00:08:16.809608246 +0200
@@ -109,14 +109,15 @@  ensure_literal_type_for_constexpr_object
 	      explain_non_literal_class (type);
 	      decl = error_mark_node;
 	    }
-	  else
+	  else if (cxx_dialect < cxx23)
 	    {
 	      if (!is_instantiation_of_constexpr (current_function_decl))
 		{
 		  auto_diagnostic_group d;
 		  error_at (DECL_SOURCE_LOCATION (decl),
 			    "variable %qD of non-literal type %qT in "
-			    "%<constexpr%> function", decl, type);
+			    "%<constexpr%> function only available with "
+			    "%<-std=c++2b%> or %<-std=gnu++2b%>", decl, type);
 		  explain_non_literal_class (type);
 		  decl = error_mark_node;
 		}
@@ -6345,6 +6346,26 @@  cxx_eval_constant_expression (const cons
 	    r = void_node;
 	    break;
 	  }
+
+	if (VAR_P (r)
+	    && (TREE_STATIC (r) || CP_DECL_THREAD_LOCAL_P (r))
+	    /* Allow __FUNCTION__ etc.  */
+	    && !DECL_ARTIFICIAL (r))
+	  {
+	    gcc_assert (cxx_dialect >= cxx23);
+	    if (!ctx->quiet)
+	      {
+		if (CP_DECL_THREAD_LOCAL_P (r))
+		  error_at (loc, "control passes through declaration of %qD "
+				 "with thread storage duration", r);
+		else
+		  error_at (loc, "control passes through declaration of %qD "
+				 "with static storage duration", r);
+	      }
+	    *non_constant_p = true;
+	    break;
+	  }
+
 	if (AGGREGATE_TYPE_P (TREE_TYPE (r))
 	    || VECTOR_TYPE_P (TREE_TYPE (r)))
 	  {
@@ -7049,10 +7070,18 @@  cxx_eval_constant_expression (const cons
       break;
 
     case GOTO_EXPR:
-      *jump_target = TREE_OPERAND (t, 0);
-      gcc_assert (breaks (jump_target) || continues (jump_target)
-		  /* Allow for jumping to a cdtor_label.  */
-		  || returns (jump_target));
+      if (breaks (&TREE_OPERAND (t, 0))
+	  || continues (&TREE_OPERAND (t, 0))
+	  /* Allow for jumping to a cdtor_label.  */
+	  || returns (&TREE_OPERAND (t, 0)))
+	*jump_target = TREE_OPERAND (t, 0);
+      else
+	{
+	  gcc_assert (cxx_dialect >= cxx23);
+	  if (!ctx->quiet)
+	    error_at (loc, "%<goto%> is not a constant expression");
+	  *non_constant_p = true;
+	}
       break;
 
     case LOOP_EXPR:
@@ -8736,18 +8765,18 @@  potential_constant_expression_1 (tree t,
       tmp = DECL_EXPR_DECL (t);
       if (VAR_P (tmp) && !DECL_ARTIFICIAL (tmp))
 	{
-	  if (TREE_STATIC (tmp))
+	  if (CP_DECL_THREAD_LOCAL_P (tmp))
 	    {
 	      if (flags & tf_error)
 		error_at (DECL_SOURCE_LOCATION (tmp), "%qD declared "
-			  "%<static%> in %<constexpr%> context", tmp);
+			  "%<thread_local%> in %<constexpr%> context", tmp);
 	      return false;
 	    }
-	  else if (CP_DECL_THREAD_LOCAL_P (tmp))
+	  else if (TREE_STATIC (tmp))
 	    {
 	      if (flags & tf_error)
 		error_at (DECL_SOURCE_LOCATION (tmp), "%qD declared "
-			  "%<thread_local%> in %<constexpr%> context", tmp);
+			  "%<static%> in %<constexpr%> context", tmp);
 	      return false;
 	    }
 	  else if (!check_for_uninitialized_const_var
@@ -9025,10 +9054,11 @@  potential_constant_expression_1 (tree t,
 
     case LABEL_EXPR:
       t = LABEL_EXPR_LABEL (t);
-      if (DECL_ARTIFICIAL (t))
+      if (DECL_ARTIFICIAL (t) || cxx_dialect >= cxx23)
 	return true;
       else if (flags & tf_error)
-	error_at (loc, "label definition is not a constant expression");
+	error_at (loc, "label definition in %<constexpr%> function only "
+		       "available with %<-std=c++2b%> or %<-std=gnu++2b%>");
       return false;
 
     case ANNOTATE_EXPR:
--- gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C.jj	2021-10-05 22:27:39.594956707 +0200
+++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C	2021-10-06 00:02:37.480296129 +0200
@@ -134,8 +134,8 @@ 
 
 #ifndef __cpp_constexpr
 #  error "__cpp_constexpr"
-#elif __cpp_constexpr != 201907
-#  error "__cpp_constexpr != 201907"
+#elif __cpp_constexpr != 202110
+#  error "__cpp_constexpr != 202110"
 #endif
 
 #ifndef __cpp_decltype_auto
--- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit1.C.jj	2021-10-05 23:47:13.934053312 +0200
+++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit1.C	2021-10-05 23:52:09.879965662 +0200
@@ -0,0 +1,68 @@ 
+// P2242R3
+// { dg-do compile { target c++14 } }
+
+constexpr int
+foo ()
+{
+lab:		// { dg-error "label definition in 'constexpr' function only available with" "" { target c++20_down } }
+  return 1;
+}
+
+constexpr int
+bar (int x)
+{
+  if (x)
+    goto lab;	// { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } }
+  return 1;
+lab:
+  return 0;
+}
+
+constexpr int
+baz (int x)
+{
+  if (!x)
+    return 1;
+  static int a;	// { dg-error "'a' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } }
+  return ++a;	// { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } .-1 }
+}
+
+constexpr int
+qux (int x)
+{
+  if (!x)
+    return 1;
+  thread_local int a;	// { dg-error "'a' declared 'thread_local' in 'constexpr' function only available with" "" { target c++20_down } }
+  return ++a;	// { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } .-1 }
+}
+
+constexpr int
+garply (int x)
+{
+  if (!x)
+    return 1;
+  extern thread_local int a;	// { dg-error "'a' declared 'thread_local' in 'constexpr' function only available with" "" { target c++20_down } }
+  return ++a;
+}
+
+struct S { S (); ~S (); int s; };	// { dg-message "'S' is not literal because:" "" { target c++20_down } }
+					// { dg-message "'S' has a non-trivial destructor" "" { target c++17_down } .-1 }
+					// { dg-message "'S' does not have 'constexpr' destructor" "" { target { c++20_only } } .-2 }
+
+constexpr int
+corge (int x)
+{
+  if (!x)
+    return 1;
+  S s;			// { dg-error "variable 's' of non-literal type 'S' in 'constexpr' function only available with" "" { target c++20_down } }
+  return 0;
+}
+
+#if __cpp_constexpr >= 202110L
+static_assert (foo ());
+static_assert (bar (0));
+static_assert (baz (0));
+static_assert (qux (0));
+static_assert (garply (0));
+static_assert (corge (0));
+#endif
--- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit2.C.jj	2021-10-05 23:47:13.934053312 +0200
+++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit2.C	2021-10-06 00:14:12.586693301 +0200
@@ -0,0 +1,54 @@ 
+// P2242R3
+// { dg-do compile }
+// { dg-options "-std=c++2b" }
+
+constexpr int
+foo ()
+{
+lab:
+  return 1;
+}
+
+constexpr int
+bar (int x)
+{
+  if (x)
+    goto lab;		// { dg-error "'goto' is not a constant expression" }
+  return 1;
+lab:
+  return 0;
+}
+
+constexpr int
+baz (int x)
+{
+  if (!x)
+    return 1;
+  static int a;		// { dg-error "control passes through declaration of 'a' with static storage duration" }
+  return ++a;
+}
+
+constexpr int
+qux (int x)
+{
+  if (!x)
+    return 1;
+  thread_local int a;	// { dg-error "control passes through declaration of 'a' with thread storage duration" }
+  return ++a;
+}
+
+struct S { S (); ~S (); int s; };	// { dg-message "'S::S\\\(\\\)' declared here" }
+
+constexpr int
+corge (int x)
+{
+  if (!x)
+    return 1;
+  S s;			// { dg-error "call to non-'constexpr' function 'S::S\\\(\\\)'" }
+  return 0;
+}
+
+constexpr int a = bar (1);	// { dg-message "in 'constexpr' expansion of" }
+constexpr int b = baz (1);	// { dg-message "in 'constexpr' expansion of" }
+constexpr int c = qux (1);	// { dg-message "in 'constexpr' expansion of" }
+constexpr int d = corge (1);	// { dg-message "in 'constexpr' expansion of" }
--- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C.jj	2021-10-05 23:47:13.934053312 +0200
+++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C	2021-10-05 23:47:13.934053312 +0200
@@ -0,0 +1,10 @@ 
+// P2242R3
+// { dg-do compile { target c++14 } }
+
+constexpr int
+foo ()
+{
+  goto lab;	// { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } }
+lab:		// { dg-error "'goto' is not a constant expression" "" { target { c++23 } } .-1 }
+  return 1;
+}
--- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit4.C.jj	2021-10-05 23:58:42.437543278 +0200
+++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit4.C	2021-10-05 23:55:57.699818980 +0200
@@ -0,0 +1,57 @@ 
+// { dg-do compile }
+// { dg-options "-std=c++2b" }
+
+int qux ();
+
+constexpr int
+foo (int x)
+{
+  switch (x)
+    {
+      static int v = qux ();
+    case 12:
+      return 1;
+    }
+  return 0;
+}
+
+constexpr int
+bar (int x)
+{
+  switch (x)
+    {
+      thread_local int v = qux ();
+    case 12:
+      return 1;
+    }
+  return 0;
+}
+
+constexpr int
+baz (int x)
+{
+  switch (x)
+    {
+      static const int v = qux ();	// { dg-message "'v' was not initialized with a constant expression" }
+    case 12:
+      return v;
+    }
+  return 0;
+}
+
+constexpr int
+corge (int x)
+{
+  switch (x)
+    {
+      const thread_local int v = qux ();	// { dg-message "'v' was not initialized with a constant expression" }
+    case 12:
+      return v;
+    }
+  return 0;
+}
+
+constexpr int a = foo (12);
+constexpr int b = bar (12);
+constexpr int c = baz (12);		// { dg-error "the value of 'v' is not usable in a constant expression" }
+constexpr int d = corge (12);		// { dg-error "the value of 'v' is not usable in a constant expression" }
--- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit5.C.jj	2021-10-05 23:58:47.250476787 +0200
+++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit5.C	2021-10-05 23:58:12.130961967 +0200
@@ -0,0 +1,57 @@ 
+// { dg-do compile }
+// { dg-options "-std=c++2b" }
+
+int qux ();
+
+constexpr int
+foo (int x)
+{
+  switch (x)
+    {
+      static const int v = 6;
+    case 12:
+      return v;
+    }
+  return 0;
+}
+
+constexpr int
+bar (int x)
+{
+  switch (x)
+    {
+      thread_local const int v = 7;
+    case 12:
+      return 7;
+    }
+  return 0;
+}
+
+constexpr int
+baz (int x)
+{
+  switch (x)
+    {
+      static int v = 6;	// { dg-message "int v' is not const" }
+    case 12:
+      return v;
+    }
+  return 0;
+}
+
+constexpr int
+corge (int x)
+{
+  switch (x)
+    {
+      thread_local int v = 6;	// { dg-message "int v' is not const" }
+    case 12:
+      return v;
+    }
+  return 0;
+}
+
+constexpr int a = foo (12);
+constexpr int b = bar (12);
+constexpr int c = baz (12);		// { dg-error "the value of 'v' is not usable in a constant expression" }
+constexpr int d = corge (12);		// { dg-error "the value of 'v' is not usable in a constant expression" }
--- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C.jj	2021-10-06 00:12:19.927249541 +0200
+++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C	2021-10-06 00:15:32.680587909 +0200
@@ -0,0 +1,25 @@ 
+// P2242R3
+// { dg-do compile }
+// { dg-options "-std=c++2b" }
+
+constexpr int
+foo ()
+{
+  goto lab;		// { dg-error "'goto' is not a constant expression" }
+lab:
+  return 1;
+}
+
+constexpr int
+bar ()
+{
+  static int a;		// { dg-error "'a' declared 'static' in 'constexpr' context" }
+  return ++a;
+}
+
+constexpr int
+baz (int x)
+{
+  thread_local int a;	// { dg-error "'a' declared 'thread_local' in 'constexpr' context" }
+  return ++a;
+}
--- gcc/testsuite/g++.dg/diagnostic/constexpr1.C.jj	2020-01-12 11:54:37.155402214 +0100
+++ gcc/testsuite/g++.dg/diagnostic/constexpr1.C	2021-10-06 00:08:47.990177482 +0200
@@ -1,5 +1,7 @@ 
 // { dg-do compile { target c++11 } }
 
-constexpr int foo() { thread_local int i __attribute__((unused)) {}; return 1; }  // { dg-error "40:.i. declared .thread_local." }
+constexpr int foo() { thread_local int i __attribute__((unused)) {}; return 1; }  // { dg-error "40:.i. declared .thread_local." "" { target c++20_down } }
+// { dg-error "40:.i. declared .thread_local. in .constexpr. context" "" { target c++23 } .-1 }
 
-constexpr int bar() { static int i __attribute__((unused)) {}; return 1; }  // { dg-error "34:.i. declared .static." }
+constexpr int bar() { static int i __attribute__((unused)) {}; return 1; }  // { dg-error "34:.i. declared .static." "" { target c++20_down } }
+// { dg-error "34:.i. declared .static. in .constexpr. context" "" { target c++23 } .-1 }
--- gcc/testsuite/g++.dg/cpp1y/constexpr-label.C.jj	2020-11-22 19:11:44.103588521 +0100
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-label.C	2021-10-05 23:52:09.879965662 +0200
@@ -4,6 +4,6 @@ 
 constexpr int
 f ()
 {
-x: // { dg-error "label definition is not a constant expression" }
+x: // { dg-error "label definition in 'constexpr' function only available with" "" { target c++20_down } }
   return 42;
 }
--- gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C.jj	2020-01-12 11:54:37.115402818 +0100
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C	2021-10-06 00:01:48.736969524 +0200
@@ -4,12 +4,12 @@  struct A { A(); };
 
 constexpr int f(int i) {
   static int j = i;		// { dg-error "static" }
-  thread_local int l = i;	// { dg-error "thread_local" }
-  goto foo;			// { dg-error "goto" }
+  thread_local int l = i;	// { dg-error "thread_local" "" { target c++20_down } }
+  goto foo;			// { dg-error "goto" "" { target c++20_down } }
  foo:
   asm("foo");			// { dg-error "asm" "" { target c++17_down } }
   int k;			// { dg-error "uninitialized" "" { target c++17_down } }
-  A a;				// { dg-error "non-literal" }
+  A a;				// { dg-error "non-literal" "" { target c++20_down } }
   return i;
 }
 
--- gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C.jj	2020-01-12 11:54:37.141402425 +0100
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C	2021-10-06 00:11:41.897774924 +0200
@@ -5,14 +5,15 @@ 
 constexpr int foo ()
 try {			// { dg-warning "function-try-block body of 'constexpr' function only available with" "" { target c++17_down } }
   int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } }
-  static double b = 1.0;// { dg-error "'b' declared 'static' in 'constexpr' function" }
-  goto l;		// { dg-error "'goto' in 'constexpr' function" }
+  static double b = 1.0;// { dg-error "'b' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } }
+			// { dg-error "'b' declared 'static' in 'constexpr' context" "" { target c++23 } .-1 }
+  goto l;		// { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } }
   l:;
   return 0;
 } catch (...) {
   long int c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" "" { target c++17_down } }
-  static float d = 2.0f;// { dg-error "'d' declared 'static' in 'constexpr' function" }
-  goto l2;		// { dg-error "'goto' in 'constexpr' function" }
+  static float d = 2.0f;// { dg-error "'d' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } }
+  goto l2;		// { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } }
   l2:;
   return -1;
 }
@@ -20,20 +21,21 @@  try {			// { dg-warning "function-try-bl
 constexpr int bar ()
 {
   int a;		// { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } }
-  static long double b = 3.0;// { dg-error "'b' declared 'static' in 'constexpr' function" }
-  goto l;		// { dg-error "'goto' in 'constexpr' function" }
+  static long double b = 3.0;// { dg-error "'b' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } }
+			// { dg-error "'b' declared 'static' in 'constexpr' context" "" { target c++23 } .-1 }
+  goto l;		// { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } }
   l:;
   try {			// { dg-warning "'try' in 'constexpr' function only available with" "" { target c++17_down } }
     short c;		// { dg-error "uninitialized variable 'c' in 'constexpr' function" "" { target c++17_down } }
-    static float d;	// { dg-error "'d' declared 'static' in 'constexpr' function" }
+    static float d;	// { dg-error "'d' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } }
 			// { dg-error "uninitialized variable 'd' in 'constexpr' function" "" { target c++17_down } .-1 }
-    goto l2;		// { dg-error "'goto' in 'constexpr' function" }
+    goto l2;		// { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } }
     l2:;
     return 0;
   } catch (int) {
     char e;		// { dg-error "uninitialized variable 'e' in 'constexpr' function" "" { target c++17_down } }
-    static int f = 5;	// { dg-error "'f' declared 'static' in 'constexpr' function" }
-    goto l3;		// { dg-error "'goto' in 'constexpr' function" }
+    static int f = 5;	// { dg-error "'f' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } }
+    goto l3;		// { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } }
     l3:;
     return 1;
   }
--- gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C.jj	2021-04-14 10:48:41.330103579 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C	2021-10-05 23:47:13.970052815 +0200
@@ -180,6 +180,6 @@  f7 ()
 constexpr int
 f8 ()
 {
-  T t4;			// { dg-error "variable 't4' of non-literal type 'T' in 'constexpr' function" }
+  T t4;			// { dg-error "variable 't4' of non-literal type 'T' in 'constexpr' function only available with" "" { target c++20_down } }
   return 0;
 }
--- gcc/testsuite/g++.dg/cpp2a/consteval3.C.jj	2020-02-28 17:34:56.393567559 +0100
+++ gcc/testsuite/g++.dg/cpp2a/consteval3.C	2021-10-06 00:10:50.161489668 +0200
@@ -1,5 +1,4 @@ 
-// { dg-do compile }
-// { dg-options "-std=c++2a" }
+// { dg-do compile { target c++20 } }
 
 struct S { S () : a (0), b (1) {} int a, b; };
 int f1 ();		// { dg-message "previous declaration 'int f1\\(\\)'" }
@@ -57,7 +56,8 @@  template consteval float f12 (float x);
 consteval int
 f13 (int x)
 {
-  static int a = 5;		// { dg-error "'a' declared 'static' in 'consteval' function" }
-  thread_local int b = 6;	// { dg-error "'b' declared 'thread_local' in 'consteval' function" }
+  static int a = 5;		// { dg-error "'a' declared 'static' in 'consteval' function only available with" "" { target c++20_only } }
+				// { dg-error "'a' declared 'static' in 'constexpr' context" "" { target c++23 } .-1 }
+  thread_local int b = 6;	// { dg-error "'b' declared 'thread_local' in 'consteval' function only available with" "" { target c++20_only } }
   return x;
 }