Message ID | 83199df1749de7a7a63a3fdf219ae0d9db59896d.1724267239.git.iain@sandoe.co.uk |
---|---|
State | New |
Headers | show |
Series | c++, coroutines: Patch set for ramp function fixes. | expand |
On 8/21/24 3:10 PM, Iain Sandoe wrote: > In the case that the initial awaiter returns an object, the initial await > can be a target expression and we need to look at its initializer to cast > the await_resume() to void and to wrap in a compoun expression that sets > the initial_await_resume_called flag. > > PR c++/110635 > > gcc/cp/ChangeLog: > > * coroutines.cc > (cp_coroutine_transform::wrap_original_function_body): Look through > initial await target expressions to find the actual co_await_expr > that we need to update. > > gcc/testsuite/ChangeLog: > > * g++.dg/coroutines/pr110635.C: New test. > > Signed-off-by: Iain Sandoe <iain@sandoe.co.uk> > --- > gcc/cp/coroutines.cc | 10 ++- > gcc/testsuite/g++.dg/coroutines/pr110635.C | 72 ++++++++++++++++++++++ > 2 files changed, 81 insertions(+), 1 deletion(-) > create mode 100644 gcc/testsuite/g++.dg/coroutines/pr110635.C > > diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc > index 1039d2f8515..fcd6d7f0a7d 100644 > --- a/gcc/cp/coroutines.cc > +++ b/gcc/cp/coroutines.cc > @@ -4287,7 +4287,15 @@ cp_coroutine_transform::wrap_original_function_body () > a reference type, look past the indirection. */ > if (INDIRECT_REF_P (initial_await)) > initial_await = TREE_OPERAND (initial_await, 0); > - tree vec = TREE_OPERAND (initial_await, 3); > + /* In the case that the initial_await returns a target expression > + we might need to look through that to update the await expr. */ > + tree iaw = initial_await; > + if (TREE_CODE (iaw) == TARGET_EXPR) > + { > + iaw = TARGET_EXPR_INITIAL (iaw); > + gcc_checking_assert (TREE_CODE (iaw) == CO_AWAIT_EXPR); > + } How about moving the assert out of the if? OK with that change. > + tree vec = TREE_OPERAND (iaw, 3); > tree aw_r = TREE_VEC_ELT (vec, 2); > aw_r = convert_to_void (aw_r, ICV_STATEMENT, tf_warning_or_error); > tree update = build2 (MODIFY_EXPR, boolean_type_node, i_a_r_c, > diff --git a/gcc/testsuite/g++.dg/coroutines/pr110635.C b/gcc/testsuite/g++.dg/coroutines/pr110635.C > new file mode 100644 > index 00000000000..ea4e0e853eb > --- /dev/null > +++ b/gcc/testsuite/g++.dg/coroutines/pr110635.C > @@ -0,0 +1,72 @@ > + > +#define CASE 0 > +#include <coroutine> > +#include <iostream> > + > +struct Coroutine { > + > + struct promise_type; > + > + using handler_type = std::coroutine_handle<promise_type>; > + > + struct initial_suspend_awaiter { > + > + bool await_ready() noexcept { > + std::cout << "await_ready" << std::endl; > + return false; > + } > + > + void await_suspend(handler_type h) noexcept { > + std::cout << "await_suspend" << std::endl; > + } > + > +#if CASE == 0 > + struct await_resume_return_object { > + await_resume_return_object() noexcept { > + std::cout << "await_resume_return_object" << std::endl; > + } > + > + ~await_resume_return_object() noexcept { > + std::cout << "~await_resume_return_object" << std::endl; > + } > + }; > +#elif CASE == 1 > + using await_resume_return_object = struct{}; > +#elif CASE == 2 > + using await_resume_return_object = int; > +#else > + using await_resume_return_object = void; > +#endif > + await_resume_return_object await_resume() noexcept { > + std::cout << "await_resume" << std::endl; > +#if CASE == 0 || CASE == 1 || CASE == 2 > + return {}; > +#endif > + } > + > + initial_suspend_awaiter() noexcept { > + std::cout << "initial_suspend_awaiter" << std::endl; > + } > + > + ~initial_suspend_awaiter() noexcept { > + std::cout << "~initial_suspend_awaiter" << std::endl; > + } > + }; > + > + struct promise_type { > + void return_void() noexcept {} > + void unhandled_exception() noexcept { std::terminate();} > + initial_suspend_awaiter initial_suspend() noexcept { return {}; } > + std::suspend_never final_suspend() noexcept { return {}; } > + Coroutine get_return_object() { > + return Coroutine{handler_type::from_promise(*this)}; > + } > + }; > + > + handler_type handler; > +}; > + > +int main() { > + auto coro = []()->Coroutine { co_return; }(); > + coro.handler.resume(); > +}
diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc index 1039d2f8515..fcd6d7f0a7d 100644 --- a/gcc/cp/coroutines.cc +++ b/gcc/cp/coroutines.cc @@ -4287,7 +4287,15 @@ cp_coroutine_transform::wrap_original_function_body () a reference type, look past the indirection. */ if (INDIRECT_REF_P (initial_await)) initial_await = TREE_OPERAND (initial_await, 0); - tree vec = TREE_OPERAND (initial_await, 3); + /* In the case that the initial_await returns a target expression + we might need to look through that to update the await expr. */ + tree iaw = initial_await; + if (TREE_CODE (iaw) == TARGET_EXPR) + { + iaw = TARGET_EXPR_INITIAL (iaw); + gcc_checking_assert (TREE_CODE (iaw) == CO_AWAIT_EXPR); + } + tree vec = TREE_OPERAND (iaw, 3); tree aw_r = TREE_VEC_ELT (vec, 2); aw_r = convert_to_void (aw_r, ICV_STATEMENT, tf_warning_or_error); tree update = build2 (MODIFY_EXPR, boolean_type_node, i_a_r_c, diff --git a/gcc/testsuite/g++.dg/coroutines/pr110635.C b/gcc/testsuite/g++.dg/coroutines/pr110635.C new file mode 100644 index 00000000000..ea4e0e853eb --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/pr110635.C @@ -0,0 +1,72 @@ + +#define CASE 0 +#include <coroutine> +#include <iostream> + +struct Coroutine { + + struct promise_type; + + using handler_type = std::coroutine_handle<promise_type>; + + struct initial_suspend_awaiter { + + bool await_ready() noexcept { + std::cout << "await_ready" << std::endl; + return false; + } + + void await_suspend(handler_type h) noexcept { + std::cout << "await_suspend" << std::endl; + } + +#if CASE == 0 + struct await_resume_return_object { + await_resume_return_object() noexcept { + std::cout << "await_resume_return_object" << std::endl; + } + + ~await_resume_return_object() noexcept { + std::cout << "~await_resume_return_object" << std::endl; + } + }; +#elif CASE == 1 + using await_resume_return_object = struct{}; +#elif CASE == 2 + using await_resume_return_object = int; +#else + using await_resume_return_object = void; +#endif + await_resume_return_object await_resume() noexcept { + std::cout << "await_resume" << std::endl; +#if CASE == 0 || CASE == 1 || CASE == 2 + return {}; +#endif + } + + initial_suspend_awaiter() noexcept { + std::cout << "initial_suspend_awaiter" << std::endl; + } + + ~initial_suspend_awaiter() noexcept { + std::cout << "~initial_suspend_awaiter" << std::endl; + } + }; + + struct promise_type { + void return_void() noexcept {} + void unhandled_exception() noexcept { std::terminate();} + initial_suspend_awaiter initial_suspend() noexcept { return {}; } + std::suspend_never final_suspend() noexcept { return {}; } + Coroutine get_return_object() { + return Coroutine{handler_type::from_promise(*this)}; + } + }; + + handler_type handler; +}; + +int main() { + auto coro = []()->Coroutine { co_return; }(); + coro.handler.resume(); +}
In the case that the initial awaiter returns an object, the initial await can be a target expression and we need to look at its initializer to cast the await_resume() to void and to wrap in a compoun expression that sets the initial_await_resume_called flag. PR c++/110635 gcc/cp/ChangeLog: * coroutines.cc (cp_coroutine_transform::wrap_original_function_body): Look through initial await target expressions to find the actual co_await_expr that we need to update. gcc/testsuite/ChangeLog: * g++.dg/coroutines/pr110635.C: New test. Signed-off-by: Iain Sandoe <iain@sandoe.co.uk> --- gcc/cp/coroutines.cc | 10 ++- gcc/testsuite/g++.dg/coroutines/pr110635.C | 72 ++++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/g++.dg/coroutines/pr110635.C