Tested x86_64-pc-linux-gnu, applying to trunk and 14. -- 8< --
When we initialize an array of a type with a non-trivial destructor, such as the backing array for the initializer_list, we have a cleanup to destroy any constructed elements if a later constructor throws. When the array being created is a variable, the end of that EH region naturally coincides with the beginning of the EH region for the cleanup for the variable as a whole. But if the array is a temporary, or a subobject of one, the array cleanup region lasts for the rest of the full-expression, along with the normal cleanup for the TARGET_EXPR. As a result, when tata throws we clean it up twice. Before r14-1705 we avoided this by disabling the array cleanup in split_nonconstant_init, but after that we don't go through split_nonconstant_init, so let's handle it in cp_genericize_target_expr. PR c++/114935 gcc/cp/ChangeLog: * cp-gimplify.cc (cp_genericize_init): Add flags parm. (cp_genericize_init_expr): Pass nullptr. (cp_genericize_target_expr): Handle cleanup flags. * typeck2.cc (build_disable_temp_cleanup): Factor out of... (split_nonconstant_init): ...here. * cp-tree.h (build_disable_temp_cleanup): Declare. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/initlist-eh1.C: New test. --- gcc/cp/cp-tree.h | 1 + gcc/cp/cp-gimplify.cc | 18 +++++++++--- gcc/cp/typeck2.cc | 34 +++++++++++++---------- gcc/testsuite/g++.dg/cpp0x/initlist-eh1.C | 25 +++++++++++++++++ 4 files changed, 60 insertions(+), 18 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/initlist-eh1.C diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 1ba7054f8bc..52d6841559c 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -8411,6 +8411,7 @@ extern int abstract_virtuals_error (abstract_class_use, tree, tsubst_flags_t = tf_warning_or_error); extern tree store_init_value (tree, tree, vec<tree, va_gc>**, int); +extern tree build_disable_temp_cleanup (tree); extern tree split_nonconstant_init (tree, tree); extern bool check_narrowing (tree, tree, tsubst_flags_t, bool = false); diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc index ab5acd18c99..5cbdf0ea498 100644 --- a/gcc/cp/cp-gimplify.cc +++ b/gcc/cp/cp-gimplify.cc @@ -1063,11 +1063,11 @@ any_non_eliding_target_exprs (tree ctor) the result. */ static void -cp_genericize_init (tree *replace, tree from, tree to) +cp_genericize_init (tree *replace, tree from, tree to, vec<tree,va_gc>** flags) { tree init = NULL_TREE; if (TREE_CODE (from) == VEC_INIT_EXPR) - init = expand_vec_init_expr (to, from, tf_warning_or_error); + init = expand_vec_init_expr (to, from, tf_warning_or_error, flags); else if (TREE_CODE (from) == CONSTRUCTOR && TREE_SIDE_EFFECTS (from) && ((flag_exceptions @@ -1101,7 +1101,7 @@ cp_genericize_init_expr (tree *stmt_p) /* Return gets confused if we clobber its INIT_EXPR this soon. */ && TREE_CODE (to) != RESULT_DECL) from = TARGET_EXPR_INITIAL (from); - cp_genericize_init (stmt_p, from, to); + cp_genericize_init (stmt_p, from, to, nullptr); } /* For a TARGET_EXPR, change the TARGET_EXPR_INITIAL. We will need to use @@ -1112,9 +1112,19 @@ cp_genericize_target_expr (tree *stmt_p) { iloc_sentinel ils = EXPR_LOCATION (*stmt_p); tree slot = TARGET_EXPR_SLOT (*stmt_p); + vec<tree, va_gc> *flags = make_tree_vector (); cp_genericize_init (&TARGET_EXPR_INITIAL (*stmt_p), - TARGET_EXPR_INITIAL (*stmt_p), slot); + TARGET_EXPR_INITIAL (*stmt_p), slot, &flags); gcc_assert (!DECL_INITIAL (slot)); + for (tree f : flags) + { + /* Once initialization is complete TARGET_EXPR_CLEANUP becomes active, so + disable any subobject cleanups. */ + tree d = build_disable_temp_cleanup (f); + auto &r = TARGET_EXPR_INITIAL (*stmt_p); + r = add_stmt_to_compound (r, d); + } + release_tree_vector (flags); } /* Similar to if (target_expr_needs_replace) replace_decl, but TP is the diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc index 2985bfdf9ec..06bad4d3303 100644 --- a/gcc/cp/typeck2.cc +++ b/gcc/cp/typeck2.cc @@ -466,6 +466,25 @@ maybe_push_temp_cleanup (tree sub, vec<tree,va_gc> **flags) } } +/* F is something added to a cleanup flags vec by maybe_push_temp_cleanup or + build_vec_init. Return the code to disable the cleanup it controls. */ + +tree +build_disable_temp_cleanup (tree f) +{ + tree d = f; + tree i = boolean_false_node; + if (TREE_CODE (f) == TREE_LIST) + { + /* To disable a build_vec_init cleanup, set + iterator = maxindex. */ + d = TREE_PURPOSE (f); + i = TREE_VALUE (f); + ggc_free (f); + } + return build2 (MODIFY_EXPR, TREE_TYPE (d), d, i); +} + /* 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 @@ -737,20 +756,7 @@ split_nonconstant_init (tree dest, tree init) init = NULL_TREE; for (tree f : flags) - { - /* See maybe_push_temp_cleanup. */ - tree d = f; - tree i = boolean_false_node; - if (TREE_CODE (f) == TREE_LIST) - { - /* To disable a build_vec_init cleanup, set - iterator = maxindex. */ - d = TREE_PURPOSE (f); - i = TREE_VALUE (f); - ggc_free (f); - } - add_stmt (build2 (MODIFY_EXPR, TREE_TYPE (d), d, i)); - } + add_stmt (build_disable_temp_cleanup (f)); release_tree_vector (flags); code = pop_stmt_list (code); diff --git a/gcc/testsuite/g++.dg/cpp0x/initlist-eh1.C b/gcc/testsuite/g++.dg/cpp0x/initlist-eh1.C new file mode 100644 index 00000000000..93baf1c2abc --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/initlist-eh1.C @@ -0,0 +1,25 @@ +// { dg-do run { target c++11 } } + +#include <initializer_list> + +int as; +struct A { + A(const char *) { ++as; } + A(const A&) { ++as; } + ~A() { --as; } +}; + +void __attribute__((noipa)) +tata(std::initializer_list<A> init) +{ + throw 1; +} + +int +main() +{ + try { tata({ "foo","bar" }); } + catch (...) { } + + if (as != 0) __builtin_abort (); +} base-commit: c943d7b5c40f447b12431df9ad27a47dad95026d -- 2.44.0