c++: fix array cleanup with throwing temp dtor

While working on PR66139 I noticed that if the destructor of a temporary
created during array initialization throws, we were failing to destroy the
last array element constructed.  Throwing destructors are rare since C++11,
but this should be fixed.

gcc/cp/ChangeLog:

	* init.c (build_vec_init): Append the decrement to elt_init.

gcc/testsuite/ChangeLog:

	* g++.dg/eh/array2.C: New test.
This commit is contained in:
Jason Merrill 2022-01-01 13:07:59 -05:00
parent 092e60f57a
commit c743614e70
2 changed files with 54 additions and 6 deletions

View File

@ -4665,11 +4665,13 @@ build_vec_init (tree base, tree maxindex, tree init,
finish_for_cond (build2 (GT_EXPR, boolean_type_node, iterator,
build_int_cst (TREE_TYPE (iterator), -1)),
for_stmt, false, 0);
elt_init = cp_build_unary_op (PREDECREMENT_EXPR, iterator, false,
complain);
if (elt_init == error_mark_node)
errors = true;
finish_for_expr (elt_init, for_stmt);
/* We used to pass this decrement to finish_for_expr; now we add it to
elt_init below so it's part of the same full-expression as the
initialization, and thus happens before any potentially throwing
temporary cleanups. */
tree decr = cp_build_unary_op (PREDECREMENT_EXPR, iterator, false,
complain);
to = build1 (INDIRECT_REF, type, base);
@ -4794,7 +4796,10 @@ build_vec_init (tree base, tree maxindex, tree init,
current_stmt_tree ()->stmts_are_full_exprs_p = 1;
if (elt_init && !errors)
finish_expr_stmt (elt_init);
elt_init = build2 (COMPOUND_EXPR, void_type_node, elt_init, decr);
else
elt_init = decr;
finish_expr_stmt (elt_init);
current_stmt_tree ()->stmts_are_full_exprs_p = 0;
finish_expr_stmt (cp_build_unary_op (PREINCREMENT_EXPR, base, false,

View File

@ -0,0 +1,43 @@
// Test that we clean up the right number of array elements when
// a temporary destructor throws.
// { dg-do run }
#if __cplusplus > 201100L
#define THROWING noexcept(false)
#else
#define THROWING
#endif
extern "C" void abort ();
int b;
int d = -1;
struct A {
A() { }
A(const A&);
~A() THROWING {
if (b == d) throw b;
}
};
struct B {
B(const A& = A()) { ++b; }
B(const B&);
~B() { --b; }
};
void f()
{
b = 0;
try
{
B bs[3];
if (b != 3) abort ();
}
catch (int i) { }
if (b != 0) abort ();
}
int main()
{
for (d = 0; d <= 3; ++d)
f();
}