diff options
author | Jason Merrill <jason@redhat.com> | 2020-01-19 09:14:54 -0500 |
---|---|---|
committer | Jason Merrill <jason@redhat.com> | 2020-01-19 13:56:22 -0500 |
commit | bcfc2227c556f2801a657ce3007374732baa8333 (patch) | |
tree | dc5c76be5ccee189500336763b3b157b7942ac47 /gcc/cp/except.c | |
parent | 303484a73541ea7f41dff0238157924e49c255ff (diff) | |
download | gcc-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/cp/except.c')
-rw-r--r-- | gcc/cp/except.c | 72 |
1 files changed, 72 insertions, 0 deletions
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" |