diff options
author | Jason Merrill <jason@redhat.com> | 2022-01-01 13:07:59 -0500 |
---|---|---|
committer | Jason Merrill <jason@redhat.com> | 2022-01-02 12:56:02 -0500 |
commit | c743614e7016c5b2e5c5871cba23de68134d4950 (patch) | |
tree | 8d82978674919f385b5f34f00cc3b7b2a732342f /gcc | |
parent | 092e60f57adeccf98e876af6b7b5734337904812 (diff) | |
download | gcc-c743614e7016c5b2e5c5871cba23de68134d4950.zip gcc-c743614e7016c5b2e5c5871cba23de68134d4950.tar.gz gcc-c743614e7016c5b2e5c5871cba23de68134d4950.tar.bz2 |
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.
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/cp/init.c | 17 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/eh/array2.C | 43 |
2 files changed, 54 insertions, 6 deletions
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(); +} |