mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 03:47:02 -05:00
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.
(cherry picked from commit 8f3afb83c8)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -8406,6 +8406,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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
25
gcc/testsuite/g++.dg/cpp0x/initlist-eh1.C
Normal file
25
gcc/testsuite/g++.dg/cpp0x/initlist-eh1.C
Normal file
@@ -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 ();
|
||||
}
|
||||
Reference in New Issue
Block a user