diff --git a/CoroutinesCodeGenerator.cpp b/CoroutinesCodeGenerator.cpp index d1a4b37b..b54b1e45 100644 --- a/CoroutinesCodeGenerator.cpp +++ b/CoroutinesCodeGenerator.cpp @@ -148,6 +148,11 @@ class CoroutineASTTransformer : public StmtVisitor if(not mSkip) { mBodyStmts.Add(child); + + if(const auto* coret = dyn_cast_or_null(child); + coret and (coret->getOperand() == nullptr)) { + mBodyStmts.Add(Goto(FINAL_SUSPEND_NAME)); + } } mSkip = false; diff --git a/tests/Issue536.expect b/tests/Issue536.expect index 14a3b505..af348b48 100644 --- a/tests/Issue536.expect +++ b/tests/Issue536.expect @@ -145,6 +145,7 @@ void __coroResume(__coroFrame * __f) __f->__suspend_37_14.await_resume(); /* co_return Issue536.cpp:38 */ __f->__promise.return_void(); + goto __final_suspend; /* co_return Issue536.cpp:37 */ __f->__promise.return_void()/* implicit */; goto __final_suspend; diff --git a/tests/Issue536_2.expect b/tests/Issue536_2.expect index 76044e97..a42df6b7 100644 --- a/tests/Issue536_2.expect +++ b/tests/Issue536_2.expect @@ -134,6 +134,7 @@ inline my_resumable coro(int x) const __f->__suspend_29_16.await_resume(); /* co_return Issue536_2.cpp:30 */ __f->__promise.return_void(); + goto __final_suspend; /* co_return Issue536_2.cpp:29 */ __f->__promise.return_void()/* implicit */; goto __final_suspend; diff --git a/tests/Issue726.cpp b/tests/Issue726.cpp new file mode 100644 index 00000000..08eaeabf --- /dev/null +++ b/tests/Issue726.cpp @@ -0,0 +1,43 @@ +// cmdline:-std=c++20 +// cmdlineinsights:-edu-show-coroutine-transformation + +#include +#include +#include +struct P { + std::suspend_always initial_suspend() + { + return {}; + } + void return_void() const noexcept + { + std::cout<<"return_void()\n"; + } + std::coroutine_handle<> get_return_object() + { + return std::coroutine_handle

::from_promise(*this); + }; + void unhandled_exception() { throw; } + std::suspend_never final_suspend() noexcept + { + return {}; + } +}; +struct R { + R( std::coroutine_handle<> d) noexcept + : data(d) + { + } + std::coroutine_handle<> data; + using promise_type = P; +}; +R funcA(){ + std::cout<<"funcA_1\n"; + co_return; + std::cout<<"funcA_2\n"; +} +int main() +{ + funcA().data.resume(); + return 0; +} diff --git a/tests/Issue726.expect b/tests/Issue726.expect new file mode 100644 index 00000000..e450f24f --- /dev/null +++ b/tests/Issue726.expect @@ -0,0 +1,159 @@ +/************************************************************************************* + * NOTE: The coroutine transformation you've enabled is a hand coded transformation! * + * Most of it is _not_ present in the AST. What you see is an approximation. * + *************************************************************************************/ +#include +#include +#include +struct P +{ + inline std::suspend_always initial_suspend() + { + return {}; + } + + inline void return_void() const noexcept + { + std::operator<<(std::cout, "return_void()\n"); + } + + inline std::coroutine_handle get_return_object() + { + return std::coroutine_handle

::from_promise(*this).operator std::coroutine_handle(); + } + + inline void unhandled_exception() + { + throw ; + } + + inline std::suspend_never final_suspend() noexcept + { + return {}; + } + + // inline constexpr P() noexcept = default; +}; + + +struct R +{ + inline R(std::coroutine_handle d) noexcept + : data{std::coroutine_handle(d)} + { + } + + std::coroutine_handle data; + using promise_type = P; +}; + + +struct __funcAFrame +{ + void (*resume_fn)(__funcAFrame *); + void (*destroy_fn)(__funcAFrame *); + std::__coroutine_traits_sfinae::promise_type __promise; + int __suspend_index; + bool __initial_await_suspend_called; + std::suspend_always __suspend_34_3; + std::suspend_never __suspend_34_3_1; +}; + +R funcA() +{ + /* Allocate the frame including the promise */ + /* Note: The actual parameter new is __builtin_coro_size */ + __funcAFrame * __f = reinterpret_cast<__funcAFrame *>(operator new(sizeof(__funcAFrame))); + __f->__suspend_index = 0; + __f->__initial_await_suspend_called = false; + + /* Construct the promise. */ + new (&__f->__promise)std::__coroutine_traits_sfinae::promise_type{}; + + /* Forward declare the resume and destroy function. */ + void __funcAResume(__funcAFrame * __f); + void __funcADestroy(__funcAFrame * __f); + + /* Assign the resume and destroy function pointers. */ + __f->resume_fn = &__funcAResume; + __f->destroy_fn = &__funcADestroy; + + /* Call the made up function with the coroutine body for initial suspend. + This function will be called subsequently by coroutine_handle<>::resume() + which calls __builtin_coro_resume(__handle_) */ + __funcAResume(__f); + + + std::coroutine_handle __coro_gro = __f->__promise.get_return_object(); + return R(std::coroutine_handle(static_cast &&>(__coro_gro))); +} + +/* This function invoked by coroutine_handle<>::resume() */ +void __funcAResume(__funcAFrame * __f) +{ + try + { + /* Create a switch to get to the correct resume point */ + switch(__f->__suspend_index) { + case 0: break; + case 1: goto __resume_funcA_1; + case 2: goto __resume_funcA_2; + } + + /* co_await Issue726.cpp:34 */ + __f->__suspend_34_3 = __f->__promise.initial_suspend(); + if(!__f->__suspend_34_3.await_ready()) { + __f->__suspend_34_3.await_suspend(std::coroutine_handle

::from_address(static_cast(__f)).operator std::coroutine_handle()); + __f->__suspend_index = 1; + __f->__initial_await_suspend_called = true; + return; + } + + __resume_funcA_1: + __f->__suspend_34_3.await_resume(); + std::operator<<(std::cout, "funcA_1\n"); + /* co_return Issue726.cpp:36 */ + __f->__promise.return_void(); + goto __final_suspend; + std::operator<<(std::cout, "funcA_2\n"); + /* co_return Issue726.cpp:34 */ + __f->__promise.return_void()/* implicit */; + goto __final_suspend; + } catch(...) { + if(!__f->__initial_await_suspend_called) { + throw ; + } + + __f->__promise.unhandled_exception(); + } + + __final_suspend: + + /* co_await Issue726.cpp:34 */ + __f->__suspend_34_3_1 = __f->__promise.final_suspend(); + if(!__f->__suspend_34_3_1.await_ready()) { + __f->__suspend_34_3_1.await_suspend(std::coroutine_handle

::from_address(static_cast(__f)).operator std::coroutine_handle()); + __f->__suspend_index = 2; + return; + } + + __resume_funcA_2: + __f->destroy_fn(__f); +} + +/* This function invoked by coroutine_handle<>::destroy() */ +void __funcADestroy(__funcAFrame * __f) +{ + /* destroy all variables with dtors */ + __f->~__funcAFrame(); + /* Deallocating the coroutine frame */ + /* Note: The actual argument to delete is __builtin_coro_frame with the promise as parameter */ + operator delete(static_cast(__f), sizeof(__funcAFrame)); +} + + +int main() +{ + static_cast &&>(funcA().data).resume(); + return 0; +}