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 <i...@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); + } + 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(); +} -- 2.39.2 (Apple Git-143)