c++: Implement C++26 P2573R2 - = delete("should have a reason"); [PR114458]
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. 2024-05-02 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. * cp-tree.h (DECL_DELETED_FN): Document representation of = delete("reason") on a DECL. 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.
This commit is contained in:
parent
c59708fba3
commit
2f15787f2e
9 changed files with 124 additions and 9 deletions
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -4477,7 +4477,10 @@ get_vec_init_expr (tree t)
|
|||
&& DECL_DECLARED_CONSTEXPR_P (NODE) \
|
||||
&& DECL_CLASS_SCOPE_P (NODE)))
|
||||
|
||||
/* Nonzero if DECL was declared with '= delete'. */
|
||||
/* Nonzero if DECL was declared with '= delete'.
|
||||
= delete("reason") is represented in addition to this flag by DECL_INITIAL
|
||||
being STRING_CST with the reason and TREE_TYPE of the STRING_CST the
|
||||
RID_DELETE IDENTIFIER_NODE. */
|
||||
#define DECL_DELETED_FN(DECL) \
|
||||
(LANG_DECL_FN_CHECK (DECL)->min.base.threadprivate_or_deleted_p)
|
||||
|
||||
|
|
|
@ -2420,6 +2420,10 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
|
|||
"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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8597,17 +8601,20 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#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;
|
||||
|
@ -1038,7 +1039,10 @@ grokfield (const cp_declarator *declarator,
|
|||
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;
|
||||
|
@ -1123,10 +1127,14 @@ grokfield (const cp_declarator *declarator,
|
|||
{
|
||||
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])
|
||||
{
|
||||
|
@ -5912,7 +5920,16 @@ mark_used (tree decl, tsubst_flags_t complain /* = tf_warning_or_error */)
|
|||
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");
|
||||
}
|
||||
|
|
|
@ -28634,6 +28634,27 @@ cp_parser_pure_specifier (cp_parser* parser)
|
|||
|| 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;
|
||||
}
|
||||
|
||||
|
|
41
gcc/testsuite/g++.dg/cpp26/delete-reason1.C
Normal file
41
gcc/testsuite/g++.dg/cpp26/delete-reason1.C
Normal file
|
@ -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" }
|
||||
}
|
20
gcc/testsuite/g++.dg/cpp26/delete-reason2.C
Normal file
20
gcc/testsuite/g++.dg/cpp26/delete-reason2.C
Normal file
|
@ -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 }
|
|
@ -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
|
||||
|
|
|
@ -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" }
|
||||
|
|
Loading…
Add table
Reference in a new issue