PR c++/33799 - destroy return value, take 2.
authorJason Merrill <jason@redhat.com>
Sun, 19 Jan 2020 14:14:54 +0000 (09:14 -0500)
committerJason Merrill <jason@redhat.com>
Sun, 19 Jan 2020 18:56:22 +0000 (13:56 -0500)
This patch differs from the reverted patch for 33799 in that it adds the
CLEANUP_STMT for the return value at the end of the function, and only if
we've seen a cleanup that might throw, so it should not affect most C++11
code.

* cp-tree.h (current_retval_sentinel): New macro.
(struct language_function): Add throwing_cleanup bitfield.
* decl.c (cxx_maybe_build_cleanup): Set it.
* except.c (maybe_set_retval_sentinel)
(maybe_splice_retval_cleanup): New functions.
* parser.c (cp_parser_compound_statement): Call
maybe_splice_retval_cleanup.
* typeck.c (check_return_expr): Call maybe_set_retval_sentinel.

gcc/cp/ChangeLog
gcc/cp/cp-tree.h
gcc/cp/decl.c
gcc/cp/except.c
gcc/cp/parser.c
gcc/cp/typeck.c
gcc/testsuite/g++.dg/eh/return1.C

index 98a4d0fa7dccc557349ddc899c2e7e2f2a976376..ef8048260a313525c3489f6b5b7b4cbc85f716bb 100644 (file)
@@ -1,5 +1,15 @@
 2020-01-19  Jason Merrill  <jason@redhat.com>
 
+       PR c++/33799 - destroy return value, take 2.
+       * cp-tree.h (current_retval_sentinel): New macro.
+       (struct language_function): Add throwing_cleanup bitfield.
+       * decl.c (cxx_maybe_build_cleanup): Set it.
+       * except.c (maybe_set_retval_sentinel)
+       (maybe_splice_retval_cleanup): New functions.
+       * parser.c (cp_parser_compound_statement): Call
+       maybe_splice_retval_cleanup.
+       * typeck.c (check_return_expr): Call maybe_set_retval_sentinel.
+
        * parser.c (cp_parser_lambda_body): Use cp_parser_function_body.
 
 2020-01-18  Jakub Jelinek  <jakub@redhat.com>
index 3d76096b04142f5773a97b0e4a779b98f1c744bb..890d5a2735090d30d283c50172aee0817a8d61e0 100644 (file)
@@ -1904,6 +1904,7 @@ struct GTY(()) language_function {
   BOOL_BITFIELD can_throw : 1;
 
   BOOL_BITFIELD invalid_constexpr : 1;
+  BOOL_BITFIELD throwing_cleanup : 1;
 
   hash_table<named_label_hash> *x_named_labels;
 
@@ -1954,6 +1955,13 @@ struct GTY(()) language_function {
 
 #define current_vtt_parm cp_function_chain->x_vtt_parm
 
+/* A boolean flag to control whether we need to clean up the return value if a
+   local destructor throws.  Only used in functions that return by value a
+   class with a destructor.  Which 'tors don't, so we can use the same
+   field as current_vtt_parm.  */
+
+#define current_retval_sentinel current_vtt_parm
+
 /* Set to 0 at beginning of a function definition, set to 1 if
    a return statement that specifies a return value is seen.  */
 
@@ -6686,6 +6694,9 @@ 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 tree maybe_set_retval_sentinel          (void);
+
 extern tree template_parms_to_args             (tree);
 extern tree template_parms_level_to_args       (tree);
 extern tree generic_targs_for                  (tree);
index e58fecc9de72eac0ee13fc77304c32f2f400cf89..28a79029d921e342056fafdbb748c9abf1ec8708 100644 (file)
@@ -17402,6 +17402,9 @@ cxx_maybe_build_cleanup (tree decl, tsubst_flags_t complain)
       && !mark_used (decl, complain) && !(complain & tf_error))
     return error_mark_node;
 
+  if (cleanup && cfun && !expr_noexcept_p (cleanup, tf_none))
+    cp_function_chain->throwing_cleanup = true;
+
   return cleanup;
 }
 
index 55b4b6af442ad2a3aeddb21f63383369e51192b6..0b40234e228bb065ecba4cd946a07390bb7f032a 100644 (file)
@@ -1325,4 +1325,76 @@ build_noexcept_spec (tree expr, tsubst_flags_t complain)
     }
 }
 
+/* If the current function has a cleanup that might throw, and the return value
+   has a non-trivial destructor, return a MODIFY_EXPR to set
+   current_retval_sentinel so that we know that the return value needs to be
+   destroyed on throw.  Otherwise, returns NULL_TREE.  */
+
+tree
+maybe_set_retval_sentinel ()
+{
+  if (processing_template_decl)
+    return NULL_TREE;
+  tree retval = DECL_RESULT (current_function_decl);
+  if (!TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (retval)))
+    return NULL_TREE;
+  if (!cp_function_chain->throwing_cleanup)
+    return NULL_TREE;
+
+  if (!current_retval_sentinel)
+    {
+      /* Just create the temporary now, maybe_splice_retval_cleanup
+        will do the rest.  */
+      current_retval_sentinel = create_temporary_var (boolean_type_node);
+      DECL_INITIAL (current_retval_sentinel) = boolean_false_node;
+      pushdecl_outermost_localscope (current_retval_sentinel);
+    }
+
+  return build2 (MODIFY_EXPR, boolean_type_node,
+                current_retval_sentinel, boolean_true_node);
+}
+
+/* COMPOUND_STMT is the STATEMENT_LIST for the current function body.  If
+   current_retval_sentinel was set in this function, wrap the body in a
+   CLEANUP_STMT to destroy the return value on throw.  */
+
+void
+maybe_splice_retval_cleanup (tree compound_stmt)
+{
+  /* If need_retval_cleanup set current_retval_sentinel, wrap the function body
+     in a CLEANUP_STMT to handle destroying the return value.  */
+  if (!DECL_CONSTRUCTOR_P (current_function_decl)
+      && !DECL_DESTRUCTOR_P (current_function_decl)
+      && current_retval_sentinel)
+    {
+      location_t loc = DECL_SOURCE_LOCATION (current_function_decl);
+
+      /* Add a DECL_EXPR for current_retval_sentinel.  */
+      tree_stmt_iterator iter = tsi_start (compound_stmt);
+      tree retval = DECL_RESULT (current_function_decl);
+      tree decl_expr = build_stmt (loc, DECL_EXPR, current_retval_sentinel);
+      tsi_link_before (&iter, decl_expr, TSI_SAME_STMT);
+
+      /* Skip past other decls, they can't contain a return.  */
+      while (TREE_CODE (tsi_stmt (iter)) == DECL_EXPR)
+       tsi_next (&iter);
+      gcc_assert (!tsi_end_p (iter));
+
+      /* Wrap the rest of the STATEMENT_LIST in a CLEANUP_STMT.  */
+      tree stmts = NULL_TREE;
+      while (!tsi_end_p (iter))
+       {
+         append_to_statement_list_force (tsi_stmt (iter), &stmts);
+         tsi_delink (&iter);
+       }
+      tree dtor = build_cleanup (retval);
+      tree cond = build3 (COND_EXPR, void_type_node, current_retval_sentinel,
+                         dtor, void_node);
+      tree cleanup = build_stmt (loc, CLEANUP_STMT,
+                                stmts, cond, retval);
+      CLEANUP_EH_ONLY (cleanup) = true;
+      append_to_statement_list_force (cleanup, &compound_stmt);
+    }
+}
+
 #include "gt-cp-except.h"
index 98c1beb400f247bef114e475df29f7aef4feab1d..caafbefda8e63013367ddad23942c06e25276f80 100644 (file)
@@ -11716,6 +11716,10 @@ cp_parser_compound_statement (cp_parser *parser, tree in_statement_expr,
     cp_parser_label_declaration (parser);
   /* Parse an (optional) statement-seq.  */
   cp_parser_statement_seq_opt (parser, in_statement_expr);
+
+  if (function_body)
+    maybe_splice_retval_cleanup (compound_stmt);
+
   /* Finish the compound-statement.  */
   finish_compound_stmt (compound_stmt);
   /* Consume the `}'.  */
index 8955442349f903632fc06f7f586a1f05c97b0cff..5964c34272e473d74a727aa207df516c694e8e9a 100644 (file)
@@ -10090,6 +10090,9 @@ check_return_expr (tree retval, bool *no_warning)
   if (retval && retval != result)
     retval = build2 (INIT_EXPR, TREE_TYPE (result), result, retval);
 
+  if (tree set = maybe_set_retval_sentinel ())
+    retval = build2 (COMPOUND_EXPR, void_type_node, retval, set);
+
   return retval;
 }
 
index ca0b8046e1b3967aed29a291e5ad90620e7cb4ef..5ef2f1dee8539340c4c1cbeddb4542044e3a3f12 100644 (file)
@@ -1,5 +1,5 @@
 // PR c++/33799
-// { dg-do run { xfail *-*-* } }
+// { dg-do run }
 
 extern "C" void abort();
 
@@ -30,11 +30,29 @@ X f()
   return X(false);
 }
 
+X g()
+{
+  return X(true),X(false);
+}
+
+void h()
+{
+#if __cplusplus >= 201103L
+  []{ return X(true),X(false); }();
+#endif
+}
+
 int main()
 {
   try { f(); }
   catch (...) {}
 
+  try { g(); }
+  catch (...) {}
+
+  try { h(); }
+  catch (...) {}
+
   if (c != d)
     throw;
 }