mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-04-27 02:50:27 +08:00
Implement P0722R3, destroying operator delete.
A destroying operator delete takes responsibility for calling the destructor for the object it is deleting; this is intended to be useful for sized delete of a class allocated with a trailing buffer, where the compiler can't know the size of the allocation, and so would pass the wrong size to the non-destroying sized operator delete. gcc/c-family/ * c-cppbuiltin.c (c_cpp_builtins): Define __cpp_impl_destroying_delete. gcc/cp/ * call.c (std_destroying_delete_t_p, destroying_delete_p): New. (aligned_deallocation_fn_p, usual_deallocation_fn_p): Use destroying_delete_p. (build_op_delete_call): Handle destroying delete. * decl2.c (coerce_delete_type): Handle destroying delete. * init.c (build_delete): Don't call dtor with destroying delete. * optimize.c (build_delete_destructor_body): Likewise. libstdc++-v3/ * libsupc++/new (std::destroying_delete_t): New. From-SVN: r266053
This commit is contained in:
parent
7de37c97b4
commit
a6bb6b07f7
@ -1,5 +1,8 @@
|
|||||||
2018-11-12 Jason Merrill <jason@redhat.com>
|
2018-11-12 Jason Merrill <jason@redhat.com>
|
||||||
|
|
||||||
|
* c-cppbuiltin.c (c_cpp_builtins): Define
|
||||||
|
__cpp_impl_destroying_delete.
|
||||||
|
|
||||||
* c-cppbuiltin.c (c_cpp_builtins): Change __cpp_explicit_bool to
|
* c-cppbuiltin.c (c_cpp_builtins): Change __cpp_explicit_bool to
|
||||||
__cpp_conditional_explicit.
|
__cpp_conditional_explicit.
|
||||||
|
|
||||||
|
@ -980,6 +980,7 @@ c_cpp_builtins (cpp_reader *pfile)
|
|||||||
/* Set feature test macros for C++2a. */
|
/* Set feature test macros for C++2a. */
|
||||||
cpp_define (pfile, "__cpp_conditional_explicit=201806");
|
cpp_define (pfile, "__cpp_conditional_explicit=201806");
|
||||||
cpp_define (pfile, "__cpp_nontype_template_parameter_class=201806");
|
cpp_define (pfile, "__cpp_nontype_template_parameter_class=201806");
|
||||||
|
cpp_define (pfile, "__cpp_impl_destroying_delete=201806");
|
||||||
}
|
}
|
||||||
if (flag_concepts)
|
if (flag_concepts)
|
||||||
cpp_define (pfile, "__cpp_concepts=201507");
|
cpp_define (pfile, "__cpp_concepts=201507");
|
||||||
|
@ -1,5 +1,14 @@
|
|||||||
2018-11-12 Jason Merrill <jason@redhat.com>
|
2018-11-12 Jason Merrill <jason@redhat.com>
|
||||||
|
|
||||||
|
Implement P0722R3, destroying operator delete.
|
||||||
|
* call.c (std_destroying_delete_t_p, destroying_delete_p): New.
|
||||||
|
(aligned_deallocation_fn_p, usual_deallocation_fn_p): Use
|
||||||
|
destroying_delete_p.
|
||||||
|
(build_op_delete_call): Handle destroying delete.
|
||||||
|
* decl2.c (coerce_delete_type): Handle destroying delete.
|
||||||
|
* init.c (build_delete): Don't call dtor with destroying delete.
|
||||||
|
* optimize.c (build_delete_destructor_body): Likewise.
|
||||||
|
|
||||||
Implement P0780R2, pack expansion in lambda init-capture.
|
Implement P0780R2, pack expansion in lambda init-capture.
|
||||||
* parser.c (cp_parser_lambda_introducer): Parse pack init-capture.
|
* parser.c (cp_parser_lambda_introducer): Parse pack init-capture.
|
||||||
* pt.c (tsubst_pack_expansion): Handle init-capture packs.
|
* pt.c (tsubst_pack_expansion): Handle init-capture packs.
|
||||||
|
@ -6190,6 +6190,31 @@ aligned_allocation_fn_p (tree t)
|
|||||||
return (a && same_type_p (TREE_VALUE (a), align_type_node));
|
return (a && same_type_p (TREE_VALUE (a), align_type_node));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* True if T is std::destroying_delete_t. */
|
||||||
|
|
||||||
|
static bool
|
||||||
|
std_destroying_delete_t_p (tree t)
|
||||||
|
{
|
||||||
|
return (TYPE_CONTEXT (t) == std_node
|
||||||
|
&& id_equal (TYPE_IDENTIFIER (t), "destroying_delete_t"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A deallocation function with at least two parameters whose second parameter
|
||||||
|
type is of type std::destroying_delete_t is a destroying operator delete. A
|
||||||
|
destroying operator delete shall be a class member function named operator
|
||||||
|
delete. [ Note: Array deletion cannot use a destroying operator
|
||||||
|
delete. --end note ] */
|
||||||
|
|
||||||
|
tree
|
||||||
|
destroying_delete_p (tree t)
|
||||||
|
{
|
||||||
|
tree a = TYPE_ARG_TYPES (TREE_TYPE (t));
|
||||||
|
if (!a || !TREE_CHAIN (a))
|
||||||
|
return NULL_TREE;
|
||||||
|
tree type = TREE_VALUE (TREE_CHAIN (a));
|
||||||
|
return std_destroying_delete_t_p (type) ? type : NULL_TREE;
|
||||||
|
}
|
||||||
|
|
||||||
/* Returns true iff T, an element of an OVERLOAD chain, is a usual deallocation
|
/* Returns true iff T, an element of an OVERLOAD chain, is a usual deallocation
|
||||||
function (3.7.4.2 [basic.stc.dynamic.deallocation]) with a parameter of
|
function (3.7.4.2 [basic.stc.dynamic.deallocation]) with a parameter of
|
||||||
std::align_val_t. */
|
std::align_val_t. */
|
||||||
@ -6207,6 +6232,8 @@ aligned_deallocation_fn_p (tree t)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
tree a = FUNCTION_ARG_CHAIN (t);
|
tree a = FUNCTION_ARG_CHAIN (t);
|
||||||
|
if (destroying_delete_p (t))
|
||||||
|
a = TREE_CHAIN (a);
|
||||||
if (same_type_p (TREE_VALUE (a), align_type_node)
|
if (same_type_p (TREE_VALUE (a), align_type_node)
|
||||||
&& TREE_CHAIN (a) == void_list_node)
|
&& TREE_CHAIN (a) == void_list_node)
|
||||||
return true;
|
return true;
|
||||||
@ -6242,6 +6269,8 @@ usual_deallocation_fn_p (tree t)
|
|||||||
tree chain = FUNCTION_ARG_CHAIN (t);
|
tree chain = FUNCTION_ARG_CHAIN (t);
|
||||||
if (!chain)
|
if (!chain)
|
||||||
return false;
|
return false;
|
||||||
|
if (destroying_delete_p (t))
|
||||||
|
chain = TREE_CHAIN (chain);
|
||||||
if (chain == void_list_node
|
if (chain == void_list_node
|
||||||
|| ((!global || flag_sized_deallocation)
|
|| ((!global || flag_sized_deallocation)
|
||||||
&& second_parm_is_size_t (t)))
|
&& second_parm_is_size_t (t)))
|
||||||
@ -6307,6 +6336,7 @@ build_op_delete_call (enum tree_code code, tree addr, tree size,
|
|||||||
fns = lookup_name_nonclass (fnname);
|
fns = lookup_name_nonclass (fnname);
|
||||||
|
|
||||||
/* Strip const and volatile from addr. */
|
/* Strip const and volatile from addr. */
|
||||||
|
tree oaddr = addr;
|
||||||
addr = cp_convert (ptr_type_node, addr, complain);
|
addr = cp_convert (ptr_type_node, addr, complain);
|
||||||
|
|
||||||
if (placement)
|
if (placement)
|
||||||
@ -6484,9 +6514,24 @@ build_op_delete_call (enum tree_code code, tree addr, tree size,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
tree destroying = destroying_delete_p (fn);
|
||||||
|
if (destroying)
|
||||||
|
{
|
||||||
|
/* Strip const and volatile from addr but retain the type of the
|
||||||
|
object. */
|
||||||
|
tree rtype = TREE_TYPE (TREE_TYPE (oaddr));
|
||||||
|
rtype = cv_unqualified (rtype);
|
||||||
|
rtype = TYPE_POINTER_TO (rtype);
|
||||||
|
addr = cp_convert (rtype, oaddr, complain);
|
||||||
|
destroying = build_functional_cast (destroying, NULL_TREE,
|
||||||
|
complain);
|
||||||
|
}
|
||||||
|
|
||||||
tree ret;
|
tree ret;
|
||||||
vec<tree, va_gc> *args = make_tree_vector ();
|
vec<tree, va_gc> *args = make_tree_vector ();
|
||||||
args->quick_push (addr);
|
args->quick_push (addr);
|
||||||
|
if (destroying)
|
||||||
|
args->quick_push (destroying);
|
||||||
if (second_parm_is_size_t (fn))
|
if (second_parm_is_size_t (fn))
|
||||||
args->quick_push (size);
|
args->quick_push (size);
|
||||||
if (aligned_deallocation_fn_p (fn))
|
if (aligned_deallocation_fn_p (fn))
|
||||||
|
@ -6127,6 +6127,7 @@ extern tree build_new_op (location_t, enum tree_code,
|
|||||||
extern tree build_op_call (tree, vec<tree, va_gc> **,
|
extern tree build_op_call (tree, vec<tree, va_gc> **,
|
||||||
tsubst_flags_t);
|
tsubst_flags_t);
|
||||||
extern bool aligned_allocation_fn_p (tree);
|
extern bool aligned_allocation_fn_p (tree);
|
||||||
|
extern tree destroying_delete_p (tree);
|
||||||
extern bool usual_deallocation_fn_p (tree);
|
extern bool usual_deallocation_fn_p (tree);
|
||||||
extern tree build_op_delete_call (enum tree_code, tree, tree,
|
extern tree build_op_delete_call (enum tree_code, tree, tree,
|
||||||
bool, tree, tree,
|
bool, tree, tree,
|
||||||
@ -6456,7 +6457,7 @@ extern void cplus_decl_attributes (tree *, tree, int);
|
|||||||
extern void finish_anon_union (tree);
|
extern void finish_anon_union (tree);
|
||||||
extern void cxx_post_compilation_parsing_cleanups (void);
|
extern void cxx_post_compilation_parsing_cleanups (void);
|
||||||
extern tree coerce_new_type (tree, location_t);
|
extern tree coerce_new_type (tree, location_t);
|
||||||
extern tree coerce_delete_type (tree, location_t);
|
extern void coerce_delete_type (tree, location_t);
|
||||||
extern void comdat_linkage (tree);
|
extern void comdat_linkage (tree);
|
||||||
extern void determine_visibility (tree);
|
extern void determine_visibility (tree);
|
||||||
extern void constrain_class_visibility (tree);
|
extern void constrain_class_visibility (tree);
|
||||||
|
@ -13401,7 +13401,7 @@ grok_op_properties (tree decl, bool complain)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (op_flags & OVL_OP_FLAG_DELETE)
|
if (op_flags & OVL_OP_FLAG_DELETE)
|
||||||
TREE_TYPE (decl) = coerce_delete_type (TREE_TYPE (decl), loc);
|
coerce_delete_type (decl, loc);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DECL_IS_OPERATOR_NEW (decl) = 1;
|
DECL_IS_OPERATOR_NEW (decl) = 1;
|
||||||
|
@ -1739,10 +1739,11 @@ coerce_new_type (tree type, location_t loc)
|
|||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
tree
|
void
|
||||||
coerce_delete_type (tree type, location_t loc)
|
coerce_delete_type (tree decl, location_t loc)
|
||||||
{
|
{
|
||||||
int e = 0;
|
int e = 0;
|
||||||
|
tree type = TREE_TYPE (decl);
|
||||||
tree args = TYPE_ARG_TYPES (type);
|
tree args = TYPE_ARG_TYPES (type);
|
||||||
|
|
||||||
gcc_assert (TREE_CODE (type) == FUNCTION_TYPE);
|
gcc_assert (TREE_CODE (type) == FUNCTION_TYPE);
|
||||||
@ -1754,19 +1755,38 @@ coerce_delete_type (tree type, location_t loc)
|
|||||||
void_type_node);
|
void_type_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tree ptrtype = ptr_type_node;
|
||||||
|
if (destroying_delete_p (decl))
|
||||||
|
{
|
||||||
|
if (DECL_CLASS_SCOPE_P (decl))
|
||||||
|
/* If the function is a destroying operator delete declared in class type
|
||||||
|
C, the type of its first parameter shall be C*. */
|
||||||
|
ptrtype = TYPE_POINTER_TO (DECL_CONTEXT (decl));
|
||||||
|
else
|
||||||
|
/* A destroying operator delete shall be a class member function named
|
||||||
|
operator delete. */
|
||||||
|
error_at (loc, "destroying operator delete must be a member function");
|
||||||
|
const ovl_op_info_t *op = IDENTIFIER_OVL_OP_INFO (DECL_NAME (decl));
|
||||||
|
if (op->flags & OVL_OP_FLAG_VEC)
|
||||||
|
error_at (loc, "operator delete[] cannot be a destroying delete");
|
||||||
|
if (!usual_deallocation_fn_p (decl))
|
||||||
|
error_at (loc, "destroying operator delete must be a usual "
|
||||||
|
"deallocation function");
|
||||||
|
}
|
||||||
|
|
||||||
if (!args || args == void_list_node
|
if (!args || args == void_list_node
|
||||||
|| !same_type_p (TREE_VALUE (args), ptr_type_node))
|
|| !same_type_p (TREE_VALUE (args), ptrtype))
|
||||||
{
|
{
|
||||||
e = 2;
|
e = 2;
|
||||||
if (args && args != void_list_node)
|
if (args && args != void_list_node)
|
||||||
args = TREE_CHAIN (args);
|
args = TREE_CHAIN (args);
|
||||||
error_at (loc, "%<operator delete%> takes type %qT as first parameter",
|
error_at (loc, "%<operator delete%> takes type %qT as first parameter",
|
||||||
ptr_type_node);
|
ptrtype);
|
||||||
}
|
}
|
||||||
switch (e)
|
switch (e)
|
||||||
{
|
{
|
||||||
case 2:
|
case 2:
|
||||||
args = tree_cons (NULL_TREE, ptr_type_node, args);
|
args = tree_cons (NULL_TREE, ptrtype, args);
|
||||||
/* Fall through. */
|
/* Fall through. */
|
||||||
case 1:
|
case 1:
|
||||||
type = (cxx_copy_lang_qualifiers
|
type = (cxx_copy_lang_qualifiers
|
||||||
@ -1776,7 +1796,7 @@ coerce_delete_type (tree type, location_t loc)
|
|||||||
default:;
|
default:;
|
||||||
}
|
}
|
||||||
|
|
||||||
return type;
|
TREE_TYPE (decl) = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* DECL is a VAR_DECL for a vtable: walk through the entries in the vtable
|
/* DECL is a VAR_DECL for a vtable: walk through the entries in the vtable
|
||||||
|
@ -4782,6 +4782,7 @@ build_delete (tree otype, tree addr, special_function_kind auto_delete,
|
|||||||
|
|
||||||
tree head = NULL_TREE;
|
tree head = NULL_TREE;
|
||||||
tree do_delete = NULL_TREE;
|
tree do_delete = NULL_TREE;
|
||||||
|
bool destroying_delete = false;
|
||||||
|
|
||||||
if (!deleting)
|
if (!deleting)
|
||||||
{
|
{
|
||||||
@ -4820,6 +4821,11 @@ build_delete (tree otype, tree addr, special_function_kind auto_delete,
|
|||||||
complain);
|
complain);
|
||||||
/* Call the complete object destructor. */
|
/* Call the complete object destructor. */
|
||||||
auto_delete = sfk_complete_destructor;
|
auto_delete = sfk_complete_destructor;
|
||||||
|
if (do_delete != error_mark_node)
|
||||||
|
{
|
||||||
|
tree fn = get_callee_fndecl (do_delete);
|
||||||
|
destroying_delete = destroying_delete_p (fn);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (TYPE_GETS_REG_DELETE (type))
|
else if (TYPE_GETS_REG_DELETE (type))
|
||||||
{
|
{
|
||||||
@ -4832,7 +4838,7 @@ build_delete (tree otype, tree addr, special_function_kind auto_delete,
|
|||||||
complain);
|
complain);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type_build_dtor_call (type))
|
if (!destroying_delete && type_build_dtor_call (type))
|
||||||
expr = build_dtor_call (cp_build_fold_indirect_ref (addr),
|
expr = build_dtor_call (cp_build_fold_indirect_ref (addr),
|
||||||
auto_delete, flags, complain);
|
auto_delete, flags, complain);
|
||||||
else
|
else
|
||||||
|
@ -117,11 +117,6 @@ build_delete_destructor_body (tree delete_dtor, tree complete_dtor)
|
|||||||
tree parm = DECL_ARGUMENTS (delete_dtor);
|
tree parm = DECL_ARGUMENTS (delete_dtor);
|
||||||
tree virtual_size = cxx_sizeof (current_class_type);
|
tree virtual_size = cxx_sizeof (current_class_type);
|
||||||
|
|
||||||
/* Call the corresponding complete destructor. */
|
|
||||||
gcc_assert (complete_dtor);
|
|
||||||
tree call_dtor = build_cxx_call (complete_dtor, 1, &parm,
|
|
||||||
tf_warning_or_error);
|
|
||||||
|
|
||||||
/* Call the delete function. */
|
/* Call the delete function. */
|
||||||
tree call_delete = build_op_delete_call (DELETE_EXPR, current_class_ptr,
|
tree call_delete = build_op_delete_call (DELETE_EXPR, current_class_ptr,
|
||||||
virtual_size,
|
virtual_size,
|
||||||
@ -130,10 +125,26 @@ build_delete_destructor_body (tree delete_dtor, tree complete_dtor)
|
|||||||
/*alloc_fn=*/NULL_TREE,
|
/*alloc_fn=*/NULL_TREE,
|
||||||
tf_warning_or_error);
|
tf_warning_or_error);
|
||||||
|
|
||||||
/* Operator delete must be called, whether or not the dtor throws. */
|
tree op = get_callee_fndecl (call_delete);
|
||||||
add_stmt (build2 (TRY_FINALLY_EXPR, void_type_node, call_dtor, call_delete));
|
if (op && DECL_P (op) && destroying_delete_p (op))
|
||||||
|
{
|
||||||
|
/* The destroying delete will handle calling complete_dtor. */
|
||||||
|
add_stmt (call_delete);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Call the corresponding complete destructor. */
|
||||||
|
gcc_assert (complete_dtor);
|
||||||
|
tree call_dtor = build_cxx_call (complete_dtor, 1, &parm,
|
||||||
|
tf_warning_or_error);
|
||||||
|
|
||||||
/* Return the address of the object. */
|
/* Operator delete must be called, whether or not the dtor throws. */
|
||||||
|
add_stmt (build2 (TRY_FINALLY_EXPR, void_type_node,
|
||||||
|
call_dtor, call_delete));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return the address of the object.
|
||||||
|
??? How is it useful to return an invalid address? */
|
||||||
if (targetm.cxx.cdtor_returns_this ())
|
if (targetm.cxx.cdtor_returns_this ())
|
||||||
{
|
{
|
||||||
tree val = DECL_ARGUMENTS (delete_dtor);
|
tree val = DECL_ARGUMENTS (delete_dtor);
|
||||||
|
41
gcc/testsuite/g++.dg/cpp2a/destroying-delete1.C
Normal file
41
gcc/testsuite/g++.dg/cpp2a/destroying-delete1.C
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// { dg-do run { target c++2a } }
|
||||||
|
|
||||||
|
#include <new>
|
||||||
|
|
||||||
|
int adt, adl;
|
||||||
|
struct A {
|
||||||
|
~A() { ++adt; }
|
||||||
|
void operator delete (A *p, std::destroying_delete_t) {
|
||||||
|
++adl;
|
||||||
|
if (adt) __builtin_abort();
|
||||||
|
p->~A();
|
||||||
|
::operator delete (p);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct B {
|
||||||
|
virtual ~B() {}
|
||||||
|
void operator delete(void*, std::size_t) { __builtin_abort(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
int edel, edtor;
|
||||||
|
struct E : B {
|
||||||
|
~E() { ++edtor; }
|
||||||
|
void operator delete(E *p, std::destroying_delete_t) {
|
||||||
|
++edel;
|
||||||
|
if (edtor) __builtin_abort();
|
||||||
|
p->~E();
|
||||||
|
::operator delete(p);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
int main() {
|
||||||
|
A* ap = new A;
|
||||||
|
delete ap;
|
||||||
|
if (adl != 1 || adt != 1)
|
||||||
|
__builtin_abort();
|
||||||
|
|
||||||
|
B* bp = new E;
|
||||||
|
delete bp; // 2: uses E::operator delete(E*, std::destroying_delete_t)
|
||||||
|
if (edel != 1 || edtor != 1)
|
||||||
|
__builtin_abort();
|
||||||
|
}
|
@ -426,6 +426,10 @@
|
|||||||
# error "__cpp_nontype_template_parameter_class != 201806"
|
# error "__cpp_nontype_template_parameter_class != 201806"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if __cpp_impl_destroying_delete != 201806
|
||||||
|
# error "__cpp_impl_destroying_delete != 201806"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __has_cpp_attribute
|
#ifdef __has_cpp_attribute
|
||||||
|
|
||||||
# if ! __has_cpp_attribute(maybe_unused)
|
# if ! __has_cpp_attribute(maybe_unused)
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
2018-11-12 Jason Merrill <jason@redhat.com>
|
||||||
|
|
||||||
|
* libsupc++/new (std::destroying_delete_t): New.
|
||||||
|
|
||||||
2018-11-12 Jonathan Wakely <jwakely@redhat.com>
|
2018-11-12 Jonathan Wakely <jwakely@redhat.com>
|
||||||
|
|
||||||
PR libstdc++/87963
|
PR libstdc++/87963
|
||||||
|
@ -208,6 +208,18 @@ namespace std
|
|||||||
#endif // _GLIBCXX_HAVE_BUILTIN_LAUNDER
|
#endif // _GLIBCXX_HAVE_BUILTIN_LAUNDER
|
||||||
#endif // C++17
|
#endif // C++17
|
||||||
|
|
||||||
|
#if __cpp_impl_destroying_delete
|
||||||
|
#define __cpp_lib_destroying_delete 201806L
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
struct destroying_delete_t
|
||||||
|
{
|
||||||
|
explicit destroying_delete_t() = default;
|
||||||
|
};
|
||||||
|
inline constexpr destroying_delete_t destroying_delete{};
|
||||||
|
}
|
||||||
|
#endif // destroying delete
|
||||||
|
|
||||||
#pragma GCC visibility pop
|
#pragma GCC visibility pop
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user