diff options
author | Jason Merrill <jason@redhat.com> | 2018-11-12 23:40:01 -0500 |
---|---|---|
committer | Jason Merrill <jason@gcc.gnu.org> | 2018-11-12 23:40:01 -0500 |
commit | a6bb6b07f76c4431cb5a2a520ac33f7c970b80c1 (patch) | |
tree | 4da8d10f970dd37ab850c548d36cd4980e9a623d /gcc/cp | |
parent | 7de37c97b4031ba61c867cf6fadf63916c666894 (diff) | |
download | gcc-a6bb6b07f76c4431cb5a2a520ac33f7c970b80c1.zip gcc-a6bb6b07f76c4431cb5a2a520ac33f7c970b80c1.tar.gz gcc-a6bb6b07f76c4431cb5a2a520ac33f7c970b80c1.tar.bz2 |
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
Diffstat (limited to 'gcc/cp')
-rw-r--r-- | gcc/cp/ChangeLog | 9 | ||||
-rw-r--r-- | gcc/cp/call.c | 45 | ||||
-rw-r--r-- | gcc/cp/cp-tree.h | 3 | ||||
-rw-r--r-- | gcc/cp/decl.c | 2 | ||||
-rw-r--r-- | gcc/cp/decl2.c | 32 | ||||
-rw-r--r-- | gcc/cp/init.c | 8 | ||||
-rw-r--r-- | gcc/cp/optimize.c | 27 |
7 files changed, 109 insertions, 17 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 2f15c08..5497a08 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -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. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 6f40156..b668e03 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -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)) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 9c4664c..c4d79c0 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -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); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index cf93415..4299405 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -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; diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index a163558..13c156b 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -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 diff --git a/gcc/cp/init.c b/gcc/cp/init.c index a17e160..5a31486 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -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 diff --git a/gcc/cp/optimize.c b/gcc/cp/optimize.c index 3923a5f..da068b5 100644 --- a/gcc/cp/optimize.c +++ b/gcc/cp/optimize.c @@ -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); + + /* 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. */ + /* 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); |