From c743614e7016c5b2e5c5871cba23de68134d4950 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Sat, 1 Jan 2022 13:07:59 -0500 Subject: [PATCH] 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. --- gcc/cp/init.c | 17 ++++++++++------ gcc/testsuite/g++.dg/eh/array2.C | 43 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 6 deletions(-) create mode 100644 gcc/testsuite/g++.dg/eh/array2.C diff --git a/gcc/cp/init.c b/gcc/cp/init.c index 2a4512e..5a5c125 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -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, diff --git a/gcc/testsuite/g++.dg/eh/array2.C b/gcc/testsuite/g++.dg/eh/array2.C new file mode 100644 index 0000000..d4d6c91 --- /dev/null +++ b/gcc/testsuite/g++.dg/eh/array2.C @@ -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(); +} -- 2.7.4