aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2019-12-19 09:06:45 -0500
committerJason Merrill <jason@gcc.gnu.org>2019-12-19 09:06:45 -0500
commit942d334ec3fdf360dfedc0f97d1b4747a1f56f08 (patch)
treeb27d4a2df27541a26c5d007a91095e2cff7fc3d1
parent39f8777c403cbaf2603bbf2dec026c4d87e2dbb9 (diff)
downloadgcc-942d334ec3fdf360dfedc0f97d1b4747a1f56f08.zip
gcc-942d334ec3fdf360dfedc0f97d1b4747a1f56f08.tar.gz
gcc-942d334ec3fdf360dfedc0f97d1b4747a1f56f08.tar.bz2
PR c++/66139 - EH cleanups for partially constructed aggregates.
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. 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. From-SVN: r279576
-rw-r--r--gcc/cp/ChangeLog9
-rw-r--r--gcc/cp/cp-gimplify.c18
-rw-r--r--gcc/cp/cp-tree.h1
-rw-r--r--gcc/cp/tree.c10
-rw-r--r--gcc/cp/typeck2.c13
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/initlist116.C29
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/initlist117.C40
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/lambda/lambda-eh.C3
8 files changed, 117 insertions, 6 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 19e1b88..b2d0c3b 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,12 @@
+2019-12-19 Jason Merrill <jason@redhat.com>
+
+ PR c++/66139 - EH cleanups for partially constructed aggregates.
+ 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.
+
2019-12-18 Jason Merrill <jason@redhat.com>
PR c++/91165 follow-on tweak
diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
index 2b6bd52..fc512cc 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/cp-tree.h b/gcc/cp/cp-tree.h
index 3d1d62c..50cd283 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/tree.c b/gcc/cp/tree.c
index 7b5a3e4..42194cb 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 a9b8936..0e7766a 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 0000000..90dd8d7
--- /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 0000000..415a5de
--- /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 7516fe0..4d1f4f3 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