diff mbox series

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

Message ID 20211005123910.GW304296@tucnak
State New
Headers show
Series c++: Implement C++23 P2242R3 - Non-literal variables (and labels and gotos) in constexpr functions | expand

Commit Message

Jakub Jelinek Oct. 5, 2021, 12:39 p.m. UTC
Hi!

The following patch implements C++23 P2242R3 - Non-literal variables
(and labels and gotos) in constexpr functions.
I think it is mostly straightforward, don't diagnose certain
statements/declarations just because of their presence in
constexpr/consteval functions, but (except for the non-literal type
var declarations which ought to be caught by e.g. constructor or
destructor call during evaluation not being constexpr and for
labels which are now always allowed) diagnose it during constexpr
evaluation.

The only unclear thing to me is "a control flow that passes through a
declaration".  The patch implements diagnostics when encountering a
DECL_EXPR for such variables.  But because constexpr/consteval functions
support switch statements, another thing that can happen is jump over
such a declaration.  Consider e.g. following testcase (not included
in the patch).  When evaluating foo (12) or bar (12) the patch
accepts those as constant expressions, eventhough it jumps across such
declarations.  For baz (12) and corge (12) it rejects them not
because of jumping across such declarations, but because those
static or thread_local variables aren't initialized with constant
expressions and are accessed.  If baz/corge is modified not to have
const keyword, they are rejected because the vars aren't aren't const.
If they have const keyword and are initialized by constant expression,
accesses to those vars are accepted.  Is that ok?

So far regtested with
GXX_TESTSUITE_STDS=98,11,14,17,20,2b make -j32 -k check-g++
ok for trunk if it passes full bootstrap/regtest?

// { dg-do compile }
// { dg-options "-std=c++2b" }

int qux ();

constexpr int
foo (int x)
{
  switch (x)
    {
      static int bar = qux ();
    case 12:
      return 1;
    }
  return 0;
}

constexpr int
bar (int x)
{
  switch (x)
    {
      thread_local int bar = qux ();
    case 12:
      return 1;
    }
  return 0;
}

constexpr int
baz (int x)
{
  switch (x)
    {
      static const int bar = qux ();	// { dg-message "'bar' was not initialized with a constant expression" }
    case 12:
      return bar;
    }
  return 0;
}

constexpr int
corge (int x)
{
  switch (x)
    {
      const thread_local int bar = qux ();	// { dg-message "'bar' was not initialized with a constant expression" }
    case 12:
      return bar;
    }
  return 0;
}

constexpr int a = foo (12);
constexpr int b = bar (12);
constexpr int c = baz (12);		// { dg-error "the value of 'bar' is not usable in a constant expression" }
constexpr int d = corge (12);		// { dg-error "the value of 'bar' is not usable in a constant expression" }


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

gcc/c-family/
	* c-cppbuiltin.c (c_cpp_builtins): For -std=c++23 predefine
	__cpp_constexpr to 202103L 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>: Allow declarations
	of static and thread_local vars for C++23.
	(potential_constant_expression_1) <case LABEL_EXPR>: Allow labels for
	C++23.
gcc/testsuite/
	* g++.dg/cpp23/feat-cxx2b.C: Expect __cpp_constexpr 202103L 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/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. 5, 2021, 9:24 p.m. UTC | #1
On 10/5/21 08:39, Jakub Jelinek wrote:
> Hi!
> 
> The following patch implements C++23 P2242R3 - Non-literal variables
> (and labels and gotos) in constexpr functions.
> I think it is mostly straightforward, don't diagnose certain
> statements/declarations just because of their presence in
> constexpr/consteval functions, but (except for the non-literal type
> var declarations which ought to be caught by e.g. constructor or
> destructor call during evaluation not being constexpr and for
> labels which are now always allowed) diagnose it during constexpr
> evaluation.
> 
> The only unclear thing to me is "a control flow that passes through a
> declaration".  The patch implements diagnostics when encountering a
> DECL_EXPR for such variables.  But because constexpr/consteval functions
> support switch statements, another thing that can happen is jump over
> such a declaration.  Consider e.g. following testcase (not included
> in the patch).  When evaluating foo (12) or bar (12) the patch
> accepts those as constant expressions, eventhough it jumps across such
> declarations.  For baz (12) and corge (12) it rejects them not
> because of jumping across such declarations, but because those
> static or thread_local variables aren't initialized with constant
> expressions and are accessed.  If baz/corge is modified not to have
> const keyword, they are rejected because the vars aren't aren't const.
> If they have const keyword and are initialized by constant expression,
> accesses to those vars are accepted.  Is that ok?

That seems correct to me.  If they have static initialization, they are 
initialized.

Some comments in the patch below.

> So far regtested with
> GXX_TESTSUITE_STDS=98,11,14,17,20,2b make -j32 -k check-g++
> ok for trunk if it passes full bootstrap/regtest?
> 
> // { dg-do compile }
> // { dg-options "-std=c++2b" }
> 
> int qux ();
> 
> constexpr int
> foo (int x)
> {
>    switch (x)
>      {
>        static int bar = qux ();
>      case 12:
>        return 1;
>      }
>    return 0;
> }
> 
> constexpr int
> bar (int x)
> {
>    switch (x)
>      {
>        thread_local int bar = qux ();
>      case 12:
>        return 1;
>      }
>    return 0;
> }
> 
> constexpr int
> baz (int x)
> {
>    switch (x)
>      {
>        static const int bar = qux ();	// { dg-message "'bar' was not initialized with a constant expression" }
>      case 12:
>        return bar;
>      }
>    return 0;
> }
> 
> constexpr int
> corge (int x)
> {
>    switch (x)
>      {
>        const thread_local int bar = qux ();	// { dg-message "'bar' was not initialized with a constant expression" }
>      case 12:
>        return bar;
>      }
>    return 0;
> }
> 
> constexpr int a = foo (12);
> constexpr int b = bar (12);
> constexpr int c = baz (12);		// { dg-error "the value of 'bar' is not usable in a constant expression" }
> constexpr int d = corge (12);		// { dg-error "the value of 'bar' is not usable in a constant expression" }
> 
> 
> 2021-10-05  Jakub Jelinek  <jakub@redhat.com>
> 
> gcc/c-family/
> 	* c-cppbuiltin.c (c_cpp_builtins): For -std=c++23 predefine
> 	__cpp_constexpr to 202103L 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>: Allow declarations
> 	of static and thread_local vars for C++23.
> 	(potential_constant_expression_1) <case LABEL_EXPR>: Allow labels for
> 	C++23.
> gcc/testsuite/
> 	* g++.dg/cpp23/feat-cxx2b.C: Expect __cpp_constexpr 202103L 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/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-09-21 23:31:01.016248936 +0200
> +++ gcc/c-family/c-cppbuiltin.c	2021-10-05 12:54:29.898321379 +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=202103L");
>   	}
>         if (flag_concepts)
>           {
> --- gcc/cp/parser.c.jj	2021-10-01 18:47:58.090795589 +0200
> +++ gcc/cp/parser.c	2021-10-05 12:54:29.907321254 +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-09-21 23:31:01.042248567 +0200
> +++ gcc/cp/decl.c	2021-10-05 12:54:29.909321226 +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-09-22 23:19:37.498912776 +0200
> +++ gcc/cp/constexpr.c	2021-10-05 13:56:02.377831320 +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);
> +			    "variable %qD of non-literal type %qT in "
> +			    "%<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,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.

> @@ -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.

> --- gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C.jj	2021-06-11 23:44:55.883676082 +0200
> +++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C	2021-10-05 12:54:29.910321212 +0200
> @@ -134,8 +134,8 @@
>   
>   #ifndef __cpp_constexpr
>   #  error "__cpp_constexpr"
> -#elif __cpp_constexpr != 201907
> -#  error "__cpp_constexpr != 201907"
> +#elif __cpp_constexpr != 202103
> +#  error "__cpp_constexpr != 202103"
>   #endif
>   
>   #ifndef __cpp_decltype_auto
> --- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit1.C.jj	2021-10-05 12:54:29.910321212 +0200
> +++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit1.C	2021-10-05 13:57:09.582896213 +0200
> @@ -0,0 +1,68 @@
> +// P2242R3
> +// { dg-do compile { target c++14 } }
> +
> +constexpr int
> +foo ()
> +{
> +lab:		// { dg-error "label definition is not a constant expression" "" { 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 >= 202103L
> +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 12:54:29.910321212 +0200
> +++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit2.C	2021-10-05 12:54:29.910321212 +0200
> @@ -0,0 +1,64 @@
> +// 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;
> +}
> +
> +constexpr int
> +garply (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 = garply (1);	// { dg-message "in 'constexpr' expansion of" }
> +constexpr int e = corge (1);	// { dg-message "in 'constexpr' expansion of" }
> --- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C.jj	2021-10-05 12:54:29.910321212 +0200
> +++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C	2021-10-05 13:57:21.176734894 +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/diagnostic/constexpr1.C.jj	2020-01-14 20:02:46.815609354 +0100
> +++ gcc/testsuite/g++.dg/diagnostic/constexpr1.C	2021-10-05 13:56:30.249443508 +0200
> @@ -1,5 +1,5 @@
>   // { 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 } }
>   
> -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 } }
> --- gcc/testsuite/g++.dg/cpp1y/constexpr-label.C.jj	2020-11-22 23:21:35.866434202 +0100
> +++ gcc/testsuite/g++.dg/cpp1y/constexpr-label.C	2021-10-05 13:37:37.660219970 +0200
> @@ -4,6 +4,6 @@
>   constexpr int
>   f ()
>   {
> -x: // { dg-error "label definition is not a constant expression" }
> +x: // { dg-error "label definition is not a constant expression" "" { target c++20_down } }
>     return 42;
>   }
> --- gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C.jj	2020-01-14 20:02:46.773609984 +0100
> +++ gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C	2021-10-05 13:38:23.322583787 +0200
> @@ -3,13 +3,13 @@
>   struct A { A(); };
>   
>   constexpr int f(int i) {
> -  static int j = i;		// { dg-error "static" }
> -  thread_local int l = i;	// { dg-error "thread_local" }
> +  static int j = i;		// { dg-error "static" "" { target c++20_down } }
> +  thread_local int l = i;	// { dg-error "thread_local" "" { target c++20_down } }
>     goto foo;			// { dg-error "goto" }
>    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-14 20:02:46.000000000 +0100
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C	2021-10-05 14:05:21.092057240 +0200
> @@ -5,14 +5,14 @@
>   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" }
> -  l:;
> +  static double b = 1.0;// { dg-error "'b' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } }
> +  goto l;		// { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } }
> +  l:;			// { dg-error "'goto' is not a constant expression" "" { target c++23 } .-1 }
>     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 +20,20 @@ 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" }
> -  l:;
> +  static long double b = 3.0;// { dg-error "'b' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } }
> +  goto l;		// { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } }
> +  l:;			// { dg-error "'goto' is not a constant expression" "" { target c++23 } .-1 }
>     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 19:19:14.049804260 +0200
> +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C	2021-10-05 13:40:48.282564167 +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 23:21:00.540567921 +0100
> +++ gcc/testsuite/g++.dg/cpp2a/consteval3.C	2021-10-05 13:42:17.371322962 +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,7 @@ 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 } }
> +  thread_local int b = 6;	// { dg-error "'b' declared 'thread_local' in 'consteval' function only available with" "" { target c++20_only } }
>     return x;
>   }
> 
> 	Jakub
>
diff mbox series

Patch

--- gcc/c-family/c-cppbuiltin.c.jj	2021-09-21 23:31:01.016248936 +0200
+++ gcc/c-family/c-cppbuiltin.c	2021-10-05 12:54:29.898321379 +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=202103L");
 	}
       if (flag_concepts)
         {
--- gcc/cp/parser.c.jj	2021-10-01 18:47:58.090795589 +0200
+++ gcc/cp/parser.c	2021-10-05 12:54:29.907321254 +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-09-21 23:31:01.042248567 +0200
+++ gcc/cp/decl.c	2021-10-05 12:54:29.909321226 +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-09-22 23:19:37.498912776 +0200
+++ gcc/cp/constexpr.c	2021-10-05 13:56:02.377831320 +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);
+			    "variable %qD of non-literal type %qT in "
+			    "%<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,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 "
@@ -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");
--- gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C.jj	2021-06-11 23:44:55.883676082 +0200
+++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C	2021-10-05 12:54:29.910321212 +0200
@@ -134,8 +134,8 @@ 
 
 #ifndef __cpp_constexpr
 #  error "__cpp_constexpr"
-#elif __cpp_constexpr != 201907
-#  error "__cpp_constexpr != 201907"
+#elif __cpp_constexpr != 202103
+#  error "__cpp_constexpr != 202103"
 #endif
 
 #ifndef __cpp_decltype_auto
--- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit1.C.jj	2021-10-05 12:54:29.910321212 +0200
+++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit1.C	2021-10-05 13:57:09.582896213 +0200
@@ -0,0 +1,68 @@ 
+// P2242R3
+// { dg-do compile { target c++14 } }
+
+constexpr int
+foo ()
+{
+lab:		// { dg-error "label definition is not a constant expression" "" { 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 >= 202103L
+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 12:54:29.910321212 +0200
+++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit2.C	2021-10-05 12:54:29.910321212 +0200
@@ -0,0 +1,64 @@ 
+// 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;
+}
+
+constexpr int
+garply (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 = garply (1);	// { dg-message "in 'constexpr' expansion of" }
+constexpr int e = corge (1);	// { dg-message "in 'constexpr' expansion of" }
--- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C.jj	2021-10-05 12:54:29.910321212 +0200
+++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C	2021-10-05 13:57:21.176734894 +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/diagnostic/constexpr1.C.jj	2020-01-14 20:02:46.815609354 +0100
+++ gcc/testsuite/g++.dg/diagnostic/constexpr1.C	2021-10-05 13:56:30.249443508 +0200
@@ -1,5 +1,5 @@ 
 // { 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 } }
 
-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 } }
--- gcc/testsuite/g++.dg/cpp1y/constexpr-label.C.jj	2020-11-22 23:21:35.866434202 +0100
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-label.C	2021-10-05 13:37:37.660219970 +0200
@@ -4,6 +4,6 @@ 
 constexpr int
 f ()
 {
-x: // { dg-error "label definition is not a constant expression" }
+x: // { dg-error "label definition is not a constant expression" "" { target c++20_down } }
   return 42;
 }
--- gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C.jj	2020-01-14 20:02:46.773609984 +0100
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C	2021-10-05 13:38:23.322583787 +0200
@@ -3,13 +3,13 @@ 
 struct A { A(); };
 
 constexpr int f(int i) {
-  static int j = i;		// { dg-error "static" }
-  thread_local int l = i;	// { dg-error "thread_local" }
+  static int j = i;		// { dg-error "static" "" { target c++20_down } }
+  thread_local int l = i;	// { dg-error "thread_local" "" { target c++20_down } }
   goto foo;			// { dg-error "goto" }
  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-14 20:02:46.000000000 +0100
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C	2021-10-05 14:05:21.092057240 +0200
@@ -5,14 +5,14 @@ 
 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" }
-  l:;
+  static double b = 1.0;// { dg-error "'b' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } }
+  goto l;		// { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } }
+  l:;			// { dg-error "'goto' is not a constant expression" "" { target c++23 } .-1 }
   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 +20,20 @@  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" }
-  l:;
+  static long double b = 3.0;// { dg-error "'b' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } }
+  goto l;		// { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } }
+  l:;			// { dg-error "'goto' is not a constant expression" "" { target c++23 } .-1 }
   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 19:19:14.049804260 +0200
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C	2021-10-05 13:40:48.282564167 +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 23:21:00.540567921 +0100
+++ gcc/testsuite/g++.dg/cpp2a/consteval3.C	2021-10-05 13:42:17.371322962 +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,7 @@  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 } }
+  thread_local int b = 6;	// { dg-error "'b' declared 'thread_local' in 'consteval' function only available with" "" { target c++20_only } }
   return x;
 }