aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2020-01-19 09:14:54 -0500
committerJason Merrill <jason@redhat.com>2020-01-19 13:56:22 -0500
commitbcfc2227c556f2801a657ce3007374732baa8333 (patch)
treedc5c76be5ccee189500336763b3b157b7942ac47 /gcc
parent303484a73541ea7f41dff0238157924e49c255ff (diff)
downloadgcc-bcfc2227c556f2801a657ce3007374732baa8333.zip
gcc-bcfc2227c556f2801a657ce3007374732baa8333.tar.gz
gcc-bcfc2227c556f2801a657ce3007374732baa8333.tar.bz2
PR c++/33799 - destroy return value, take 2.
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.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/cp/ChangeLog10
-rw-r--r--gcc/cp/cp-tree.h11
-rw-r--r--gcc/cp/decl.c3
-rw-r--r--gcc/cp/except.c72
-rw-r--r--gcc/cp/parser.c4
-rw-r--r--gcc/cp/typeck.c3
-rw-r--r--gcc/testsuite/g++.dg/eh/return1.C20
7 files changed, 122 insertions, 1 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 98a4d0f..ef80482 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -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>
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 3d76096..890d5a2 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -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);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index e58fecc..28a7902 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -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;
}
diff --git a/gcc/cp/except.c b/gcc/cp/except.c
index 55b4b6a..0b40234 100644
--- a/gcc/cp/except.c
+++ b/gcc/cp/except.c
@@ -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"
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 98c1beb..caafbef 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -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 `}'. */
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 8955442..5964c34 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -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;
}
diff --git a/gcc/testsuite/g++.dg/eh/return1.C b/gcc/testsuite/g++.dg/eh/return1.C
index ca0b804..5ef2f1de 100644
--- a/gcc/testsuite/g++.dg/eh/return1.C
+++ b/gcc/testsuite/g++.dg/eh/return1.C
@@ -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;
}