diff options
-rw-r--r-- | gcc/cp/call.c | 31 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/destroying-delete5.C | 4 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/destroying-delete6.C | 36 |
3 files changed, 67 insertions, 4 deletions
diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 7f7ee88..44fc6d0 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -7267,6 +7267,8 @@ build_op_delete_call (enum tree_code code, tree addr, tree size, tree oaddr = addr; addr = cp_convert (ptr_type_node, addr, complain); + tree excluded_destroying = NULL_TREE; + if (placement) { /* "A declaration of a placement deallocation function matches the @@ -7352,6 +7354,15 @@ build_op_delete_call (enum tree_code code, tree addr, tree size, dealloc_info di_elt; if (usual_deallocation_fn_p (elt, &di_elt)) { + /* If we're called for an EH cleanup in a new-expression, we can't + use a destroying delete; the exception was thrown before the + object was constructed. */ + if (alloc_fn && di_elt.destroying) + { + excluded_destroying = elt; + continue; + } + if (!fn) { fn = elt; @@ -7499,6 +7510,14 @@ build_op_delete_call (enum tree_code code, tree addr, tree size, return ret; } + /* If there's only a destroying delete that we can't use because the + object isn't constructed yet, and we used global new, use global + delete as well. */ + if (excluded_destroying + && DECL_NAMESPACE_SCOPE_P (alloc_fn)) + return build_op_delete_call (code, addr, size, true, placement, + alloc_fn, complain); + /* [expr.new] If no unambiguous matching deallocation function can be found, @@ -7508,8 +7527,16 @@ build_op_delete_call (enum tree_code code, tree addr, tree size, { if ((complain & tf_warning) && !placement) - warning (0, "no corresponding deallocation function for %qD", - alloc_fn); + { + bool w = warning (0, + "no corresponding deallocation function for %qD", + alloc_fn); + if (w && excluded_destroying) + inform (DECL_SOURCE_LOCATION (excluded_destroying), "destroying " + "delete %qD cannot be used to release the allocated memory" + " if the initialization throws because the object is not " + "constructed yet", excluded_destroying); + } return NULL_TREE; } diff --git a/gcc/testsuite/g++.dg/cpp2a/destroying-delete5.C b/gcc/testsuite/g++.dg/cpp2a/destroying-delete5.C index 553c964..6113d7f 100644 --- a/gcc/testsuite/g++.dg/cpp2a/destroying-delete5.C +++ b/gcc/testsuite/g++.dg/cpp2a/destroying-delete5.C @@ -18,7 +18,7 @@ void * Expression::operator new(std::size_t sz) int i; -void Expression::operator delete(Expression *p, std::destroying_delete_t) +void Expression::operator delete(Expression *p, std::destroying_delete_t) // { dg-message "destroying delete" } { Expression * e = p; ::i = e->i; @@ -28,7 +28,7 @@ void Expression::operator delete(Expression *p, std::destroying_delete_t) int main() { - auto p = new Expression(); + auto p = new Expression(); // { dg-warning "no corresponding dealloc" } p->i = 1; delete p; if (i != 1) diff --git a/gcc/testsuite/g++.dg/cpp2a/destroying-delete6.C b/gcc/testsuite/g++.dg/cpp2a/destroying-delete6.C new file mode 100644 index 0000000..be78373 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/destroying-delete6.C @@ -0,0 +1,36 @@ +// PR c++/100588 +// { dg-do run { target c++20 } } + +extern "C" void abort (); +extern "C" int puts (const char *); +#include <new> + +#ifndef DEBUG +#define puts(S) +#endif + +class A { + public: + A() { throw 42; } + ~A() { puts("A::~A"); } + + void operator delete(void* p) { + puts("regular delete invoked"); + ::operator delete(p); + } + + void operator delete(A* p, std::destroying_delete_t) { + puts("destroying delete invoked"); + p->~A(); + ::operator delete(p); + abort (); + } +}; + +int main() { + try { + new A; + } catch (int) { + } +} + |