From 0bc060a49d7ac66f72e93cc080abcc9e2175f55c Mon Sep 17 00:00:00 2001 From: jason Date: Tue, 22 Jan 2008 04:53:33 +0000 Subject: [PATCH] PR c++/34196 * tree.h (TRY_CATCH_IS_CLEANUP): New macro. * cp/decl.c (wrap_cleanups_r): Set TRY_CATCH_IS_CLEANUP. * tree-eh.c (honor_protect_cleanup_actions): Strip TRY_CATCH_EXPR if it is set. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@131710 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 7 +++++++ gcc/cp/ChangeLog | 5 +++++ gcc/cp/decl.c | 18 ++++++++++++++++-- gcc/testsuite/g++.dg/eh/init-temp2.C | 31 +++++++++++++++++++++++++++++++ gcc/tree-eh.c | 17 +++++++++++++++++ gcc/tree.h | 9 ++++++++- 6 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/g++.dg/eh/init-temp2.C diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 873cdd4..d65e496 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,10 @@ +2008-01-21 Jason Merrill + + PR c++/34196 + * tree.h (TRY_CATCH_IS_CLEANUP): New macro. + * tree-eh.c (honor_protect_cleanup_actions): Strip TRY_CATCH_EXPR + if it is set. + 2008-01-21 DJ Delorie * doc/tm.texi (HARD_REGNO_NREGS): Note that this macro must not diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 768efc5..fa48f7c 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,8 @@ +2008-01-21 Jason Merrill + + PR c++/34196 + * decl.c (wrap_cleanups_r): Set TRY_CATCH_IS_CLEANUP. + 2008-01-21 Richard Guenther PR c++/34850 diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 4b2a55f..338e1ed 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -5165,7 +5165,10 @@ wrap_cleanups_r (tree *stmt_p, int *walk_subtrees, void *data) tree tcleanup = TARGET_EXPR_CLEANUP (*stmt_p); tcleanup = build2 (TRY_CATCH_EXPR, void_type_node, tcleanup, guard); - + /* Tell honor_protect_cleanup_actions to handle this as a separate + cleanup. */ + TRY_CATCH_IS_CLEANUP (tcleanup) = 1; + TARGET_EXPR_CLEANUP (*stmt_p) = tcleanup; } @@ -5175,7 +5178,18 @@ wrap_cleanups_r (tree *stmt_p, int *walk_subtrees, void *data) /* We're initializing a local variable which has a cleanup GUARD. If there are any temporaries used in the initializer INIT of this variable, we need to wrap their cleanups with TRY_CATCH_EXPR (, GUARD) so that the - variable will be cleaned up properly if one of them throws. */ + variable will be cleaned up properly if one of them throws. + + Unfortunately, there's no way to express this properly in terms of + nesting, as the regions for the temporaries overlap the region for the + variable itself; if there are two temporaries, the variable needs to be + the first thing destroyed if either of them throws. However, we only + want to run the variable's cleanup if it actually got constructed. So + we need to guard the temporary cleanups with the variable's cleanup if + 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. */ static void wrap_temporary_cleanups (tree init, tree guard) diff --git a/gcc/testsuite/g++.dg/eh/init-temp2.C b/gcc/testsuite/g++.dg/eh/init-temp2.C new file mode 100644 index 0000000..6a58dda --- /dev/null +++ b/gcc/testsuite/g++.dg/eh/init-temp2.C @@ -0,0 +1,31 @@ +// PR c++/34196 +// { dg-options "-O -Wuninitialized" } + +template class AutoPtr +{ + _Tp* _M_ptr; + +public: + explicit AutoPtr(_Tp* __p = 0) : _M_ptr(__p) {} + + ~AutoPtr() { delete _M_ptr; } +}; + +struct A +{ + A() { } + ~A() { throw 1.0; } +}; + +struct B +{ + virtual ~B(); +}; + +B* f (const A &s) { throw 1; } + +int +main() +{ + AutoPtr wt(f(A())); +} diff --git a/gcc/tree-eh.c b/gcc/tree-eh.c index 71d3d94..44d5a6d 100644 --- a/gcc/tree-eh.c +++ b/gcc/tree-eh.c @@ -840,6 +840,23 @@ honor_protect_cleanup_actions (struct leh_state *outer_state, if (this_state) finally = lower_try_finally_dup_block (finally, outer_state); + /* If this cleanup consists of a TRY_CATCH_EXPR with TRY_CATCH_IS_CLEANUP + set, the handler of the TRY_CATCH_EXPR is another cleanup which ought + to be in an enclosing scope, but needs to be implemented at this level + to avoid a nesting violation (see wrap_temporary_cleanups in + cp/decl.c). Since it's logically at an outer level, we should call + terminate before we get to it, so strip it away before adding the + MUST_NOT_THROW filter. */ + i = tsi_start (finally); + x = tsi_stmt (i); + if (protect_cleanup_actions + && TREE_CODE (x) == TRY_CATCH_EXPR + && TRY_CATCH_IS_CLEANUP (x)) + { + tsi_link_before (&i, TREE_OPERAND (x, 0), TSI_SAME_STMT); + tsi_delink (&i); + } + /* Resume execution after the exception. Adding this now lets lower_eh_filter not add unnecessary gotos, as it is clear that we never fallthru from this copy of the finally block. */ diff --git a/gcc/tree.h b/gcc/tree.h index 52cb973..de0b11d 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -447,6 +447,8 @@ struct gimple_stmt GTY(()) IDENTIFIER_NODE CLEANUP_EH_ONLY in TARGET_EXPR, WITH_CLEANUP_EXPR + TRY_CATCH_IS_CLEANUP in + TRY_CATCH_EXPR ASM_INPUT_P in ASM_EXPR EH_FILTER_MUST_NOT_THROW in EH_FILTER_EXPR @@ -1166,11 +1168,16 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int, should be cleaned up some day. */ #define TREE_STATIC(NODE) ((NODE)->base.static_flag) -/* In a TARGET_EXPR, WITH_CLEANUP_EXPR, means that the pertinent cleanup +/* In a TARGET_EXPR or WITH_CLEANUP_EXPR, means that the pertinent cleanup should only be executed if an exception is thrown, not on normal exit of its scope. */ #define CLEANUP_EH_ONLY(NODE) ((NODE)->base.static_flag) +/* In a TRY_CATCH_EXPR, means that the handler should be considered a + separate cleanup in honor_protect_cleanup_actions. */ +#define TRY_CATCH_IS_CLEANUP(NODE) \ + (TRY_CATCH_EXPR_CHECK (NODE)->base.static_flag) + /* Used as a temporary field on a CASE_LABEL_EXPR to indicate that the CASE_HIGH operand has been processed. */ #define CASE_HIGH_SEEN(NODE) \ -- 2.7.4