Hi!

The following testcase r12-6328, because the elements of the array
are destructed twice, once when the callee encounters delete[] p;
and then second time when the exception is thrown.
The array elts should be only destructed if exception is thrown from
one of the constructors during the build_vec_init emitted code in case of
new expressions, but when the new expression completes, it is IMO
responsibility of user code to delete[] it when it is no longer needed.

So, the following patch arranges for build_vec_init emitted code to clear
the rval variable used to guard the eh cleanup of it at the end, but does so
only if we emit a cleanup like that and only if it is from build_new_1.  For
other uses of build_vec_init the elements should be IMO destructed by the
compiler (and the g++.dg/{eh/{aggregate1,ref-temp2},init/aggr7-eh3}.C tests
verify that behavior).

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2025-01-23  Jakub Jelinek  <ja...@redhat.com>

        PR c++/117827
        * cp-tree.h (build_vec_init): Add another argument defaulted to false.
        * init.cc (build_new_1): Pass NULL, true as extra arguments to
        build_vec_init.
        (build_vec_init): Add NO_CLEANUPS_WHEN_COMPLETE_P argument.  If
        build_vec_delete_1 cleanup is registered and
        NO_CLEANUPS_WHEN_COMPLETE_P is true, copy rval to base, clear rval and
        return from stmt expression base instead of rval.

        * g++.dg/init/array66.C: New test.

--- gcc/cp/cp-tree.h.jj 2025-01-21 16:25:41.452994411 +0100
+++ gcc/cp/cp-tree.h    2025-01-23 10:54:18.363769919 +0100
@@ -7391,7 +7391,8 @@ extern tree build_new                             
(location_t,
 extern tree get_temp_regvar                    (tree, tree);
 extern tree build_vec_init                     (tree, tree, tree, bool, int,
                                                 tsubst_flags_t,
-                                                vec<tree, va_gc> ** = nullptr);
+                                                vec<tree, va_gc> ** = nullptr,
+                                                bool = false);
 extern tree build_delete                       (location_t, tree, tree,
                                                 special_function_kind,
                                                 int, int, tsubst_flags_t);
--- gcc/cp/init.cc.jj   2025-01-22 19:34:41.470604917 +0100
+++ gcc/cp/init.cc      2025-01-23 10:58:51.619891446 +0100
@@ -3718,7 +3718,9 @@ build_new_1 (vec<tree, va_gc> **placemen
                              vecinit,
                              explicit_value_init_p,
                              /*from_array=*/0,
-                              complain);
+                             complain,
+                             NULL,
+                             /*no_cleanups_when_complete_p=*/true);
        }
       else
        {
@@ -4500,14 +4502,22 @@ combine_allocator_temps (tree &init, tre
    FROM_ARRAY is 1 if we should index into INIT in parallel
    with initialization of DECL.
    FROM_ARRAY is 2 if we should index into INIT in parallel,
-   but use assignment instead of initialization.  */
+   but use assignment instead of initialization.
+
+   NO_CLEANUPS_WHEN_COMPLETE_P should be true if destruction
+   of the elements should be disabled after the build_vec_init
+   emitted code finishes.  This is useful for new expressions,
+   where the elements should be destructed only when something
+   throws during the construction, or when something later
+   encounters delete expression for it.  */
 
 tree
 build_vec_init (tree base, tree maxindex, tree init,
                bool explicit_value_init_p,
                int from_array,
                tsubst_flags_t complain,
-               vec<tree, va_gc>** cleanup_flags /* = nullptr */)
+               vec<tree, va_gc>** cleanup_flags /* = nullptr */,
+               bool no_cleanups_when_complete_p /* = false */)
 {
   tree rval;
   tree base2 = NULL_TREE;
@@ -4530,6 +4540,7 @@ build_vec_init (tree base, tree maxindex
   tree obase = base;
   bool xvalue = false;
   bool errors = false;
+  bool clear_rval = false;
   location_t loc = (init ? cp_expr_loc_or_input_loc (init)
                    : location_of (base));
 
@@ -4720,6 +4731,11 @@ build_vec_init (tree base, tree maxindex
        errors = true;
       TARGET_EXPR_CLEANUP (iterator_targ) = e;
       CLEANUP_EH_ONLY (iterator_targ) = true;
+      /* Signal that we want to clear rval near the end of the statement
+        expression so that the the build_vec_delete_1 cleanup does nothing
+        after the whole construction succeeded.  */
+      if (no_cleanups_when_complete_p)
+       clear_rval = true;
 
       /* Since we push this cleanup before doing any initialization, cleanups
         for any temporaries in the initialization are naturally within our
@@ -5096,7 +5112,23 @@ build_vec_init (tree base, tree maxindex
 
   /* The value of the array initialization is the array itself, RVAL
      is a pointer to the first element.  */
-  finish_stmt_expr_expr (rval, stmt_expr);
+  if (clear_rval)
+    {
+      /* If there is a build_vec_delete_1 cleanup on rval, make sure
+        to return the value of rval but clear the rval variable so that
+        the cleanup does nothing when reaching this.  So, emit
+        ({ ... base = rval; rval = nullptr; base; })  */
+      finish_expr_stmt (cp_build_modify_expr (input_location, base,
+                                             NOP_EXPR, rval,
+                                             tf_warning_or_error));
+      finish_expr_stmt (cp_build_modify_expr (input_location, rval,
+                                             NOP_EXPR,
+                                             build_zero_cst (ptype),
+                                             tf_warning_or_error));
+      finish_stmt_expr_expr (base, stmt_expr);
+    }
+  else
+    finish_stmt_expr_expr (rval, stmt_expr);
 
   stmt_expr = finish_init_stmts (is_global, stmt_expr, compound_stmt);
 
--- gcc/testsuite/g++.dg/init/array66.C.jj      2025-01-23 00:41:16.615072941 
+0100
+++ gcc/testsuite/g++.dg/init/array66.C 2025-01-23 00:41:16.615072941 +0100
@@ -0,0 +1,33 @@
+// PR c++/117827
+// { dg-do run { target c++11 } }
+
+struct C {
+  int c;
+  static int d, e;
+  C () : c (0) { ++d; }
+  C (const C &) = delete;
+  C &operator= (const C &) = delete;
+  ~C () { ++e; }
+};
+int C::d, C::e;
+
+C *
+foo (C *p)
+{
+  delete[] p;
+  throw 1;
+}
+
+int
+main ()
+{
+  try
+    {
+      foo (new C[1] {});
+    }
+  catch (...)
+    {
+    }
+  if (C::d != C::e)
+    __builtin_abort ();
+}

        Jakub

Reply via email to