c++: keep destroying array after one dtor throws [PR66451]
authorJason Merrill <jason@redhat.com>
Wed, 5 Jan 2022 14:49:37 +0000 (09:49 -0500)
committerJason Merrill <jason@redhat.com>
Fri, 7 Jan 2022 00:24:31 +0000 (19:24 -0500)
When we're cleaning up an array, if one destructor throws, we should still
try to clean up the rest of the array.  We can use TRY_CATCH_EXPR for this,
instead of a TARGET_EXPR like my other recent patches, because a destructor
call can't involve any temporaries that need to live longer.

I thought about only doing this when we call build_vec_delete_1 from
build_vec_init, but it seems appropriate for delete-expressions as well;
we've said that the array's lifetime is over, it makes sense to keep trying
to destroy it.  The standard isn't clear, but clang seems to agree with me.

PR c++/66451

gcc/cp/ChangeLog:

* init.c (build_vec_delete_1): Handle throwing dtor.
(build_vec_init): Tell it we're in a cleanup already.

gcc/testsuite/ChangeLog:

* g++.dg/eh/array3.C: New test.
* g++.dg/eh/array1.C: Mark destructor as throw().
* g++.dg/ipa/devirt-40.C: Likewise.
* g++.dg/warn/pr83054.C: Likewise.
* g++.dg/eh/delete1.C: Shorten array to one element.

gcc/cp/init.c
gcc/testsuite/g++.dg/eh/array1.C
gcc/testsuite/g++.dg/eh/array3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/eh/delete1.C
gcc/testsuite/g++.dg/ipa/devirt-40.C
gcc/testsuite/g++.dg/warn/pr83054.C

index 7c7b810..df63e61 100644 (file)
@@ -4006,7 +4006,8 @@ build_new (location_t loc, vec<tree, va_gc> **placement, tree type,
 static tree
 build_vec_delete_1 (location_t loc, tree base, tree maxindex, tree type,
                    special_function_kind auto_delete_vec,
-                   int use_global_delete, tsubst_flags_t complain)
+                   int use_global_delete, tsubst_flags_t complain,
+                   bool in_cleanup = false)
 {
   tree virtual_size;
   tree ptype = build_pointer_type (type = complete_type (type));
@@ -4109,6 +4110,18 @@ build_vec_delete_1 (location_t loc, tree base, tree maxindex, tree type,
   body = build_compound_expr (loc, body, tmp);
 
   loop = build1 (LOOP_EXPR, void_type_node, body);
+
+  /* If one destructor throws, keep trying to clean up the rest, unless we're
+     already in a build_vec_init cleanup.  */
+  if (flag_exceptions && !in_cleanup && !expr_noexcept_p (tmp, tf_none))
+    {
+      loop = build2 (TRY_CATCH_EXPR, void_type_node, loop,
+                    unshare_expr (loop));
+      /* Tell honor_protect_cleanup_actions to discard this on the
+        exceptional path.  */
+      TRY_CATCH_IS_CLEANUP (loop) = true;
+    }
+
   loop = build_compound_expr (loc, tbase_init, loop);
 
  no_destructor:
@@ -4490,7 +4503,8 @@ build_vec_init (tree base, tree maxindex, tree init,
 
       e = build_vec_delete_1 (input_location, rval, m,
                              inner_elt_type, sfk_complete_destructor,
-                             /*use_global_delete=*/0, complain);
+                             /*use_global_delete=*/0, complain,
+                             /*in_cleanup*/true);
       if (e == error_mark_node)
        errors = true;
       TARGET_EXPR_CLEANUP (iterator_targ) = e;
index 30b035c..79d62ad 100644 (file)
@@ -2,10 +2,16 @@
 // rather than one for each element.
 // { dg-options "-fdump-tree-gimple" }
 
+#if __cplusplus < 201100L
+#define NOTHROW throw()
+#else
+#define NOTHROW noexcept
+#endif
+
 struct A
 {
   A();
-  ~A();
+  ~A() NOTHROW;
 };
 
 void f()
diff --git a/gcc/testsuite/g++.dg/eh/array3.C b/gcc/testsuite/g++.dg/eh/array3.C
new file mode 100644 (file)
index 0000000..547541b
--- /dev/null
@@ -0,0 +1,40 @@
+// PR c++/66451
+// { dg-do run }
+
+#if __cplusplus > 201100L
+#define THROWING noexcept(false)
+#else
+#define THROWING
+#endif
+
+extern "C" void abort();
+
+int c;
+struct A
+{
+  int a;
+
+  A(int new_a) : a(new_a) { ++c; }
+  A(const A&); // not defined
+  ~A() THROWING
+  {
+    --c;
+    if(a==4)
+      throw a;
+  }
+};
+
+struct B
+{
+  A a[2];
+  ~B() { }
+};
+
+int sink;
+int main()
+{
+  try {
+    B b = {3,4};
+  } catch(...) { }
+  if (c != 0) abort();
+}
index 1727a74..92ed646 100644 (file)
@@ -69,7 +69,7 @@ int ary ()
 {
   deleted = 0;
 
-  Baz *p = new Baz[5];
+  Baz *p = new Baz[1];
   try { delete[] p; }
   catch (...) { return deleted != 1;}
   return 1;
index 32e0d22..31fe150 100644 (file)
@@ -1,4 +1,12 @@
 /* { dg-options "-O2 -fdump-tree-fre3-details"  } */
+
+// A throwing dtor in C++98 mode changes the results.
+#if __cplusplus < 201100L
+#define NOTHROW throw()
+#else
+#define NOTHROW noexcept
+#endif
+
 typedef enum
 {
 } UErrorCode;
@@ -6,7 +14,7 @@ class UnicodeString
 {
 public:
   UnicodeString ();
-  virtual ~UnicodeString ();
+  virtual ~UnicodeString () NOTHROW;
 };
 class A
 {
index 506c960..5285f94 100644 (file)
@@ -2,6 +2,13 @@
 // { dg-options "-O3 -Wsuggest-final-types" }
 // { dg-do compile }
 
+// A throwing dtor in C++98 mode changes the warning.
+#if __cplusplus < 201100L
+#define NOTHROW throw()
+#else
+#define NOTHROW noexcept
+#endif
+
 extern "C" int printf (const char *, ...);
 struct foo // { dg-warning "final would enable devirtualization of 5 calls" }
 {
@@ -12,7 +19,7 @@ struct foo // { dg-warning "final would enable devirtualization of 5 calls" }
     x = count++;
     printf("this %d = %x\n", x, (void *)this);
   }
-  virtual ~foo () {
+  virtual ~foo () NOTHROW {
     printf("this %d = %x\n", x, (void *)this);
     --count;
   }