Bootstrapped/regtested on x86_64-pc-linux-gnu.

-- >8 --
The regression is caused by a nested aggregate subobject that
contains both separately emitted non-constant-initialized members
and remaining constant-initialized members. If the non-constant
initialization of a subsequent member in the enclosing aggregate
throws an exception, both the cleanup for the inner
non-constant-initialized members and the cleanup for the entire
outer subobject are executed, resulting in double destruction.

For the test case in the original PR, the generated sequence is as follows:

        construct the constant-initialized part of a
        construct a.b.c
        enable cleanup for a.b.c
        enable cleanup for a.b
        throw
        disable cleanup for a.b
        disable cleanup for a.b.c

When the object at the current recursion level cannot be fully
split out, the cleanups for subobjects introduced within that level
should be disabled, preventing them from escaping to an outer level
and overlapping with the cleanup for the enclosing object.

This transforms the sequence into the following form:

        construct the constant-initialized part of a
        construct a.b.c
        enable cleanup for a.b.c
        disable cleanup for a.b.c
        enable cleanup for a.b
        throw
        disable cleanup for a.b

        PR c++/125771

gcc/cp/ChangeLog:

        * typeck2.cc (disable_temp_cleanups_since): New function.
        (split_nonconstant_init_1): Disable recursive-level cleanups when
        the constructor is only partially split.

gcc/testsuite/ChangeLog:

        * g++.dg/eh/aggregate3.C: New test.

Signed-off-by: Wang Jinghao <[email protected]>
---
 gcc/cp/typeck2.cc                    | 35 ++++++++++++++++++++--
 gcc/testsuite/g++.dg/eh/aggregate3.C | 44 ++++++++++++++++++++++++++++
 2 files changed, 76 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/eh/aggregate3.C

diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc
index 12c34ba6b3a..cd45bedea45 100644
--- a/gcc/cp/typeck2.cc
+++ b/gcc/cp/typeck2.cc
@@ -573,6 +573,21 @@ build_disable_temp_cleanup (tree f)
   return build2 (MODIFY_EXPR, TREE_TYPE (d), d, i);
 }
 
+/* Disable and forget the cleanups added to FLAGS after LEN.  */
+
+static void
+disable_temp_cleanups_since (vec<tree,va_gc> **flags, unsigned len)
+{
+  if (!*flags)
+    return;
+
+  unsigned n = vec_safe_length (*flags);
+  for (unsigned i = len; i < n; ++i)
+    finish_expr_stmt (build_disable_temp_cleanup ((**flags)[i]));
+
+  (*flags)->truncate (len);
+}
+
 /* The recursive part of split_nonconstant_init.  DEST is an lvalue
    expression to which INIT should be assigned.  INIT is a CONSTRUCTOR.
    Return true if the whole of the value was initialized by the
@@ -590,6 +605,7 @@ split_nonconstant_init_1 (tree dest, tree init, bool last,
   bool complete_p = true;
   HOST_WIDE_INT num_split_elts = 0;
   tree last_split_elt = NULL_TREE;
+  unsigned cleanup_start = vec_safe_length (*flags);
 
   switch (TREE_CODE (type))
     {
@@ -811,10 +827,23 @@ split_nonconstant_init_1 (tree dest, tree init, bool last,
 
   /* We didn't split out anything.  */
   if (num_split_elts == 0)
-    return false;
+    {
+      /* When the object at the current recursion level cannot be fully
+        split out, disable the cleanups for subobjects newly introduced at
+        that level, preventing them from escaping to an outer level and
+        overlapping with the cleanup for the enclosing object.  */
+      disable_temp_cleanups_since (flags, cleanup_start);
+      return false;
+    }
+
+  complete_p = (complete_p
+               && complete_ctor_at_level_p (TREE_TYPE (init), num_split_elts,
+                                            inner_type));
+  if (!complete_p)
+    /* Likewise.  */
+    disable_temp_cleanups_since (flags, cleanup_start);
 
-  return complete_p && complete_ctor_at_level_p (TREE_TYPE (init),
-                                                num_split_elts, inner_type);
+  return complete_p;
 }
 
 /* A subroutine of store_init_value.  Splits non-constant static
diff --git a/gcc/testsuite/g++.dg/eh/aggregate3.C 
b/gcc/testsuite/g++.dg/eh/aggregate3.C
new file mode 100644
index 00000000000..57d4f4a06c5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/eh/aggregate3.C
@@ -0,0 +1,44 @@
+// PR c++/125771
+// { dg-do run { target c++11 } }
+
+bool guard = false;
+
+int
+fail ()
+{
+  throw 0;
+}
+
+struct C
+{
+  C () {}
+  ~C ()
+  {
+    if (guard)
+      __builtin_abort ();
+    guard = true;
+  }
+};
+
+struct B
+{
+  C c;
+  int j;
+};
+
+struct A
+{
+  B b;
+  int i;
+};
+
+int
+main ()
+{
+  try
+    {
+      A a{{C{}, 0}, fail ()};
+    }
+  catch (...)
+    {}
+}
-- 
2.52.0

Reply via email to