c++: Implement C++26 P2573R2 - = delete("should have a reason"); [PR114458]

Message ID ZjHiUGFqJhpu24Vz@tucnak
State New
Headers
Series c++: Implement C++26 P2573R2 - = delete("should have a reason"); [PR114458] |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_gcc_check--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 success Testing passed

Commit Message

Jakub Jelinek May 1, 2024, 6:33 a.m. UTC
  Hi!

The following patch implements the C++26 P2573R2
= delete("should have a reason"); paper.
I've tried to avoid increasing compile time memory for it when it isn't
used (e.g. by adding a new lang_decl tree member), so the reason is
represented as STRING_CST in DECL_INITIAL (which normally is for
DECL_DELETED_FN error_mark_node) and to differentiate this delete("reason")
initializer from some bogus attempt to initialize a function with "reason"
using the RID_DELETE identifier as TREE_TYPE of the STRING_CST, as nothing
needs to care about the type of the reason.  If preferred it could
be say TREE_LIST with the reason STRING_CST and RID_DELETE identifier or
something similar instead, but that would need more compile time memory when
it is used.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2024-05-01  Jakub Jelinek  <jakub@redhat.com>

	PR c++/114458
gcc/c-family/
	* c-cppbuiltin.cc (c_cpp_builtins): Predefine
	__cpp_deleted_function=202403L for C++26.
gcc/cp/ChangeLog
	* parser.cc (cp_parser_pure_specifier): Implement C++26 P2573R2
	- = delete("should have a reason");.  Parse deleted-function-body.
	* decl.cc (duplicate_decls): Copy DECL_INITIAL from DECL_DELETED_FN
	olddecl to newdecl if it is a STRING_CST.
	(cp_finish_decl): Handle deleted init with a reason.
	* decl2.cc: Include "escaped_string.h".
	(grokfield): Handle deleted init with a reason.
	(mark_used): Emit DECL_DELETED_FN reason in the message if any.
gcc/testsuite/
	* g++.dg/cpp26/feat-cxx26.C (__cpp_deleted_function): Add test.
	* g++.dg/cpp26/delete-reason1.C: New test.
	* g++.dg/cpp26/delete-reason2.C: New test.
	* g++.dg/parse/error65.C (f1): Adjust expected diagnostics.


	Jakub
  

Comments

Jason Merrill May 1, 2024, 6:58 p.m. UTC | #1
On 5/1/24 23:33, Jakub Jelinek wrote:
> 
> The following patch implements the C++26 P2573R2
> = delete("should have a reason"); paper.
> I've tried to avoid increasing compile time memory for it when it isn't
> used (e.g. by adding a new lang_decl tree member), so the reason is
> represented as STRING_CST in DECL_INITIAL (which normally is for
> DECL_DELETED_FN error_mark_node) and to differentiate this delete("reason")
> initializer from some bogus attempt to initialize a function with "reason"
> using the RID_DELETE identifier as TREE_TYPE of the STRING_CST, as nothing
> needs to care about the type of the reason.  If preferred it could
> be say TREE_LIST with the reason STRING_CST and RID_DELETE identifier or
> something similar instead, but that would need more compile time memory when
> it is used.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

Please add a comment about this representation to the definition of 
DECL_DELETED_FN.  OK with that change.

> 2024-05-01  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR c++/114458
> gcc/c-family/
> 	* c-cppbuiltin.cc (c_cpp_builtins): Predefine
> 	__cpp_deleted_function=202403L for C++26.
> gcc/cp/ChangeLog
> 	* parser.cc (cp_parser_pure_specifier): Implement C++26 P2573R2
> 	- = delete("should have a reason");.  Parse deleted-function-body.
> 	* decl.cc (duplicate_decls): Copy DECL_INITIAL from DECL_DELETED_FN
> 	olddecl to newdecl if it is a STRING_CST.
> 	(cp_finish_decl): Handle deleted init with a reason.
> 	* decl2.cc: Include "escaped_string.h".
> 	(grokfield): Handle deleted init with a reason.
> 	(mark_used): Emit DECL_DELETED_FN reason in the message if any.
> gcc/testsuite/
> 	* g++.dg/cpp26/feat-cxx26.C (__cpp_deleted_function): Add test.
> 	* g++.dg/cpp26/delete-reason1.C: New test.
> 	* g++.dg/cpp26/delete-reason2.C: New test.
> 	* g++.dg/parse/error65.C (f1): Adjust expected diagnostics.
> 
> --- gcc/c-family/c-cppbuiltin.cc.jj	2024-04-30 08:57:07.359039013 +0200
> +++ gcc/c-family/c-cppbuiltin.cc	2024-04-30 19:16:45.069542205 +0200
> @@ -1092,6 +1092,7 @@ c_cpp_builtins (cpp_reader *pfile)
>   	  cpp_define (pfile, "__cpp_static_assert=202306L");
>   	  cpp_define (pfile, "__cpp_placeholder_variables=202306L");
>   	  cpp_define (pfile, "__cpp_structured_bindings=202403L");
> +	  cpp_define (pfile, "__cpp_deleted_function=202403L");
>   	}
>         if (flag_concepts)
>           {
> --- gcc/cp/parser.cc.jj	2024-04-30 08:57:07.349039147 +0200
> +++ gcc/cp/parser.cc	2024-04-30 16:47:01.427952875 +0200
> @@ -28573,6 +28573,27 @@ cp_parser_pure_specifier (cp_parser* par
>         || token->keyword == RID_DELETE)
>       {
>         maybe_warn_cpp0x (CPP0X_DEFAULTED_DELETED);
> +      if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
> +	{
> +	  if (cxx_dialect >= cxx11 && cxx_dialect < cxx26)
> +	    pedwarn (cp_lexer_peek_token (parser->lexer)->location,
> +		     OPT_Wc__26_extensions,
> +		     "%<delete%> reason only available with "
> +		     "%<-std=c++2c%> or %<-std=gnu++2c%>");
> +
> +	  /* Consume the `('.  */
> +	  matching_parens parens;
> +	  parens.consume_open (parser);
> +	  tree reason = cp_parser_unevaluated_string_literal (parser);
> +	  /* Consume the `)'.  */
> +	  parens.require_close (parser);
> +	  if (TREE_CODE (reason) == STRING_CST)
> +	    {
> +	      TREE_TYPE (reason) = token->u.value;
> +	      return reason;
> +	    }
> +	}
> +
>         return token->u.value;
>       }
>   
> --- gcc/cp/decl.cc.jj	2024-04-30 08:55:26.172389593 +0200
> +++ gcc/cp/decl.cc	2024-04-30 19:01:32.316543498 +0200
> @@ -2410,6 +2410,10 @@ duplicate_decls (tree newdecl, tree oldd
>   			"previous declaration of %qD", olddecl);
>   	    }
>   	  DECL_DELETED_FN (newdecl) |= DECL_DELETED_FN (olddecl);
> +	  if (DECL_DELETED_FN (olddecl)
> +	      && DECL_INITIAL (olddecl)
> +	      && TREE_CODE (DECL_INITIAL (olddecl)) == STRING_CST)
> +	    DECL_INITIAL (newdecl) = DECL_INITIAL (olddecl);
>   	}
>       }
>   
> @@ -8587,17 +8591,20 @@ cp_finish_decl (tree decl, tree init, bo
>     if (init && TREE_CODE (decl) == FUNCTION_DECL)
>       {
>         tree clone;
> -      if (init == ridpointers[(int)RID_DELETE])
> +      if (init == ridpointers[(int)RID_DELETE]
> +	  || (TREE_CODE (init) == STRING_CST
> +	      && TREE_TYPE (init) == ridpointers[(int)RID_DELETE]))
>   	{
>   	  /* FIXME check this is 1st decl.  */
>   	  DECL_DELETED_FN (decl) = 1;
>   	  DECL_DECLARED_INLINE_P (decl) = 1;
> -	  DECL_INITIAL (decl) = error_mark_node;
> +	  DECL_INITIAL (decl)
> +	    = TREE_CODE (init) == STRING_CST ? init : error_mark_node;
>   	  FOR_EACH_CLONE (clone, decl)
>   	    {
>   	      DECL_DELETED_FN (clone) = 1;
>   	      DECL_DECLARED_INLINE_P (clone) = 1;
> -	      DECL_INITIAL (clone) = error_mark_node;
> +	      DECL_INITIAL (clone) = DECL_INITIAL (decl);
>   	    }
>   	  init = NULL_TREE;
>   	}
> --- gcc/cp/decl2.cc.jj	2024-04-25 20:33:30.770858926 +0200
> +++ gcc/cp/decl2.cc	2024-04-30 18:49:37.555956481 +0200
> @@ -50,6 +50,7 @@ along with GCC; see the file COPYING3.
>   #include "asan.h"
>   #include "optabs-query.h"
>   #include "omp-general.h"
> +#include "escaped_string.h"
>   
>   /* Id for dumping the raw trees.  */
>   int raw_dump_id;
> @@ -1042,7 +1043,10 @@ grokfield (const cp_declarator *declarat
>       init = NULL_TREE;
>   
>     int initialized;
> -  if (init == ridpointers[(int)RID_DELETE])
> +  if (init == ridpointers[(int)RID_DELETE]
> +      || (init
> +	  && TREE_CODE (init) == STRING_CST
> +	  && TREE_TYPE (init) == ridpointers[(int)RID_DELETE]))
>       initialized = SD_DELETED;
>     else if (init == ridpointers[(int)RID_DEFAULT])
>       initialized = SD_DEFAULTED;
> @@ -1127,10 +1131,14 @@ grokfield (const cp_declarator *declarat
>       {
>         if (TREE_CODE (value) == FUNCTION_DECL)
>   	{
> -	  if (init == ridpointers[(int)RID_DELETE])
> +	  if (init == ridpointers[(int)RID_DELETE]
> +	      || (TREE_CODE (init) == STRING_CST
> +		  && TREE_TYPE (init) == ridpointers[(int)RID_DELETE]))
>   	    {
>   	      DECL_DELETED_FN (value) = 1;
>   	      DECL_DECLARED_INLINE_P (value) = 1;
> +	      if (TREE_CODE (init) == STRING_CST)
> +		DECL_INITIAL (value) = init;
>   	    }
>   	  else if (init == ridpointers[(int)RID_DEFAULT])
>   	    {
> @@ -5884,7 +5892,16 @@ mark_used (tree decl, tsubst_flags_t com
>   	    sorry ("converting lambda that uses %<...%> to function pointer");
>   	  else if (complain & tf_error)
>   	    {
> -	      error ("use of deleted function %qD", decl);
> +	      if (DECL_INITIAL (decl)
> +		  && TREE_CODE (DECL_INITIAL (decl)) == STRING_CST)
> +		{
> +		  escaped_string msg;
> +		  msg.escape (TREE_STRING_POINTER (DECL_INITIAL (decl)));
> +		  error ("use of deleted function %qD: %s",
> +			 decl, (const char *) msg);
> +		}
> +	      else
> +		error ("use of deleted function %qD", decl);
>   	      if (!maybe_explain_implicit_delete (decl))
>   		inform (DECL_SOURCE_LOCATION (decl), "declared here");
>   	    }
> --- gcc/testsuite/g++.dg/cpp26/feat-cxx26.C.jj	2024-04-30 08:57:07.401038450 +0200
> +++ gcc/testsuite/g++.dg/cpp26/feat-cxx26.C	2024-04-30 19:17:46.105740201 +0200
> @@ -609,3 +609,9 @@
>   #elif __cpp_placeholder_variables != 202306
>   #  error "__cpp_placeholder_variables != 202306"
>   #endif
> +
> +#ifndef __cpp_deleted_function
> +#  error "__cpp_deleted_function"
> +#elif __cpp_deleted_function != 202403
> +#  error "__cpp_deleted_function != 202403"
> +#endif
> --- gcc/testsuite/g++.dg/cpp26/delete-reason1.C.jj	2024-04-30 19:18:05.223488994 +0200
> +++ gcc/testsuite/g++.dg/cpp26/delete-reason1.C	2024-04-30 19:33:30.813363904 +0200
> @@ -0,0 +1,41 @@
> +// P2573R2 = delete("should have a reason");
> +// { dg-do compile { target c++11 } }
> +// { dg-options "" }
> +
> +void foo () = delete ("reason");		// { dg-warning "'delete' reason only available with" "" { target c++23_down } }
> +						// { dg-message "declared here" "" { target *-*-* } .-1 }
> +struct S {
> +  void bar () = delete ("another reason");	// { dg-warning "'delete' reason only available with" "" { target c++23_down } }
> +};						// { dg-message "declared here" "" { target *-*-* } .-1 }
> +int baz (int) = delete ("yet another reason");	// { dg-warning "'delete' reason only available with" "" { target c++23_down } }
> +int baz (int);					// { dg-message "declared here" }
> +template <typename T>
> +void qux (T) = delete ("some other reason");	// { dg-warning "'delete' reason only available with" "" { target c++23_down } }
> +						// { dg-message "declared here" "" { target *-*-* } .-1 }
> +template <typename T>
> +struct U {
> +  U () = delete ("my reasons");			// { dg-warning "'delete' reason only available with" "" { target c++23_down } }
> +  U (int);					// { dg-message "declared here" "" { target *-*-* } .-1 }
> +  ~U () = delete ("your reasons");		// { dg-warning "'delete' reason only available with" "" { target c++23_down } }
> +};						// { dg-message "declared here" "" { target *-*-* } .-1 }
> +template <>
> +void qux (long long) = delete;			// { dg-message "declared here" }
> +template <typename T>
> +void corge (T) = delete;			// { dg-message "declared here" }
> +template <>
> +void corge (double) = delete ("their reasons");	// { dg-warning "'delete' reason only available with" "" { target c++23_down } }
> +						// { dg-message "declared here" "" { target *-*-* } .-1 }
> +
> +void
> +test (U<int> &x)
> +{
> +  foo ();					// { dg-error "use of deleted function 'void foo\\\(\\\)': reason" }
> +  S{}.bar ();					// { dg-error "use of deleted function 'void S::bar\\\(\\\)': another reason" }
> +  baz (0);					// { dg-error "use of deleted function 'int baz\\\(int\\\)': yet another reason" }
> +  qux (0L);					// { dg-error "use of deleted function 'void qux\\\(T\\\) \\\[with T = long int\\\]': some other reason" }
> +  qux (0LL);					// { dg-error "use of deleted function 'void qux\\\(T\\\) \\\[with T = long long int\\\]'" }
> +  U<long> u;					// { dg-error "use of deleted function 'U<T>::U\\\(\\\) \\\[with T = long int\\\]': my reasons" }
> +						// { dg-error "use of deleted function 'U<T>::~U\\\(\\\) \\\[with T = long int\\\]': your reasons" "" { target *-*-* } .-1 }
> +  corge (0);					// { dg-error "use of deleted function 'void corge\\\(T\\\) \\\[with T = int\\\]'" }
> +  corge (0.0);					// { dg-error "use of deleted function 'void corge\\\(T\\\) \\\[with T = double\\\]': their reasons" }
> +}
> --- gcc/testsuite/g++.dg/cpp26/delete-reason2.C.jj	2024-04-30 19:34:02.853944821 +0200
> +++ gcc/testsuite/g++.dg/cpp26/delete-reason2.C	2024-04-30 19:40:31.070867037 +0200
> @@ -0,0 +1,20 @@
> +// P2573R2 = delete("should have a reason");
> +// { dg-do compile { target c++11 } }
> +// { dg-options "" }
> +
> +void foo () = delete (;				// { dg-warning "'delete' reason only available with" "" { target c++23_down } }
> +						// { dg-error "expected string-literal before ';' token" "" { target *-*-* } .-1 }
> +						// { dg-error "expected '\\\)' before ';' token" "" { target *-*-* } .-2 }
> +void bar () = delete ();			// { dg-warning "'delete' reason only available with" "" { target c++23_down } }
> +						// { dg-error "expected string-literal before '\\\)' token" "" { target *-*-* } .-1 }
> +void baz () = delete (0);			// { dg-warning "'delete' reason only available with" "" { target c++23_down } }
> +						// { dg-error "expected string-literal before numeric constant" "" { target *-*-* } .-1 }
> +						// { dg-error "expected '\\\)' before numeric constant" "" { target *-*-* } .-2 }
> +						// { dg-error "expected ',' or ';' before numeric constant" "" { target *-*-* } .-3 }
> +void qux () = delete (L"");			// { dg-warning "'delete' reason only available with" "" { target c++23_down } }
> +						// { dg-error "a wide string is invalid in this context before '\\\)' token" "" { target *-*-* } .-1 }
> +void corge () = delete (u8"");			// { dg-warning "'delete' reason only available with" "" { target c++23_down } }
> +						// { dg-error "a wide string is invalid in this context before '\\\)' token" "" { target *-*-* } .-1 }
> +void garply () = delete ("something" + 0);	// { dg-warning "'delete' reason only available with" "" { target c++23_down } }
> +						// { dg-error "expected '\\\)' before '\\\+' token" "" { target *-*-* } .-1 }
> +						// { dg-error "expected ',' or ';' before '\\\+' token" "" { target *-*-* } .-2 }
> --- gcc/testsuite/g++.dg/parse/error65.C.jj	2023-10-18 12:37:14.462340550 +0200
> +++ gcc/testsuite/g++.dg/parse/error65.C	2024-05-01 08:25:10.215013182 +0200
> @@ -1,8 +1,7 @@
>   // PR c++/111840
>   // { dg-do compile { target c++11 } }
>   
> -// NB: =delete("reason") may be allowed via P2573.
> -int f1() = delete("should have a reason"); // { dg-error "expected" }
> +int f1() = delete("should have a reason"); // { dg-error "'delete' reason only available with" "" { target c++23_down } }
>   int f2() = delete[""]; // { dg-error "expected" }
>   int f3() = delete{""}; // { dg-error "expected" }
>   int f4() = delete""; // { dg-error "expected" }
> 
> 	Jakub
>
  

Patch

--- gcc/c-family/c-cppbuiltin.cc.jj	2024-04-30 08:57:07.359039013 +0200
+++ gcc/c-family/c-cppbuiltin.cc	2024-04-30 19:16:45.069542205 +0200
@@ -1092,6 +1092,7 @@  c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_static_assert=202306L");
 	  cpp_define (pfile, "__cpp_placeholder_variables=202306L");
 	  cpp_define (pfile, "__cpp_structured_bindings=202403L");
+	  cpp_define (pfile, "__cpp_deleted_function=202403L");
 	}
       if (flag_concepts)
         {
--- gcc/cp/parser.cc.jj	2024-04-30 08:57:07.349039147 +0200
+++ gcc/cp/parser.cc	2024-04-30 16:47:01.427952875 +0200
@@ -28573,6 +28573,27 @@  cp_parser_pure_specifier (cp_parser* par
       || token->keyword == RID_DELETE)
     {
       maybe_warn_cpp0x (CPP0X_DEFAULTED_DELETED);
+      if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
+	{
+	  if (cxx_dialect >= cxx11 && cxx_dialect < cxx26)
+	    pedwarn (cp_lexer_peek_token (parser->lexer)->location,
+		     OPT_Wc__26_extensions,
+		     "%<delete%> reason only available with "
+		     "%<-std=c++2c%> or %<-std=gnu++2c%>");
+
+	  /* Consume the `('.  */
+	  matching_parens parens;
+	  parens.consume_open (parser);
+	  tree reason = cp_parser_unevaluated_string_literal (parser);
+	  /* Consume the `)'.  */
+	  parens.require_close (parser);
+	  if (TREE_CODE (reason) == STRING_CST)
+	    {
+	      TREE_TYPE (reason) = token->u.value;
+	      return reason;
+	    }
+	}
+
       return token->u.value;
     }
 
--- gcc/cp/decl.cc.jj	2024-04-30 08:55:26.172389593 +0200
+++ gcc/cp/decl.cc	2024-04-30 19:01:32.316543498 +0200
@@ -2410,6 +2410,10 @@  duplicate_decls (tree newdecl, tree oldd
 			"previous declaration of %qD", olddecl);
 	    }
 	  DECL_DELETED_FN (newdecl) |= DECL_DELETED_FN (olddecl);
+	  if (DECL_DELETED_FN (olddecl)
+	      && DECL_INITIAL (olddecl)
+	      && TREE_CODE (DECL_INITIAL (olddecl)) == STRING_CST)
+	    DECL_INITIAL (newdecl) = DECL_INITIAL (olddecl);
 	}
     }
 
@@ -8587,17 +8591,20 @@  cp_finish_decl (tree decl, tree init, bo
   if (init && TREE_CODE (decl) == FUNCTION_DECL)
     {
       tree clone;
-      if (init == ridpointers[(int)RID_DELETE])
+      if (init == ridpointers[(int)RID_DELETE]
+	  || (TREE_CODE (init) == STRING_CST
+	      && TREE_TYPE (init) == ridpointers[(int)RID_DELETE]))
 	{
 	  /* FIXME check this is 1st decl.  */
 	  DECL_DELETED_FN (decl) = 1;
 	  DECL_DECLARED_INLINE_P (decl) = 1;
-	  DECL_INITIAL (decl) = error_mark_node;
+	  DECL_INITIAL (decl)
+	    = TREE_CODE (init) == STRING_CST ? init : error_mark_node;
 	  FOR_EACH_CLONE (clone, decl)
 	    {
 	      DECL_DELETED_FN (clone) = 1;
 	      DECL_DECLARED_INLINE_P (clone) = 1;
-	      DECL_INITIAL (clone) = error_mark_node;
+	      DECL_INITIAL (clone) = DECL_INITIAL (decl);
 	    }
 	  init = NULL_TREE;
 	}
--- gcc/cp/decl2.cc.jj	2024-04-25 20:33:30.770858926 +0200
+++ gcc/cp/decl2.cc	2024-04-30 18:49:37.555956481 +0200
@@ -50,6 +50,7 @@  along with GCC; see the file COPYING3.
 #include "asan.h"
 #include "optabs-query.h"
 #include "omp-general.h"
+#include "escaped_string.h"
 
 /* Id for dumping the raw trees.  */
 int raw_dump_id;
@@ -1042,7 +1043,10 @@  grokfield (const cp_declarator *declarat
     init = NULL_TREE;
 
   int initialized;
-  if (init == ridpointers[(int)RID_DELETE])
+  if (init == ridpointers[(int)RID_DELETE]
+      || (init
+	  && TREE_CODE (init) == STRING_CST
+	  && TREE_TYPE (init) == ridpointers[(int)RID_DELETE]))
     initialized = SD_DELETED;
   else if (init == ridpointers[(int)RID_DEFAULT])
     initialized = SD_DEFAULTED;
@@ -1127,10 +1131,14 @@  grokfield (const cp_declarator *declarat
     {
       if (TREE_CODE (value) == FUNCTION_DECL)
 	{
-	  if (init == ridpointers[(int)RID_DELETE])
+	  if (init == ridpointers[(int)RID_DELETE]
+	      || (TREE_CODE (init) == STRING_CST
+		  && TREE_TYPE (init) == ridpointers[(int)RID_DELETE]))
 	    {
 	      DECL_DELETED_FN (value) = 1;
 	      DECL_DECLARED_INLINE_P (value) = 1;
+	      if (TREE_CODE (init) == STRING_CST)
+		DECL_INITIAL (value) = init;
 	    }
 	  else if (init == ridpointers[(int)RID_DEFAULT])
 	    {
@@ -5884,7 +5892,16 @@  mark_used (tree decl, tsubst_flags_t com
 	    sorry ("converting lambda that uses %<...%> to function pointer");
 	  else if (complain & tf_error)
 	    {
-	      error ("use of deleted function %qD", decl);
+	      if (DECL_INITIAL (decl)
+		  && TREE_CODE (DECL_INITIAL (decl)) == STRING_CST)
+		{
+		  escaped_string msg;
+		  msg.escape (TREE_STRING_POINTER (DECL_INITIAL (decl)));
+		  error ("use of deleted function %qD: %s",
+			 decl, (const char *) msg);
+		}
+	      else
+		error ("use of deleted function %qD", decl);
 	      if (!maybe_explain_implicit_delete (decl))
 		inform (DECL_SOURCE_LOCATION (decl), "declared here");
 	    }
--- gcc/testsuite/g++.dg/cpp26/feat-cxx26.C.jj	2024-04-30 08:57:07.401038450 +0200
+++ gcc/testsuite/g++.dg/cpp26/feat-cxx26.C	2024-04-30 19:17:46.105740201 +0200
@@ -609,3 +609,9 @@ 
 #elif __cpp_placeholder_variables != 202306
 #  error "__cpp_placeholder_variables != 202306"
 #endif
+
+#ifndef __cpp_deleted_function
+#  error "__cpp_deleted_function"
+#elif __cpp_deleted_function != 202403
+#  error "__cpp_deleted_function != 202403"
+#endif
--- gcc/testsuite/g++.dg/cpp26/delete-reason1.C.jj	2024-04-30 19:18:05.223488994 +0200
+++ gcc/testsuite/g++.dg/cpp26/delete-reason1.C	2024-04-30 19:33:30.813363904 +0200
@@ -0,0 +1,41 @@ 
+// P2573R2 = delete("should have a reason");
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+void foo () = delete ("reason");		// { dg-warning "'delete' reason only available with" "" { target c++23_down } }
+						// { dg-message "declared here" "" { target *-*-* } .-1 }
+struct S {
+  void bar () = delete ("another reason");	// { dg-warning "'delete' reason only available with" "" { target c++23_down } }
+};						// { dg-message "declared here" "" { target *-*-* } .-1 }
+int baz (int) = delete ("yet another reason");	// { dg-warning "'delete' reason only available with" "" { target c++23_down } }
+int baz (int);					// { dg-message "declared here" }
+template <typename T>
+void qux (T) = delete ("some other reason");	// { dg-warning "'delete' reason only available with" "" { target c++23_down } }
+						// { dg-message "declared here" "" { target *-*-* } .-1 }
+template <typename T>
+struct U {
+  U () = delete ("my reasons");			// { dg-warning "'delete' reason only available with" "" { target c++23_down } }
+  U (int);					// { dg-message "declared here" "" { target *-*-* } .-1 }
+  ~U () = delete ("your reasons");		// { dg-warning "'delete' reason only available with" "" { target c++23_down } }
+};						// { dg-message "declared here" "" { target *-*-* } .-1 }
+template <>
+void qux (long long) = delete;			// { dg-message "declared here" }
+template <typename T>
+void corge (T) = delete;			// { dg-message "declared here" }
+template <>
+void corge (double) = delete ("their reasons");	// { dg-warning "'delete' reason only available with" "" { target c++23_down } }
+						// { dg-message "declared here" "" { target *-*-* } .-1 }
+
+void
+test (U<int> &x)
+{
+  foo ();					// { dg-error "use of deleted function 'void foo\\\(\\\)': reason" }
+  S{}.bar ();					// { dg-error "use of deleted function 'void S::bar\\\(\\\)': another reason" }
+  baz (0);					// { dg-error "use of deleted function 'int baz\\\(int\\\)': yet another reason" }
+  qux (0L);					// { dg-error "use of deleted function 'void qux\\\(T\\\) \\\[with T = long int\\\]': some other reason" }
+  qux (0LL);					// { dg-error "use of deleted function 'void qux\\\(T\\\) \\\[with T = long long int\\\]'" }
+  U<long> u;					// { dg-error "use of deleted function 'U<T>::U\\\(\\\) \\\[with T = long int\\\]': my reasons" }
+						// { dg-error "use of deleted function 'U<T>::~U\\\(\\\) \\\[with T = long int\\\]': your reasons" "" { target *-*-* } .-1 }
+  corge (0);					// { dg-error "use of deleted function 'void corge\\\(T\\\) \\\[with T = int\\\]'" }
+  corge (0.0);					// { dg-error "use of deleted function 'void corge\\\(T\\\) \\\[with T = double\\\]': their reasons" }
+}
--- gcc/testsuite/g++.dg/cpp26/delete-reason2.C.jj	2024-04-30 19:34:02.853944821 +0200
+++ gcc/testsuite/g++.dg/cpp26/delete-reason2.C	2024-04-30 19:40:31.070867037 +0200
@@ -0,0 +1,20 @@ 
+// P2573R2 = delete("should have a reason");
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+void foo () = delete (;				// { dg-warning "'delete' reason only available with" "" { target c++23_down } }
+						// { dg-error "expected string-literal before ';' token" "" { target *-*-* } .-1 }
+						// { dg-error "expected '\\\)' before ';' token" "" { target *-*-* } .-2 }
+void bar () = delete ();			// { dg-warning "'delete' reason only available with" "" { target c++23_down } }
+						// { dg-error "expected string-literal before '\\\)' token" "" { target *-*-* } .-1 }
+void baz () = delete (0);			// { dg-warning "'delete' reason only available with" "" { target c++23_down } }
+						// { dg-error "expected string-literal before numeric constant" "" { target *-*-* } .-1 }
+						// { dg-error "expected '\\\)' before numeric constant" "" { target *-*-* } .-2 }
+						// { dg-error "expected ',' or ';' before numeric constant" "" { target *-*-* } .-3 }
+void qux () = delete (L"");			// { dg-warning "'delete' reason only available with" "" { target c++23_down } }
+						// { dg-error "a wide string is invalid in this context before '\\\)' token" "" { target *-*-* } .-1 }
+void corge () = delete (u8"");			// { dg-warning "'delete' reason only available with" "" { target c++23_down } }
+						// { dg-error "a wide string is invalid in this context before '\\\)' token" "" { target *-*-* } .-1 }
+void garply () = delete ("something" + 0);	// { dg-warning "'delete' reason only available with" "" { target c++23_down } }
+						// { dg-error "expected '\\\)' before '\\\+' token" "" { target *-*-* } .-1 }
+						// { dg-error "expected ',' or ';' before '\\\+' token" "" { target *-*-* } .-2 }
--- gcc/testsuite/g++.dg/parse/error65.C.jj	2023-10-18 12:37:14.462340550 +0200
+++ gcc/testsuite/g++.dg/parse/error65.C	2024-05-01 08:25:10.215013182 +0200
@@ -1,8 +1,7 @@ 
 // PR c++/111840
 // { dg-do compile { target c++11 } }
 
-// NB: =delete("reason") may be allowed via P2573.
-int f1() = delete("should have a reason"); // { dg-error "expected" }
+int f1() = delete("should have a reason"); // { dg-error "'delete' reason only available with" "" { target c++23_down } }
 int f2() = delete[""]; // { dg-error "expected" }
 int f3() = delete{""}; // { dg-error "expected" }
 int f4() = delete""; // { dg-error "expected" }