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
This commit is contained in:
parent
39f8777c40
commit
942d334ec3
@ -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
|
||||
|
@ -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. */
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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)))
|
||||
|
29
gcc/testsuite/g++.dg/cpp0x/initlist116.C
Normal file
29
gcc/testsuite/g++.dg/cpp0x/initlist116.C
Normal file
@ -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();
|
||||
}
|
40
gcc/testsuite/g++.dg/cpp0x/initlist117.C
Normal file
40
gcc/testsuite/g++.dg/cpp0x/initlist117.C
Normal file
@ -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 }; });
|
||||
}
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user