diff options
author | Iain Sandoe <iain@sandoe.co.uk> | 2024-08-17 12:49:41 +0100 |
---|---|---|
committer | Iain Sandoe <iain@sandoe.co.uk> | 2024-08-24 19:53:30 +0100 |
commit | efc99ab2d5fdb7f2a942199b0e5b16e1e2bb8c27 (patch) | |
tree | f63ea0ace7936906b59e982c4ceb5b5cd07909a8 | |
parent | 624fb5b4407b4b9c23ca813a49b928d650d52480 (diff) | |
download | gcc-efc99ab2d5fdb7f2a942199b0e5b16e1e2bb8c27.zip gcc-efc99ab2d5fdb7f2a942199b0e5b16e1e2bb8c27.tar.gz gcc-efc99ab2d5fdb7f2a942199b0e5b16e1e2bb8c27.tar.bz2 |
c++, coroutines: Fix handling of early exceptions [PR113773].
The responsibility for destroying part of the frame content (promise,
arg copies and the frame itself) transitions from the ramp to the
body of the coroutine once we reach the await_resume () for the
initial suspend.
We added the variable that flags the transition, but failed to act on
it. This corrects that so that the ramp only tries to run DTORs for
objects when an exception occurs before the initial suspend await
resume has started.
PR c++/113773
gcc/cp/ChangeLog:
* coroutines.cc
(cp_coroutine_transform::build_ramp_function): Only cleanup the
frame state on exceptions that occur before the initial await
resume has begun.
gcc/testsuite/ChangeLog:
* g++.dg/coroutines/torture/pr113773.C: New test.
Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
-rw-r--r-- | gcc/cp/coroutines.cc | 39 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/coroutines/torture/pr113773.C | 66 |
2 files changed, 92 insertions, 13 deletions
diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc index 77dce83..cbe99ac 100644 --- a/gcc/cp/coroutines.cc +++ b/gcc/cp/coroutines.cc @@ -5119,12 +5119,22 @@ cp_coroutine_transform::build_ramp_function () finish_if_stmt_cond (coro_gro_live, gro_d_if); finish_expr_stmt (gro_ret_dtor); finish_then_clause (gro_d_if); - tree gro_d_if_scope = IF_SCOPE (gro_d_if); - IF_SCOPE (gro_d_if) = NULL; - gro_d_if = do_poplevel (gro_d_if_scope); - add_stmt (gro_d_if); + finish_if_stmt (gro_d_if); } + /* Before initial resume is called, the responsibility for cleanup on + exception falls to the ramp. After that, the coroutine body code + should do the cleanup. */ + tree iarc_m = lookup_member (frame_type, coro_frame_i_a_r_c_id, + 1, 0, tf_warning_or_error); + tree iarc_x + = build_class_member_access_expr (deref_fp, iarc_m, NULL_TREE, + /*preserve_reference*/false, + tf_warning_or_error); + tree not_iarc + = build1_loc (loc, TRUTH_NOT_EXPR, boolean_type_node, iarc_x); + tree cleanup_if = begin_if_stmt (); + finish_if_stmt_cond (not_iarc, cleanup_if); /* If the promise is live, then run its dtor if that's available. */ if (promise_dtor && promise_dtor != error_mark_node) { @@ -5132,10 +5142,7 @@ cp_coroutine_transform::build_ramp_function () finish_if_stmt_cond (coro_promise_live, promise_d_if); finish_expr_stmt (promise_dtor); finish_then_clause (promise_d_if); - tree promise_d_if_scope = IF_SCOPE (promise_d_if); - IF_SCOPE (promise_d_if) = NULL; - promise_d_if = do_poplevel (promise_d_if_scope); - add_stmt (promise_d_if); + finish_if_stmt (promise_d_if); } /* Clean up any frame copies of parms with non-trivial dtors. @@ -5159,15 +5166,21 @@ cp_coroutine_transform::build_ramp_function () finish_if_stmt_cond (parm_i->guard_var, dtor_if); finish_expr_stmt (parm_i->fr_copy_dtor); finish_then_clause (dtor_if); - tree parm_d_if_scope = IF_SCOPE (dtor_if); - IF_SCOPE (dtor_if) = NULL; - dtor_if = do_poplevel (parm_d_if_scope); - add_stmt (dtor_if); + finish_if_stmt (dtor_if); } } - /* We always expect to delete the frame. */ + /* No delete the frame if required. */ + tree fnf_if = begin_if_stmt (); + finish_if_stmt_cond (fnf_x, fnf_if); finish_expr_stmt (delete_frame_call); + finish_then_clause (fnf_if); + finish_if_stmt (fnf_if); + + /* Finished cleanups conditional on "initial resume is not called". */ + finish_then_clause (cleanup_if); + finish_if_stmt (cleanup_if); + tree rethrow = build_throw (loc, NULL_TREE, tf_warning_or_error); suppress_warning (rethrow); finish_expr_stmt (rethrow); diff --git a/gcc/testsuite/g++.dg/coroutines/torture/pr113773.C b/gcc/testsuite/g++.dg/coroutines/torture/pr113773.C new file mode 100644 index 0000000..b048b0d --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/torture/pr113773.C @@ -0,0 +1,66 @@ +// { dg-do run } +#include <coroutine> +#ifdef OUTPUT +#include <iostream> +#endif + +struct result { + operator int() { + throw 42; + } +}; + +static int p_dtor_count = 0; +class promise { +public: + result get_return_object() { return {}; } + std::suspend_never initial_suspend() { +#ifdef OUTPUT + std::cout << "initial suspend" << std::endl; +#endif + return {}; + } + void unhandled_exception() { +#ifdef OUTPUT + std::cout << "unhandled exception" << std::endl; +#endif + } + std::suspend_never final_suspend() noexcept { +#ifdef OUTPUT + std::cout << "final suspend" << std::endl; +#endif + return {}; + } + void return_void() {} + ~promise() { + p_dtor_count++; +#ifdef OUTPUT + std::cout << "~promise()" << std::endl; +#endif + } +}; + +template <class... Args> +struct std::coroutine_traits<int, Args...> { + using promise_type = promise; +}; + +int f() { + co_return; +} + +int main() { + try { + f(); + } + catch (int i) { + if (i != 42) + __builtin_abort (); +#ifdef OUTPUT + std::cout << "caught 42" << std::endl; +#endif + } + if (p_dtor_count != 1) + __builtin_abort (); + return 0; +} |