diff options
author | Jason Merrill <jason@redhat.com> | 2024-05-03 09:52:46 -0400 |
---|---|---|
committer | Jason Merrill <jason@redhat.com> | 2024-05-03 16:00:02 -0400 |
commit | 8f3afb83c879f1bfa722a963a07c06aaf174ef72 (patch) | |
tree | 3e62425428c0384ea200388eb961bee5442097f8 /gcc | |
parent | c943d7b5c40f447b12431df9ad27a47dad95026d (diff) | |
download | gcc-8f3afb83c879f1bfa722a963a07c06aaf174ef72.zip gcc-8f3afb83c879f1bfa722a963a07c06aaf174ef72.tar.gz gcc-8f3afb83c879f1bfa722a963a07c06aaf174ef72.tar.bz2 |
c++: initializer_list<string> and EH [PR114935]
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.
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/cp/cp-gimplify.cc | 18 | ||||
-rw-r--r-- | gcc/cp/cp-tree.h | 1 | ||||
-rw-r--r-- | gcc/cp/typeck2.cc | 34 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp0x/initlist-eh1.C | 25 |
4 files changed, 60 insertions, 18 deletions
diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc index ab5acd1..5cbdf0e 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/cp-tree.h b/gcc/cp/cp-tree.h index 1ba7054..52d6841 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/typeck2.cc b/gcc/cp/typeck2.cc index 2985bfd..06bad4d 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 0000000..93baf1c --- /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 (); +} |