diff --git a/CodeGenerator.cpp b/CodeGenerator.cpp index a360b433..35427b9e 100644 --- a/CodeGenerator.cpp +++ b/CodeGenerator.cpp @@ -1086,6 +1086,14 @@ void CodeGenerator::InsertArg(const CoroutineBodyStmt* stmt) } //----------------------------------------------------------------------------- +void CodeGenerator::InsertArg(const DependentCoawaitExpr* stmt) +{ + mOutputFormatHelper.Append(kwCoAwaitSpace); + + InsertArg(stmt->getOperand()); +} +//----------------------------------------------------------------------------- + void CodeGenerator::InsertArg(const CoroutineSuspendExpr* stmt) { // co_await or co_yield @@ -1119,13 +1127,29 @@ void CodeGenerator::InsertArg(const CoreturnStmt* stmt) void CodeGenerator::InsertMethodBody(const FunctionDecl* stmt, const size_t posBeforeFunc) { + auto IsPrimaryTemplate = [&] { + // For now, don't transform the primary template of a coroutine + if(const auto* cxxMethod = dyn_cast_or_null(stmt)) { + if(const auto* tmpl = cxxMethod->getParent()->getDescribedClassTemplate(); + tmpl and not isa(cxxMethod->getParent())) { + return true; + } + } + + if(FunctionDecl::TK_FunctionTemplate == stmt->getTemplatedKind()) { + return true; + } + + return false; + }; + if(stmt->doesThisDeclarationHaveABody()) { mOutputFormatHelper.AppendNewLine(); // If this function has a CoroutineBodyStmt as direct descend and coroutine transformation is enabled use the \c // CoroutinesCodeGenerator, otherwise insert the body as usual. if(const auto* corBody = dyn_cast_or_null(stmt->getBody()); - (nullptr != corBody) and GetInsightsOptions().ShowCoroutineTransformation) { + (nullptr != corBody) and not IsPrimaryTemplate() and GetInsightsOptions().ShowCoroutineTransformation) { CoroutinesCodeGenerator codeGenerator{mOutputFormatHelper, posBeforeFunc}; codeGenerator.InsertCoroutine(*stmt, corBody); diff --git a/CodeGeneratorTypes.h b/CodeGeneratorTypes.h index 6a85b23b..588274c7 100644 --- a/CodeGeneratorTypes.h +++ b/CodeGeneratorTypes.h @@ -122,6 +122,7 @@ SUPPORTED_STMT(PackExpansionExpr) SUPPORTED_STMT(CXXFoldExpr) SUPPORTED_STMT(CoroutineBodyStmt) SUPPORTED_STMT(CoroutineSuspendExpr) +SUPPORTED_STMT(DependentCoawaitExpr) SUPPORTED_STMT(CoreturnStmt) SUPPORTED_STMT(DependentScopeDeclRefExpr) SUPPORTED_STMT(CXXRewrittenBinaryOperator) diff --git a/tests/Issue526.cpp b/tests/Issue526.cpp new file mode 100644 index 00000000..c1e16563 --- /dev/null +++ b/tests/Issue526.cpp @@ -0,0 +1,29 @@ +// cmdline:-std=c++2b +// cmdlineinsights:-edu-show-coroutine-transformation + +#include +#include +#include + +template +struct executable { static U execute() { co_await T{}; } }; +struct hello_logic { + bool await_ready() const noexcept { return true; } + void await_suspend(std::coroutine_handle<>) const noexcept { } + void await_resume() const noexcept { std::cout << "Hello, world" << std::endl; } +}; +struct hello_world : executable { + struct promise_type { + auto get_return_object() { return hello_world(this); } + std::suspend_never initial_suspend() noexcept { return {}; } + std::suspend_never final_suspend() noexcept { return {}; } + void return_void() { } + void unhandled_exception() { } + }; + using coro_handle = std::coroutine_handle; + hello_world(promise_type* promise) : handle_(coro_handle::from_promise(*promise)) { } +private: + coro_handle handle_; +}; + +int main() { hello_world::execute(); } diff --git a/tests/Issue526.expect b/tests/Issue526.expect new file mode 100644 index 00000000..2ce88890 --- /dev/null +++ b/tests/Issue526.expect @@ -0,0 +1,206 @@ +/************************************************************************************* + * 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. * + *************************************************************************************/ +// cmdline:-std=c++2b +// cmdlineinsights:-edu-show-coroutine-transformation + +#include +#include +#include + +template +struct executable +{ + static inline U execute() + { + co_await T{}; + } + +}; + +/* First instantiated from: Issue526.cpp:15 */ +#ifdef INSIGHTS_USE_TEMPLATE +template<> +struct executable +{ + struct __executeFrame +{ + void (*resume_fn)(__executeFrame *); + void (*destroy_fn)(__executeFrame *); + std::__coroutine_traits_sfinae::promise_type __promise; + int __suspend_index; + bool __initial_await_suspend_called; + std::suspend_never __suspend_9_30; + hello_logic __suspend_9_51; + std::suspend_never __suspend_9_30_1; +}; + +static inline hello_world execute() + { + /* Allocate the frame including the promise */ + /* Note: The actual parameter new is __builtin_coro_size */ + __executeFrame * __f = reinterpret_cast<__executeFrame *>(operator new(sizeof(__executeFrame))); + __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 __executeResume(__executeFrame * __f); + void __executeDestroy(__executeFrame * __f); + + /* Assign the resume and destroy function pointers. */ + __f->resume_fn = &__executeResume; + __f->destroy_fn = &__executeDestroy; + + /* 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_) */ + __executeResume(__f); + + + return __f->__promise.get_return_object(); + } + + /* This function invoked by coroutine_handle<>::resume() */ + void __executeResume(__executeFrame * __f) + { + try + { + /* Create a switch to get to the correct resume point */ + switch(__f->__suspend_index) { + case 0: break; + case 1: goto __resume_execute_1; + case 2: goto __resume_execute_2; + } + + /* co_await Issue526.cpp:9 */ + __f->__suspend_9_30 = __f->__promise.initial_suspend(); + if(!__f->__suspend_9_30.await_ready()) { + __f->__suspend_9_30.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_execute_1: + __f->__suspend_9_30.await_resume(); + + /* co_await Issue526.cpp:9 */ + __f->__suspend_9_51 = hello_logic{}; + if(!__f->__suspend_9_51.await_ready()) { + __f->__suspend_9_51.await_suspend(std::coroutine_handle::from_address(static_cast(__f)).operator std::coroutine_handle()); + __f->__suspend_index = 2; + return; + } + + __resume_execute_2: + __f->__suspend_9_51.await_resume(); + goto __final_suspend; + } catch(...) { + if(!__f->__initial_await_suspend_called) { + throw ; + } + + __f->__promise.unhandled_exception(); + } + + __final_suspend: + + /* co_await Issue526.cpp:9 */ + __f->__suspend_9_30_1 = __f->__promise.final_suspend(); + if(!__f->__suspend_9_30_1.await_ready()) { + __f->__suspend_9_30_1.await_suspend(std::coroutine_handle::from_address(static_cast(__f)).operator std::coroutine_handle()); + } + + ; + } + + /* This function invoked by coroutine_handle<>::destroy() */ + void __executeDestroy(__executeFrame * __f) + { + /* destroy all variables with dtors */ + __f->~__executeFrame(); + /* Deallocating the coroutine frame */ + /* Note: The actual argument to delete is __builtin_coro_frame with the promise as parameter */ + operator delete(static_cast(__f)); + } + + + // inline constexpr executable() noexcept = default; +}; + +#endif + +struct hello_logic +{ + inline bool await_ready() const noexcept + { + return true; + } + + inline void await_suspend(std::coroutine_handle) const noexcept + { + } + + inline void await_resume() const noexcept + { + std::operator<<(std::cout, "Hello, world").operator<<(std::endl); + } + +}; + + +struct hello_world : public executable +{ + struct promise_type + { + inline hello_world get_return_object() + { + return hello_world(this); + } + + inline std::suspend_never initial_suspend() noexcept + { + return {}; + } + + inline std::suspend_never final_suspend() noexcept + { + return {}; + } + + inline void return_void() + { + } + + inline void unhandled_exception() + { + } + + // inline constexpr promise_type() noexcept = default; + }; + + using coro_handle = std::coroutine_handle; + inline hello_world(promise_type * promise) + : executable() + , handle_{std::coroutine_handle::from_promise(*promise)} + { + } + + + private: + std::coroutine_handle handle_; + public: +}; + + + +int main() +{ + executable::execute(); + return 0; +} +