diff mbox series

[pushed,08/11] c++: clean up ref-extended temp on throwing dtor [PR53868]

Message ID 20220107002156.2992278-8-jason@redhat.com
State New
Headers show
Series [pushed,01/11] c++: don't preevaluate new-initializer | expand

Commit Message

Jason Merrill Jan. 7, 2022, 12:21 a.m. UTC
We have wrap_temporary_cleanups to handle the EH region nesting problems
between cleanups for complete variables and cleanups for temporaries used in
their construction, but we weren't calling it for temporaries extended from
binding to a reference.

We still don't want this for array cleanups (since my PR94041 fix), so I
move that exception from initialize_local_var to wrap_temporary_cleanups.

	PR c++/53868

gcc/cp/ChangeLog:

	* decl.c (cp_finish_decl): Use wrap_temporary_cleanups for
	cleanups from set_up_extended_ref_temp.
	(wrap_temporary_cleanups): Ignore array cleanups.
	(initialize_local_var): Don't check for array here.
	* cp-tree.h (BIND_EXPR_VEC_DTOR): New.
	* init.c (build_vec_delete_1): Set it.

gcc/testsuite/ChangeLog:

	* g++.dg/eh/ref-temp1.C: New test.
	* g++.dg/eh/ref-temp2.C: New test.
---
 gcc/cp/cp-tree.h                    |  5 +++
 gcc/cp/decl.c                       | 25 +++++++++++---
 gcc/cp/init.c                       |  1 +
 gcc/testsuite/g++.dg/eh/ref-temp1.C | 51 +++++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/eh/ref-temp2.C | 15 +++++++++
 5 files changed, 93 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/eh/ref-temp1.C
 create mode 100644 gcc/testsuite/g++.dg/eh/ref-temp2.C
diff mbox series

Patch

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 56e6d661537..e204182da97 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -465,6 +465,7 @@  extern GTY(()) tree cp_global_trees[CPTI_MAX];
       OVL_USING_P (in OVERLOAD)
       IMPLICIT_CONV_EXPR_NONTYPE_ARG (in IMPLICIT_CONV_EXPR)
       BASELINK_FUNCTIONS_MAYBE_INCOMPLETE_P (in BASELINK)
+      BIND_EXPR_VEC_DTOR (in BIND_EXPR)
    2: IDENTIFIER_KIND_BIT_2 (in IDENTIFIER_NODE)
       ICS_THIS_FLAG (in _CONV)
       DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (in VAR_DECL)
@@ -712,6 +713,10 @@  typedef struct ptrmem_cst * ptrmem_cst_t;
 #define BIND_EXPR_TRY_BLOCK(NODE) \
   TREE_LANG_FLAG_0 (BIND_EXPR_CHECK (NODE))
 
+/* This BIND_EXPR is from build_vec_delete_1.  */
+#define BIND_EXPR_VEC_DTOR(NODE) \
+  TREE_LANG_FLAG_1 (BIND_EXPR_CHECK (NODE))
+
 /* Used to mark the block around the member initializers and cleanups.  */
 #define BIND_EXPR_BODY_BLOCK(NODE) \
   TREE_LANG_FLAG_3 (BIND_EXPR_CHECK (NODE))
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index b16a4f9ed34..5fe341e0b75 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -7451,11 +7451,24 @@  wrap_cleanups_r (tree *stmt_p, int *walk_subtrees, void *data)
    they are run on the normal path, but not if they are run on the
    exceptional path.  We implement this by telling
    honor_protect_cleanup_actions to strip the variable cleanup from the
-   exceptional path.  */
+   exceptional path.
+
+   Another approach could be to make the variable cleanup region enclose
+   initialization, but depend on a flag to indicate that the variable is
+   initialized; that's effectively what we do for arrays.  But the current
+   approach works fine for non-arrays, and has no code overhead in the usual
+   case where the temporary destructors are noexcept.  */
 
 static void
 wrap_temporary_cleanups (tree init, tree guard)
 {
+  if (TREE_CODE (guard) == BIND_EXPR)
+    {
+      /* An array cleanup region already encloses any temporary cleanups,
+	 don't wrap it around them again.  */
+      gcc_checking_assert (BIND_EXPR_VEC_DTOR (guard));
+      return;
+    }
   cp_walk_tree_without_duplicates (&init, wrap_cleanups_r, (void *)guard);
 }
 
@@ -7518,8 +7531,8 @@  initialize_local_var (tree decl, tree init)
 
 	  /* If we're only initializing a single object, guard the
 	     destructors of any temporaries used in its initializer with
-	     its destructor.  But arrays are handled in build_vec_init.  */
-	  if (cleanup && TREE_CODE (type) != ARRAY_TYPE)
+	     its destructor.  */
+	  if (cleanup)
 	    wrap_temporary_cleanups (init, cleanup);
 
 	  gcc_assert (building_stmt_list_p ());
@@ -8367,7 +8380,11 @@  cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
   if (cleanups)
     {
       for (tree t : *cleanups)
-	push_cleanup (decl, t, false);
+	{
+	  push_cleanup (decl, t, false);
+	  /* As in initialize_local_var.  */
+	  wrap_temporary_cleanups (init, t);
+	}
       release_tree_vector (cleanups);
     }
 
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index df63e618394..bfe4ad464bf 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -4092,6 +4092,7 @@  build_vec_delete_1 (location_t loc, tree base, tree maxindex, tree type,
   tbase_init = build_stmt (loc, DECL_EXPR, tbase);
   controller = build3 (BIND_EXPR, void_type_node, tbase, NULL_TREE, NULL_TREE);
   TREE_SIDE_EFFECTS (controller) = 1;
+  BIND_EXPR_VEC_DTOR (controller) = true;
 
   body = build1 (EXIT_EXPR, void_type_node,
 		 build2 (EQ_EXPR, boolean_type_node, tbase,
diff --git a/gcc/testsuite/g++.dg/eh/ref-temp1.C b/gcc/testsuite/g++.dg/eh/ref-temp1.C
new file mode 100644
index 00000000000..2df1a4937b7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/eh/ref-temp1.C
@@ -0,0 +1,51 @@ 
+// PR c++/53868
+// { dg-do run { target c++11 } }
+
+#if __cplusplus > 201100L
+#define THROWING noexcept(false)
+#else
+#define THROWING
+#endif
+
+extern "C" int printf(const char *, ...);
+extern "C" void abort();
+
+struct SubobjectInA {
+   SubobjectInA();
+   ~SubobjectInA();
+};
+
+int a;
+struct A : SubobjectInA {
+   A() = delete;
+   A(const A &) = delete;
+  A(A &&) = delete;
+   A(int);
+   ~A();
+};
+
+#ifdef DEBUG
+#define TRACE_FUNC( ... ) \
+{   printf("%s\n", __PRETTY_FUNCTION__); __VA_ARGS__   }
+#else
+#define TRACE_FUNC( ... ) \
+{   __VA_ARGS__   }
+#endif
+
+struct Q {
+   Q() : q(0)  TRACE_FUNC()
+   ~Q() THROWING;
+   int q;
+};
+
+int main() {
+   try { const A &a = Q().q; }
+   catch (...) { if (!a) return 0; }
+   abort();
+}
+
+SubobjectInA::SubobjectInA()  TRACE_FUNC()
+SubobjectInA::~SubobjectInA()  TRACE_FUNC()
+A::A(int)  TRACE_FUNC(++a;)
+A::~A()  TRACE_FUNC(--a;)
+Q::~Q() THROWING TRACE_FUNC( throw 0; )
diff --git a/gcc/testsuite/g++.dg/eh/ref-temp2.C b/gcc/testsuite/g++.dg/eh/ref-temp2.C
new file mode 100644
index 00000000000..0c718962d49
--- /dev/null
+++ b/gcc/testsuite/g++.dg/eh/ref-temp2.C
@@ -0,0 +1,15 @@ 
+// { dg-do run { target c++11 } }
+
+struct B { B() {} ~B() noexcept(false) { throw 42; } };
+int a;
+struct A { A() { ++a; }; A(B) { ++a; } ~A() { --a; } };
+
+using Arr = A[3];
+
+int main()
+{
+  try {
+    auto&& ref = Arr{B()};
+  } catch (...) { }
+  return a;
+}