diff mbox series

[pushed] c++: fix array cleanup with throwing temp dtor

Message ID 20220102175450.2293820-1-jason@redhat.com
State New
Headers show
Series [pushed] c++: fix array cleanup with throwing temp dtor | expand

Commit Message

Jason Merrill Jan. 2, 2022, 5:54 p.m. UTC
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.

Tested x86_64-pc-linux-gnu, applying to trunk.

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


base-commit: 62eb5308fe6c46f7eded3c9e06c53491515a6e63
prerequisite-patch-id: ecfffb22c1ee7e449270885df0aaa6d5fc9d291f
diff mbox series

Patch

diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 2a4512e462a..5a5c1257902 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 00000000000..d4d6c91cde7
--- /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();
+}