aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/cp/call.c31
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/destroying-delete5.C4
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/destroying-delete6.C36
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) {
+ }
+}
+