mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-04-26 07:30:29 +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>
|
||||
|
||||
* c-cppbuiltin.c (c_cpp_builtins): Define
|
||||
__cpp_impl_destroying_delete.
|
||||
|
||||
* c-cppbuiltin.c (c_cpp_builtins): Change __cpp_explicit_bool to
|
||||
__cpp_conditional_explicit.
|
||||
|
||||
|
@ -980,6 +980,7 @@ c_cpp_builtins (cpp_reader *pfile)
|
||||
/* Set feature test macros for C++2a. */
|
||||
cpp_define (pfile, "__cpp_conditional_explicit=201806");
|
||||
cpp_define (pfile, "__cpp_nontype_template_parameter_class=201806");
|
||||
cpp_define (pfile, "__cpp_impl_destroying_delete=201806");
|
||||
}
|
||||
if (flag_concepts)
|
||||
cpp_define (pfile, "__cpp_concepts=201507");
|
||||
|
@ -1,5 +1,14 @@
|
||||
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.
|
||||
* parser.c (cp_parser_lambda_introducer): Parse pack init-capture.
|
||||
* 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));
|
||||
}
|
||||
|
||||
/* 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
|
||||
function (3.7.4.2 [basic.stc.dynamic.deallocation]) with a parameter of
|
||||
std::align_val_t. */
|
||||
@ -6207,6 +6232,8 @@ aligned_deallocation_fn_p (tree t)
|
||||
return false;
|
||||
|
||||
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)
|
||||
&& TREE_CHAIN (a) == void_list_node)
|
||||
return true;
|
||||
@ -6242,6 +6269,8 @@ usual_deallocation_fn_p (tree t)
|
||||
tree chain = FUNCTION_ARG_CHAIN (t);
|
||||
if (!chain)
|
||||
return false;
|
||||
if (destroying_delete_p (t))
|
||||
chain = TREE_CHAIN (chain);
|
||||
if (chain == void_list_node
|
||||
|| ((!global || flag_sized_deallocation)
|
||||
&& 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);
|
||||
|
||||
/* Strip const and volatile from addr. */
|
||||
tree oaddr = addr;
|
||||
addr = cp_convert (ptr_type_node, addr, complain);
|
||||
|
||||
if (placement)
|
||||
@ -6484,9 +6514,24 @@ build_op_delete_call (enum tree_code code, tree addr, tree size,
|
||||
}
|
||||
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;
|
||||
vec<tree, va_gc> *args = make_tree_vector ();
|
||||
args->quick_push (addr);
|
||||
if (destroying)
|
||||
args->quick_push (destroying);
|
||||
if (second_parm_is_size_t (fn))
|
||||
args->quick_push (size);
|
||||
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> **,
|
||||
tsubst_flags_t);
|
||||
extern bool aligned_allocation_fn_p (tree);
|
||||
extern tree destroying_delete_p (tree);
|
||||
extern bool usual_deallocation_fn_p (tree);
|
||||
extern tree build_op_delete_call (enum tree_code, 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 cxx_post_compilation_parsing_cleanups (void);
|
||||
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 determine_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)
|
||||
TREE_TYPE (decl) = coerce_delete_type (TREE_TYPE (decl), loc);
|
||||
coerce_delete_type (decl, loc);
|
||||
else
|
||||
{
|
||||
DECL_IS_OPERATOR_NEW (decl) = 1;
|
||||
|
@ -1739,10 +1739,11 @@ coerce_new_type (tree type, location_t loc)
|
||||
return type;
|
||||
}
|
||||
|
||||
tree
|
||||
coerce_delete_type (tree type, location_t loc)
|
||||
void
|
||||
coerce_delete_type (tree decl, location_t loc)
|
||||
{
|
||||
int e = 0;
|
||||
tree type = TREE_TYPE (decl);
|
||||
tree args = TYPE_ARG_TYPES (type);
|
||||
|
||||
gcc_assert (TREE_CODE (type) == FUNCTION_TYPE);
|
||||
@ -1754,19 +1755,38 @@ coerce_delete_type (tree type, location_t loc)
|
||||
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
|
||||
|| !same_type_p (TREE_VALUE (args), ptr_type_node))
|
||||
|| !same_type_p (TREE_VALUE (args), ptrtype))
|
||||
{
|
||||
e = 2;
|
||||
if (args && args != void_list_node)
|
||||
args = TREE_CHAIN (args);
|
||||
error_at (loc, "%<operator delete%> takes type %qT as first parameter",
|
||||
ptr_type_node);
|
||||
ptrtype);
|
||||
}
|
||||
switch (e)
|
||||
{
|
||||
case 2:
|
||||
args = tree_cons (NULL_TREE, ptr_type_node, args);
|
||||
args = tree_cons (NULL_TREE, ptrtype, args);
|
||||
/* Fall through. */
|
||||
case 1:
|
||||
type = (cxx_copy_lang_qualifiers
|
||||
@ -1776,7 +1796,7 @@ coerce_delete_type (tree type, location_t loc)
|
||||
default:;
|
||||
}
|
||||
|
||||
return type;
|
||||
TREE_TYPE (decl) = type;
|
||||
}
|
||||
|
||||
/* 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 do_delete = NULL_TREE;
|
||||
bool destroying_delete = false;
|
||||
|
||||
if (!deleting)
|
||||
{
|
||||
@ -4820,6 +4821,11 @@ build_delete (tree otype, tree addr, special_function_kind auto_delete,
|
||||
complain);
|
||||
/* Call the complete object 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))
|
||||
{
|
||||
@ -4832,7 +4838,7 @@ build_delete (tree otype, tree addr, special_function_kind auto_delete,
|
||||
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),
|
||||
auto_delete, flags, complain);
|
||||
else
|
||||
|
@ -117,11 +117,6 @@ build_delete_destructor_body (tree delete_dtor, tree complete_dtor)
|
||||
tree parm = DECL_ARGUMENTS (delete_dtor);
|
||||
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. */
|
||||
tree call_delete = build_op_delete_call (DELETE_EXPR, current_class_ptr,
|
||||
virtual_size,
|
||||
@ -130,10 +125,26 @@ build_delete_destructor_body (tree delete_dtor, tree complete_dtor)
|
||||
/*alloc_fn=*/NULL_TREE,
|
||||
tf_warning_or_error);
|
||||
|
||||
/* Operator delete must be called, whether or not the dtor throws. */
|
||||
add_stmt (build2 (TRY_FINALLY_EXPR, void_type_node, call_dtor, call_delete));
|
||||
tree op = get_callee_fndecl (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 ())
|
||||
{
|
||||
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"
|
||||
#endif
|
||||
|
||||
#if __cpp_impl_destroying_delete != 201806
|
||||
# error "__cpp_impl_destroying_delete != 201806"
|
||||
#endif
|
||||
|
||||
#ifdef __has_cpp_attribute
|
||||
|
||||
# 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>
|
||||
|
||||
PR libstdc++/87963
|
||||
|
@ -208,6 +208,18 @@ namespace std
|
||||
#endif // _GLIBCXX_HAVE_BUILTIN_LAUNDER
|
||||
#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
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user