aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2022-01-01 13:07:59 -0500
committerJason Merrill <jason@redhat.com>2022-01-02 12:56:02 -0500
commitc743614e7016c5b2e5c5871cba23de68134d4950 (patch)
tree8d82978674919f385b5f34f00cc3b7b2a732342f /gcc
parent092e60f57adeccf98e876af6b7b5734337904812 (diff)
downloadgcc-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.c17
-rw-r--r--gcc/testsuite/g++.dg/eh/array2.C43
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();
+}