From 942d334ec3fdf360dfedc0f97d1b4747a1f56f08 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Thu, 19 Dec 2019 09:06:45 -0500 Subject: 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 --- gcc/cp/ChangeLog | 9 ++++++ gcc/cp/cp-gimplify.c | 18 ++++++++++-- gcc/cp/cp-tree.h | 1 + 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 +- 8 files changed, 117 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/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 + + 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 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 *); extern vec* vec_copy_and_insert (vec*, 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 + +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 t { 0, 1 }; }); + check ([] { std::initializer_list { 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 -- cgit v1.1