diff mbox series

[pushed] c++: initializer_list<string> and EH [PR114935]

Message ID 20240503200050.914147-1-jason@redhat.com
State New
Headers show
Series [pushed] c++: initializer_list<string> and EH [PR114935] | expand

Commit Message

Jason Merrill May 3, 2024, 8 p.m. UTC
Tested x86_64-pc-linux-gnu, applying to trunk and 14.

-- 8< --

When we initialize an array of a type with a non-trivial destructor, such as
the backing array for the initializer_list, we have a cleanup to destroy any
constructed elements if a later constructor throws.  When the array being
created is a variable, the end of that EH region naturally coincides with
the beginning of the EH region for the cleanup for the variable as a whole.

But if the array is a temporary, or a subobject of one, the array cleanup
region lasts for the rest of the full-expression, along with the normal
cleanup for the TARGET_EXPR.  As a result, when tata throws we clean it up
twice.  Before r14-1705 we avoided this by disabling the array cleanup in
split_nonconstant_init, but after that we don't go through
split_nonconstant_init, so let's handle it in cp_genericize_target_expr.

	PR c++/114935

gcc/cp/ChangeLog:

	* cp-gimplify.cc (cp_genericize_init): Add flags parm.
	(cp_genericize_init_expr): Pass nullptr.
	(cp_genericize_target_expr): Handle cleanup flags.
	* typeck2.cc (build_disable_temp_cleanup): Factor out of...
	(split_nonconstant_init): ...here.
	* cp-tree.h (build_disable_temp_cleanup): Declare.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp0x/initlist-eh1.C: New test.
---
 gcc/cp/cp-tree.h                          |  1 +
 gcc/cp/cp-gimplify.cc                     | 18 +++++++++---
 gcc/cp/typeck2.cc                         | 34 +++++++++++++----------
 gcc/testsuite/g++.dg/cpp0x/initlist-eh1.C | 25 +++++++++++++++++
 4 files changed, 60 insertions(+), 18 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/initlist-eh1.C


base-commit: c943d7b5c40f447b12431df9ad27a47dad95026d
diff mbox series

Patch

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 1ba7054f8bc..52d6841559c 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -8411,6 +8411,7 @@  extern int abstract_virtuals_error		(abstract_class_use, tree,
 						 tsubst_flags_t = tf_warning_or_error);
 
 extern tree store_init_value			(tree, tree, vec<tree, va_gc>**, int);
+extern tree build_disable_temp_cleanup		(tree);
 extern tree split_nonconstant_init		(tree, tree);
 extern bool check_narrowing			(tree, tree, tsubst_flags_t,
 						 bool = false);
diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
index ab5acd18c99..5cbdf0ea498 100644
--- a/gcc/cp/cp-gimplify.cc
+++ b/gcc/cp/cp-gimplify.cc
@@ -1063,11 +1063,11 @@  any_non_eliding_target_exprs (tree ctor)
    the result.  */
 
 static void
-cp_genericize_init (tree *replace, tree from, tree to)
+cp_genericize_init (tree *replace, tree from, tree to, vec<tree,va_gc>** flags)
 {
   tree init = NULL_TREE;
   if (TREE_CODE (from) == VEC_INIT_EXPR)
-    init = expand_vec_init_expr (to, from, tf_warning_or_error);
+    init = expand_vec_init_expr (to, from, tf_warning_or_error, flags);
   else if (TREE_CODE (from) == CONSTRUCTOR
 	   && TREE_SIDE_EFFECTS (from)
 	   && ((flag_exceptions
@@ -1101,7 +1101,7 @@  cp_genericize_init_expr (tree *stmt_p)
       /* Return gets confused if we clobber its INIT_EXPR this soon.  */
       && TREE_CODE (to) != RESULT_DECL)
     from = TARGET_EXPR_INITIAL (from);
-  cp_genericize_init (stmt_p, from, to);
+  cp_genericize_init (stmt_p, from, to, nullptr);
 }
 
 /* For a TARGET_EXPR, change the TARGET_EXPR_INITIAL.  We will need to use
@@ -1112,9 +1112,19 @@  cp_genericize_target_expr (tree *stmt_p)
 {
   iloc_sentinel ils = EXPR_LOCATION (*stmt_p);
   tree slot = TARGET_EXPR_SLOT (*stmt_p);
+  vec<tree, va_gc> *flags = make_tree_vector ();
   cp_genericize_init (&TARGET_EXPR_INITIAL (*stmt_p),
-		      TARGET_EXPR_INITIAL (*stmt_p), slot);
+		      TARGET_EXPR_INITIAL (*stmt_p), slot, &flags);
   gcc_assert (!DECL_INITIAL (slot));
+  for (tree f : flags)
+    {
+      /* Once initialization is complete TARGET_EXPR_CLEANUP becomes active, so
+	 disable any subobject cleanups.  */
+      tree d = build_disable_temp_cleanup (f);
+      auto &r = TARGET_EXPR_INITIAL (*stmt_p);
+      r = add_stmt_to_compound (r, d);
+    }
+  release_tree_vector (flags);
 }
 
 /* Similar to if (target_expr_needs_replace) replace_decl, but TP is the
diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc
index 2985bfdf9ec..06bad4d3303 100644
--- a/gcc/cp/typeck2.cc
+++ b/gcc/cp/typeck2.cc
@@ -466,6 +466,25 @@  maybe_push_temp_cleanup (tree sub, vec<tree,va_gc> **flags)
     }
 }
 
+/* F is something added to a cleanup flags vec by maybe_push_temp_cleanup or
+   build_vec_init.  Return the code to disable the cleanup it controls.  */
+
+tree
+build_disable_temp_cleanup (tree f)
+{
+  tree d = f;
+  tree i = boolean_false_node;
+  if (TREE_CODE (f) == TREE_LIST)
+    {
+      /* To disable a build_vec_init cleanup, set
+	 iterator = maxindex.  */
+      d = TREE_PURPOSE (f);
+      i = TREE_VALUE (f);
+      ggc_free (f);
+    }
+  return build2 (MODIFY_EXPR, TREE_TYPE (d), d, i);
+}
+
 /* The recursive part of split_nonconstant_init.  DEST is an lvalue
    expression to which INIT should be assigned.  INIT is a CONSTRUCTOR.
    Return true if the whole of the value was initialized by the
@@ -737,20 +756,7 @@  split_nonconstant_init (tree dest, tree init)
 	init = NULL_TREE;
 
       for (tree f : flags)
-	{
-	  /* See maybe_push_temp_cleanup.  */
-	  tree d = f;
-	  tree i = boolean_false_node;
-	  if (TREE_CODE (f) == TREE_LIST)
-	    {
-	      /* To disable a build_vec_init cleanup, set
-		 iterator = maxindex.  */
-	      d = TREE_PURPOSE (f);
-	      i = TREE_VALUE (f);
-	      ggc_free (f);
-	    }
-	  add_stmt (build2 (MODIFY_EXPR, TREE_TYPE (d), d, i));
-	}
+	add_stmt (build_disable_temp_cleanup (f));
       release_tree_vector (flags);
 
       code = pop_stmt_list (code);
diff --git a/gcc/testsuite/g++.dg/cpp0x/initlist-eh1.C b/gcc/testsuite/g++.dg/cpp0x/initlist-eh1.C
new file mode 100644
index 00000000000..93baf1c2abc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/initlist-eh1.C
@@ -0,0 +1,25 @@ 
+// { dg-do run { target c++11 } }
+
+#include <initializer_list>
+
+int as;
+struct A {
+  A(const char *) { ++as; }
+  A(const A&) { ++as; }
+  ~A() { --as; }
+};
+
+void __attribute__((noipa))
+tata(std::initializer_list<A> init)
+{
+  throw 1;
+}
+
+int
+main()
+{
+  try { tata({ "foo","bar" }); }
+  catch (...) { }
+
+  if (as != 0) __builtin_abort ();
+}