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.
/* 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");
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.
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. */
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;
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)))
fns = lookup_name_nonclass (fnname);
/* Strip const and volatile from addr. */
+ tree oaddr = addr;
addr = cp_convert (ptr_type_node, addr, complain);
if (placement)
}
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))
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,
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);
}
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;
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);
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
default:;
}
- return type;
+ TREE_TYPE (decl) = type;
}
\f
/* DECL is a VAR_DECL for a vtable: walk through the entries in the vtable
tree head = NULL_TREE;
tree do_delete = NULL_TREE;
+ bool destroying_delete = false;
if (!deleting)
{
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))
{
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
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,
/*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);
--- /dev/null
+// { 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();
+}
# 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)
+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
#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