[pushed] c++: fix throwing cleanup with label

Message ID 20230607013053.2770663-1-jason@redhat.com
State Committed
Commit 08cea4e56a094ff9cc7c65fdc9ce8c4d7aff64be
Headers
Series [pushed] c++: fix throwing cleanup with label |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Testing passed

Commit Message

Jason Merrill June 7, 2023, 1:30 a.m. UTC
  Tested x86_64-pc-linux-gnu, applying to trunk.

-- 8< --

While looking at PR92407 I noticed that the expectations of
maybe_splice_retval_cleanup weren't being met; an sk_cleanup level was
confusing its attempt to recognize the outer block of the function.  And
even if I fixed the detection, it failed to actually wrap the body of the
function because the STATEMENT_LIST it got only had the label, not anything
after it.  So I moved the call after poplevel does pop_stmt_list on all the
sk_cleanup levels.

	PR c++/33799

gcc/cp/ChangeLog:

	* except.cc (maybe_splice_retval_cleanup): Change
	recognition of function body and try scopes.
	* semantics.cc (do_poplevel): Call it after poplevel.
	(at_try_scope): New.
	* cp-tree.h (maybe_splice_retval_cleanup): Adjust.

gcc/testsuite/ChangeLog:

	* g++.dg/eh/return1.C: Add label cases.
---
 gcc/cp/cp-tree.h                  |  2 +-
 gcc/cp/except.cc                  | 25 ++++++++++++-------------
 gcc/cp/semantics.cc               | 16 +++++++++++++++-
 gcc/testsuite/g++.dg/eh/return1.C | 21 +++++++++++++++++++++
 4 files changed, 49 insertions(+), 15 deletions(-)


base-commit: 29c82c6ca929e0f5eccfe038dea71177d814c6b7
prerequisite-patch-id: aed53cdac161144c31cb1433282e1ad1d49d3770
prerequisite-patch-id: 1098cb4457a5eff90fa8176f9b0d8d2e9477596e
  

Patch

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 101da35d322..834fdd12ef8 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7047,7 +7047,7 @@  extern tree begin_eh_spec_block			(void);
 extern void finish_eh_spec_block		(tree, tree);
 extern tree build_eh_type_type			(tree);
 extern tree cp_protect_cleanup_actions		(void);
-extern void maybe_splice_retval_cleanup		(tree);
+extern void maybe_splice_retval_cleanup		(tree, bool);
 extern tree maybe_set_retval_sentinel		(void);
 
 extern tree template_parms_to_args		(tree);
diff --git a/gcc/cp/except.cc b/gcc/cp/except.cc
index b04eb00d220..28106dadf1e 100644
--- a/gcc/cp/except.cc
+++ b/gcc/cp/except.cc
@@ -1312,21 +1312,20 @@  maybe_set_retval_sentinel ()
    on throw.  */
 
 void
-maybe_splice_retval_cleanup (tree compound_stmt)
+maybe_splice_retval_cleanup (tree compound_stmt, bool is_try)
 {
-  /* If we need a cleanup for the return value, add it in at the same level as
-     pushdecl_outermost_localscope.  And also in try blocks.  */
-  const bool function_body
-    = (current_binding_level->level_chain
-       && current_binding_level->level_chain->kind == sk_function_parms
-      /* When we're processing a default argument, c_f_d may not have been
-	 set.  */
-       && current_function_decl);
+  if (!current_function_decl || !cfun
+      || DECL_CONSTRUCTOR_P (current_function_decl)
+      || DECL_DESTRUCTOR_P (current_function_decl)
+      || !current_retval_sentinel)
+    return;
 
-  if ((function_body || current_binding_level->kind == sk_try)
-      && !DECL_CONSTRUCTOR_P (current_function_decl)
-      && !DECL_DESTRUCTOR_P (current_function_decl)
-      && current_retval_sentinel)
+  /* if we need a cleanup for the return value, add it in at the same level as
+     pushdecl_outermost_localscope.  And also in try blocks.  */
+  cp_binding_level *b = current_binding_level;
+  const bool function_body = b->kind == sk_function_parms;
+
+  if (function_body || is_try)
     {
       location_t loc = DECL_SOURCE_LOCATION (current_function_decl);
       tree_stmt_iterator iter = tsi_start (compound_stmt);
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index c94ea090a99..a13c16f34a3 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -628,6 +628,17 @@  set_cleanup_locs (tree stmts, location_t loc)
       set_cleanup_locs (stmt, loc);
 }
 
+/* True iff the innermost block scope is a try block.  */
+
+static bool
+at_try_scope ()
+{
+  cp_binding_level *b = current_binding_level;
+  while (b && b->kind == sk_cleanup)
+    b = b->level_chain;
+  return b && b->kind == sk_try;
+}
+
 /* Finish a scope.  */
 
 tree
@@ -635,11 +646,14 @@  do_poplevel (tree stmt_list)
 {
   tree block = NULL;
 
-  maybe_splice_retval_cleanup (stmt_list);
+  bool was_try = at_try_scope ();
 
   if (stmts_are_full_exprs_p ())
     block = poplevel (kept_level_p (), 1, 0);
 
+  /* This needs to come after poplevel merges sk_cleanup statement_lists.  */
+  maybe_splice_retval_cleanup (stmt_list, was_try);
+
   stmt_list = pop_stmt_list (stmt_list);
 
   /* input_location is the last token of the scope, usually a }.  */
diff --git a/gcc/testsuite/g++.dg/eh/return1.C b/gcc/testsuite/g++.dg/eh/return1.C
index ac2225405da..e22d674ae9a 100644
--- a/gcc/testsuite/g++.dg/eh/return1.C
+++ b/gcc/testsuite/g++.dg/eh/return1.C
@@ -33,6 +33,13 @@  X f()
   return X(false);
 }
 
+X f2()
+{
+ foo:
+  X x(true);
+  return X(false);
+}
+
 X g()
 {
   return X(true),X(false);
@@ -54,6 +61,16 @@  X i()
   return X(false);
 }
 
+X i2()
+{
+  try {
+  foo:
+    X x(true);
+    return X(false);
+  } catch(...) {}
+  return X(false);
+}
+
 X j()
 {
   try {
@@ -84,6 +101,8 @@  int main()
   try { f(); }
   catch (...) {}
 
+  try { f2(); } catch (...) {}
+
   try { g(); }
   catch (...) {}
 
@@ -93,6 +112,8 @@  int main()
   try { i(); }
   catch (...) {}
 
+  try { i2(); } catch (...) {}
+
   try { j(); } catch (...) {}
 
   try { k<X>(); } catch (...) {}