From 4c6afbbd48f0c40ddf949bc403d9bd5f5e14204f Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Wed, 5 Jan 2022 11:18:25 -0500 Subject: [PATCH] c++: clean up ref-extended temp on throwing dtor [PR53868] We have wrap_temporary_cleanups to handle the EH region nesting problems between cleanups for complete variables and cleanups for temporaries used in their construction, but we weren't calling it for temporaries extended from binding to a reference. We still don't want this for array cleanups (since my PR94041 fix), so I move that exception from initialize_local_var to wrap_temporary_cleanups. PR c++/53868 gcc/cp/ChangeLog: * decl.c (cp_finish_decl): Use wrap_temporary_cleanups for cleanups from set_up_extended_ref_temp. (wrap_temporary_cleanups): Ignore array cleanups. (initialize_local_var): Don't check for array here. * cp-tree.h (BIND_EXPR_VEC_DTOR): New. * init.c (build_vec_delete_1): Set it. gcc/testsuite/ChangeLog: * g++.dg/eh/ref-temp1.C: New test. * g++.dg/eh/ref-temp2.C: New test. --- gcc/cp/cp-tree.h | 5 ++++ gcc/cp/decl.c | 25 +++++++++++++++--- gcc/cp/init.c | 1 + gcc/testsuite/g++.dg/eh/ref-temp1.C | 51 +++++++++++++++++++++++++++++++++++++ gcc/testsuite/g++.dg/eh/ref-temp2.C | 15 +++++++++++ 5 files changed, 93 insertions(+), 4 deletions(-) create mode 100644 gcc/testsuite/g++.dg/eh/ref-temp1.C create mode 100644 gcc/testsuite/g++.dg/eh/ref-temp2.C diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 56e6d66..e204182da 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -465,6 +465,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX]; OVL_USING_P (in OVERLOAD) IMPLICIT_CONV_EXPR_NONTYPE_ARG (in IMPLICIT_CONV_EXPR) BASELINK_FUNCTIONS_MAYBE_INCOMPLETE_P (in BASELINK) + BIND_EXPR_VEC_DTOR (in BIND_EXPR) 2: IDENTIFIER_KIND_BIT_2 (in IDENTIFIER_NODE) ICS_THIS_FLAG (in _CONV) DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (in VAR_DECL) @@ -712,6 +713,10 @@ typedef struct ptrmem_cst * ptrmem_cst_t; #define BIND_EXPR_TRY_BLOCK(NODE) \ TREE_LANG_FLAG_0 (BIND_EXPR_CHECK (NODE)) +/* This BIND_EXPR is from build_vec_delete_1. */ +#define BIND_EXPR_VEC_DTOR(NODE) \ + TREE_LANG_FLAG_1 (BIND_EXPR_CHECK (NODE)) + /* Used to mark the block around the member initializers and cleanups. */ #define BIND_EXPR_BODY_BLOCK(NODE) \ TREE_LANG_FLAG_3 (BIND_EXPR_CHECK (NODE)) diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index b16a4f9..5fe341e 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -7451,11 +7451,24 @@ wrap_cleanups_r (tree *stmt_p, int *walk_subtrees, void *data) 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. */ + exceptional path. + + Another approach could be to make the variable cleanup region enclose + initialization, but depend on a flag to indicate that the variable is + initialized; that's effectively what we do for arrays. But the current + approach works fine for non-arrays, and has no code overhead in the usual + case where the temporary destructors are noexcept. */ static void wrap_temporary_cleanups (tree init, tree guard) { + if (TREE_CODE (guard) == BIND_EXPR) + { + /* An array cleanup region already encloses any temporary cleanups, + don't wrap it around them again. */ + gcc_checking_assert (BIND_EXPR_VEC_DTOR (guard)); + return; + } cp_walk_tree_without_duplicates (&init, wrap_cleanups_r, (void *)guard); } @@ -7518,8 +7531,8 @@ initialize_local_var (tree decl, tree init) /* If we're only initializing a single object, guard the destructors of any temporaries used in its initializer with - its destructor. But arrays are handled in build_vec_init. */ - if (cleanup && TREE_CODE (type) != ARRAY_TYPE) + its destructor. */ + if (cleanup) wrap_temporary_cleanups (init, cleanup); gcc_assert (building_stmt_list_p ()); @@ -8367,7 +8380,11 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p, if (cleanups) { for (tree t : *cleanups) - push_cleanup (decl, t, false); + { + push_cleanup (decl, t, false); + /* As in initialize_local_var. */ + wrap_temporary_cleanups (init, t); + } release_tree_vector (cleanups); } diff --git a/gcc/cp/init.c b/gcc/cp/init.c index df63e61..bfe4ad4 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -4092,6 +4092,7 @@ build_vec_delete_1 (location_t loc, tree base, tree maxindex, tree type, tbase_init = build_stmt (loc, DECL_EXPR, tbase); controller = build3 (BIND_EXPR, void_type_node, tbase, NULL_TREE, NULL_TREE); TREE_SIDE_EFFECTS (controller) = 1; + BIND_EXPR_VEC_DTOR (controller) = true; body = build1 (EXIT_EXPR, void_type_node, build2 (EQ_EXPR, boolean_type_node, tbase, diff --git a/gcc/testsuite/g++.dg/eh/ref-temp1.C b/gcc/testsuite/g++.dg/eh/ref-temp1.C new file mode 100644 index 0000000..2df1a49 --- /dev/null +++ b/gcc/testsuite/g++.dg/eh/ref-temp1.C @@ -0,0 +1,51 @@ +// PR c++/53868 +// { dg-do run { target c++11 } } + +#if __cplusplus > 201100L +#define THROWING noexcept(false) +#else +#define THROWING +#endif + +extern "C" int printf(const char *, ...); +extern "C" void abort(); + +struct SubobjectInA { + SubobjectInA(); + ~SubobjectInA(); +}; + +int a; +struct A : SubobjectInA { + A() = delete; + A(const A &) = delete; + A(A &&) = delete; + A(int); + ~A(); +}; + +#ifdef DEBUG +#define TRACE_FUNC( ... ) \ +{ printf("%s\n", __PRETTY_FUNCTION__); __VA_ARGS__ } +#else +#define TRACE_FUNC( ... ) \ +{ __VA_ARGS__ } +#endif + +struct Q { + Q() : q(0) TRACE_FUNC() + ~Q() THROWING; + int q; +}; + +int main() { + try { const A &a = Q().q; } + catch (...) { if (!a) return 0; } + abort(); +} + +SubobjectInA::SubobjectInA() TRACE_FUNC() +SubobjectInA::~SubobjectInA() TRACE_FUNC() +A::A(int) TRACE_FUNC(++a;) +A::~A() TRACE_FUNC(--a;) +Q::~Q() THROWING TRACE_FUNC( throw 0; ) diff --git a/gcc/testsuite/g++.dg/eh/ref-temp2.C b/gcc/testsuite/g++.dg/eh/ref-temp2.C new file mode 100644 index 0000000..0c71896 --- /dev/null +++ b/gcc/testsuite/g++.dg/eh/ref-temp2.C @@ -0,0 +1,15 @@ +// { dg-do run { target c++11 } } + +struct B { B() {} ~B() noexcept(false) { throw 42; } }; +int a; +struct A { A() { ++a; }; A(B) { ++a; } ~A() { --a; } }; + +using Arr = A[3]; + +int main() +{ + try { + auto&& ref = Arr{B()}; + } catch (...) { } + return a; +} -- 2.7.4