There were several overlapping PRs about failure to clean up fully
constructed subobjects when an exception is thrown during aggregate
initialization of a temporary.  I fixed this for non-temporaries in the
context of 57510, but that fix didn't handle temporaries.  So this patch
does split_nonconstant_init at gimplification time, which is much smaller
than alternatives I tried.

Tested x86_64-pc-linux-gnu, applying to trunk.

        PR c++/57510
        * cp-gimplify.c (cp_gimplify_init_expr): Use split_nonconstant_init.
        * typeck2.c (split_nonconstant_init): Handle non-variable dest.
        (split_nonconstant_init_1): Clear TREE_SIDE_EFFECTS.
        * tree.c (is_local_temp): New.
---
 gcc/cp/cp-tree.h                              |  1 +
 gcc/cp/cp-gimplify.c                          | 18 ++++++++-
 gcc/cp/tree.c                                 | 10 +++++
 gcc/cp/typeck2.c                              | 13 +++++-
 gcc/testsuite/g++.dg/cpp0x/initlist116.C      | 29 ++++++++++++++
 gcc/testsuite/g++.dg/cpp0x/initlist117.C      | 40 +++++++++++++++++++
 gcc/testsuite/g++.dg/cpp0x/lambda/lambda-eh.C |  3 +-
 7 files changed, 108 insertions(+), 6 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/initlist116.C
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/initlist117.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 3d1d62c2de8..50cd2837010 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7359,6 +7359,7 @@ extern tree build_min_non_dep_call_vec            (tree, 
tree, vec<tree, va_gc> *);
 extern vec<tree, va_gc>* vec_copy_and_insert    (vec<tree, va_gc>*, tree, 
unsigned);
 extern tree build_cplus_new                    (tree, tree, tsubst_flags_t);
 extern tree build_local_temp                   (tree);
+extern bool is_local_temp                      (tree);
 extern tree build_aggr_init_expr               (tree, tree);
 extern tree get_target_expr                    (tree);
 extern tree get_target_expr_sfinae             (tree, tsubst_flags_t);
diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
index 2b6bd5295d2..fc512ccff5c 100644
--- a/gcc/cp/cp-gimplify.c
+++ b/gcc/cp/cp-gimplify.c
@@ -513,7 +513,7 @@ gimplify_expr_stmt (tree *stmt_p)
 /* Gimplify initialization from an AGGR_INIT_EXPR.  */
 
 static void
-cp_gimplify_init_expr (tree *expr_p)
+cp_gimplify_init_expr (tree *expr_p, gimple_seq *pre_p)
 {
   tree from = TREE_OPERAND (*expr_p, 1);
   tree to = TREE_OPERAND (*expr_p, 0);
@@ -526,6 +526,20 @@ cp_gimplify_init_expr (tree *expr_p)
   if (TREE_CODE (from) == TARGET_EXPR)
     from = TARGET_EXPR_INITIAL (from);
 
+  /* If we might need to clean up a partially constructed object, break down
+     the CONSTRUCTOR with split_nonconstant_init.  */
+  if (TREE_CODE (from) == CONSTRUCTOR
+      && TREE_SIDE_EFFECTS (from)
+      && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (to)))
+    {
+      gimplify_expr (&to, pre_p, NULL, is_gimple_lvalue, fb_lvalue);
+      replace_placeholders (from, to);
+      from = split_nonconstant_init (to, from);
+      cp_genericize_tree (&from, false);
+      *expr_p = from;
+      return;
+    }
+
   /* Look through any COMPOUND_EXPRs, since build_compound_expr pushes them
      inside the TARGET_EXPR.  */
   for (t = from; t; )
@@ -717,7 +731,7 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, 
gimple_seq *post_p)
         LHS of an assignment might also be involved in the RHS, as in bug
         25979.  */
     case INIT_EXPR:
-      cp_gimplify_init_expr (expr_p);
+      cp_gimplify_init_expr (expr_p, pre_p);
       if (TREE_CODE (*expr_p) != INIT_EXPR)
        return GS_OK;
       /* Fall through.  */
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 7b5a3e423f9..42194cb937f 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -539,6 +539,16 @@ build_local_temp (tree type)
   return slot;
 }
 
+/* Return whether DECL is such a local temporary.  */
+
+bool
+is_local_temp (tree decl)
+{
+  return (VAR_P (decl) && DECL_ARTIFICIAL (decl)
+         && !TREE_STATIC (decl)
+         && DECL_FUNCTION_SCOPE_P (decl));
+}
+
 /* Set various status flags when building an AGGR_INIT_EXPR object T.  */
 
 static void
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index a9b89369f07..0e7766a1f3d 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -749,6 +749,7 @@ split_nonconstant_init_1 (tree dest, tree init)
 
   /* The rest of the initializer is now a constant. */
   TREE_CONSTANT (init) = 1;
+  TREE_SIDE_EFFECTS (init) = 0;
 
   /* We didn't split out anything.  */
   if (num_split_elts == 0)
@@ -777,8 +778,16 @@ split_nonconstant_init (tree dest, tree init)
       if (split_nonconstant_init_1 (dest, init))
        init = NULL_TREE;
       code = pop_stmt_list (code);
-      DECL_INITIAL (dest) = init;
-      TREE_READONLY (dest) = 0;
+      if (VAR_P (dest) && !is_local_temp (dest))
+       {
+         DECL_INITIAL (dest) = init;
+         TREE_READONLY (dest) = 0;
+       }
+      else if (init)
+       {
+         tree ie = build2 (INIT_EXPR, void_type_node, dest, init);
+         code = add_stmt_to_compound (ie, code);
+       }
     }
   else if (TREE_CODE (init) == STRING_CST
           && array_of_runtime_bound_p (TREE_TYPE (dest)))
diff --git a/gcc/testsuite/g++.dg/cpp0x/initlist116.C 
b/gcc/testsuite/g++.dg/cpp0x/initlist116.C
new file mode 100644
index 00000000000..90dd8d70d63
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/initlist116.C
@@ -0,0 +1,29 @@
+// PR c++/66139
+// { dg-do run { target c++11 } }
+
+int constructed = 0;
+
+class lock_guard_ext{
+public:
+  lock_guard_ext() { ++constructed; }
+  ~lock_guard_ext() { --constructed; }
+};
+ 
+struct Access {
+  lock_guard_ext lock;
+  int value;
+};
+ 
+int t() {
+  throw 0;
+}
+
+Access foo1() {
+  return { {}, t() };
+}
+ 
+int main () {
+  try { foo1(); } catch (int) {}
+  if (constructed != 0)
+    __builtin_abort();
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/initlist117.C 
b/gcc/testsuite/g++.dg/cpp0x/initlist117.C
new file mode 100644
index 00000000000..415a5de2dd1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/initlist117.C
@@ -0,0 +1,40 @@
+// PR c++/66139
+// { dg-do run { target c++11 } }
+
+#include <initializer_list>
+
+int c, d;
+
+struct a
+{
+  a (int i) { if (i) throw i; c++; }
+  ~a () { d++; }
+};
+
+void check (void (*f) ())
+{
+  try
+  {
+    c = d = 0;
+    f ();
+  }
+  catch (int)
+  {
+    if (c != 1 || d != 1)
+      __builtin_abort ();
+    return;
+  }
+  __builtin_abort ();
+}
+
+int main ()
+{
+  struct s { a x, y; };
+  check ([] { s t { 0, 1 }; });
+  check ([] { s { 0, 1 }; });
+  check ([] { a t[2] { 0, 1 }; });
+  using array = a[2];
+  check ([] { array { 0, 1 }; });
+  check ([] { std::initializer_list <a> t { 0, 1 }; });
+  check ([] { std::initializer_list <a> { 0, 1 }; });
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-eh.C 
b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-eh.C
index 7516fe0fe01..4d1f4f3edfc 100644
--- a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-eh.C
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-eh.C
@@ -1,8 +1,7 @@
 // Test that we properly clean up if we get an exception in the middle of
 // constructing the closure object.
 
-// This test fails because of PR 41449; it isn't a lambda issue.
-// { dg-do run { xfail *-*-* } }
+// { dg-do run }
 // { dg-require-effective-target c++11 }
 
 struct A

base-commit: 98d9b1ea26c52e18a921dc20afc5dd8a0df5b619
-- 
2.18.1

Reply via email to