[gcc r12-6380] c++: destroying delete, throw in new-expr [PR100588]

Jason Merrill jason@gcc.gnu.org
Sat Jan 8 02:03:53 GMT 2022


https://gcc.gnu.org/g:75047f795111150fd10a8f86f5c72deab10cde77

commit r12-6380-g75047f795111150fd10a8f86f5c72deab10cde77
Author: Jason Merrill <jason@redhat.com>
Date:   Thu Jan 6 13:26:21 2022 -0500

    c++: destroying delete, throw in new-expr [PR100588]
    
    The standard says that a destroying operator delete is preferred, but that
    only applies to the delete-expression, not the cleanup if a new-expression
    initialization throws.  As a result of this patch, several of the destroying
    delete tests don't get EH cleanups, but I'm turning off the warning in cases
    where the initialization can't throw anyway.
    
    It's unclear what should happen if the class does not declare a non-deleting
    operator delete; a proposal in CWG was to call the global delete, which
    makes sense to me if the class doesn't declare its own operator new.  If it
    does, we warn and don't call any deallocation function if initialization
    throws.
    
            PR c++/100588
    
    gcc/cp/ChangeLog:
    
            * call.c (build_op_delete_call): Ignore destroying delete
            if alloc_fn.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/cpp2a/destroying-delete5.C: Expect warning.
            * g++.dg/cpp2a/destroying-delete6.C: New test.

Diff:
---
 gcc/cp/call.c                                   | 31 +++++++++++++++++++--
 gcc/testsuite/g++.dg/cpp2a/destroying-delete5.C |  4 +--
 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 7f7ee88deed..44fc6d0f695 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 553c964b9e9..6113d7f3d9e 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 00000000000..be783738082
--- /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) {
+  }
+}
+


More information about the Gcc-cvs mailing list