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