Skip to content

Commit 8d8e528

Browse files
committed
Fixed #726: Added missing goto __final_suspend;.
1 parent d547eda commit 8d8e528

File tree

5 files changed

+209
-0
lines changed

5 files changed

+209
-0
lines changed

CoroutinesCodeGenerator.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,11 @@ class CoroutineASTTransformer : public StmtVisitor<CoroutineASTTransformer>
148148

149149
if(not mSkip) {
150150
mBodyStmts.Add(child);
151+
152+
if(const auto* coret = dyn_cast_or_null<CoreturnStmt>(child);
153+
coret and (coret->getOperand() == nullptr)) {
154+
mBodyStmts.Add(Goto(FINAL_SUSPEND_NAME));
155+
}
151156
}
152157

153158
mSkip = false;

tests/Issue536.expect

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ void __coroResume(__coroFrame * __f)
145145
__f->__suspend_37_14.await_resume();
146146
/* co_return Issue536.cpp:38 */
147147
__f->__promise.return_void();
148+
goto __final_suspend;
148149
/* co_return Issue536.cpp:37 */
149150
__f->__promise.return_void()/* implicit */;
150151
goto __final_suspend;

tests/Issue536_2.expect

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ inline my_resumable coro(int x) const
134134
__f->__suspend_29_16.await_resume();
135135
/* co_return Issue536_2.cpp:30 */
136136
__f->__promise.return_void();
137+
goto __final_suspend;
137138
/* co_return Issue536_2.cpp:29 */
138139
__f->__promise.return_void()/* implicit */;
139140
goto __final_suspend;

tests/Issue726.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// cmdline:-std=c++20
2+
// cmdlineinsights:-edu-show-coroutine-transformation
3+
4+
#include <cstdio>
5+
#include <iostream>
6+
#include <coroutine>
7+
struct P {
8+
std::suspend_always initial_suspend()
9+
{
10+
return {};
11+
}
12+
void return_void() const noexcept
13+
{
14+
std::cout<<"return_void()\n";
15+
}
16+
std::coroutine_handle<> get_return_object()
17+
{
18+
return std::coroutine_handle<P>::from_promise(*this);
19+
};
20+
void unhandled_exception() { throw; }
21+
std::suspend_never final_suspend() noexcept
22+
{
23+
return {};
24+
}
25+
};
26+
struct R {
27+
R( std::coroutine_handle<> d) noexcept
28+
: data(d)
29+
{
30+
}
31+
std::coroutine_handle<> data;
32+
using promise_type = P;
33+
};
34+
R funcA(){
35+
std::cout<<"funcA_1\n";
36+
co_return;
37+
std::cout<<"funcA_2\n";
38+
}
39+
int main()
40+
{
41+
funcA().data.resume();
42+
return 0;
43+
}

tests/Issue726.expect

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/*************************************************************************************
2+
* NOTE: The coroutine transformation you've enabled is a hand coded transformation! *
3+
* Most of it is _not_ present in the AST. What you see is an approximation. *
4+
*************************************************************************************/
5+
#include <cstdio>
6+
#include <iostream>
7+
#include <coroutine>
8+
struct P
9+
{
10+
inline std::suspend_always initial_suspend()
11+
{
12+
return {};
13+
}
14+
15+
inline void return_void() const noexcept
16+
{
17+
std::operator<<(std::cout, "return_void()\n");
18+
}
19+
20+
inline std::coroutine_handle<void> get_return_object()
21+
{
22+
return std::coroutine_handle<P>::from_promise(*this).operator std::coroutine_handle<void>();
23+
}
24+
25+
inline void unhandled_exception()
26+
{
27+
throw ;
28+
}
29+
30+
inline std::suspend_never final_suspend() noexcept
31+
{
32+
return {};
33+
}
34+
35+
// inline constexpr P() noexcept = default;
36+
};
37+
38+
39+
struct R
40+
{
41+
inline R(std::coroutine_handle<void> d) noexcept
42+
: data{std::coroutine_handle<void>(d)}
43+
{
44+
}
45+
46+
std::coroutine_handle<void> data;
47+
using promise_type = P;
48+
};
49+
50+
51+
struct __funcAFrame
52+
{
53+
void (*resume_fn)(__funcAFrame *);
54+
void (*destroy_fn)(__funcAFrame *);
55+
std::__coroutine_traits_sfinae<R>::promise_type __promise;
56+
int __suspend_index;
57+
bool __initial_await_suspend_called;
58+
std::suspend_always __suspend_34_3;
59+
std::suspend_never __suspend_34_3_1;
60+
};
61+
62+
R funcA()
63+
{
64+
/* Allocate the frame including the promise */
65+
/* Note: The actual parameter new is __builtin_coro_size */
66+
__funcAFrame * __f = reinterpret_cast<__funcAFrame *>(operator new(sizeof(__funcAFrame)));
67+
__f->__suspend_index = 0;
68+
__f->__initial_await_suspend_called = false;
69+
70+
/* Construct the promise. */
71+
new (&__f->__promise)std::__coroutine_traits_sfinae<R>::promise_type{};
72+
73+
/* Forward declare the resume and destroy function. */
74+
void __funcAResume(__funcAFrame * __f);
75+
void __funcADestroy(__funcAFrame * __f);
76+
77+
/* Assign the resume and destroy function pointers. */
78+
__f->resume_fn = &__funcAResume;
79+
__f->destroy_fn = &__funcADestroy;
80+
81+
/* Call the made up function with the coroutine body for initial suspend.
82+
This function will be called subsequently by coroutine_handle<>::resume()
83+
which calls __builtin_coro_resume(__handle_) */
84+
__funcAResume(__f);
85+
86+
87+
std::coroutine_handle<void> __coro_gro = __f->__promise.get_return_object();
88+
return R(std::coroutine_handle<void>(static_cast<std::coroutine_handle<void> &&>(__coro_gro)));
89+
}
90+
91+
/* This function invoked by coroutine_handle<>::resume() */
92+
void __funcAResume(__funcAFrame * __f)
93+
{
94+
try
95+
{
96+
/* Create a switch to get to the correct resume point */
97+
switch(__f->__suspend_index) {
98+
case 0: break;
99+
case 1: goto __resume_funcA_1;
100+
case 2: goto __resume_funcA_2;
101+
}
102+
103+
/* co_await Issue726.cpp:34 */
104+
__f->__suspend_34_3 = __f->__promise.initial_suspend();
105+
if(!__f->__suspend_34_3.await_ready()) {
106+
__f->__suspend_34_3.await_suspend(std::coroutine_handle<P>::from_address(static_cast<void *>(__f)).operator std::coroutine_handle<void>());
107+
__f->__suspend_index = 1;
108+
__f->__initial_await_suspend_called = true;
109+
return;
110+
}
111+
112+
__resume_funcA_1:
113+
__f->__suspend_34_3.await_resume();
114+
std::operator<<(std::cout, "funcA_1\n");
115+
/* co_return Issue726.cpp:36 */
116+
__f->__promise.return_void();
117+
goto __final_suspend;
118+
std::operator<<(std::cout, "funcA_2\n");
119+
/* co_return Issue726.cpp:34 */
120+
__f->__promise.return_void()/* implicit */;
121+
goto __final_suspend;
122+
} catch(...) {
123+
if(!__f->__initial_await_suspend_called) {
124+
throw ;
125+
}
126+
127+
__f->__promise.unhandled_exception();
128+
}
129+
130+
__final_suspend:
131+
132+
/* co_await Issue726.cpp:34 */
133+
__f->__suspend_34_3_1 = __f->__promise.final_suspend();
134+
if(!__f->__suspend_34_3_1.await_ready()) {
135+
__f->__suspend_34_3_1.await_suspend(std::coroutine_handle<P>::from_address(static_cast<void *>(__f)).operator std::coroutine_handle<void>());
136+
__f->__suspend_index = 2;
137+
return;
138+
}
139+
140+
__resume_funcA_2:
141+
__f->destroy_fn(__f);
142+
}
143+
144+
/* This function invoked by coroutine_handle<>::destroy() */
145+
void __funcADestroy(__funcAFrame * __f)
146+
{
147+
/* destroy all variables with dtors */
148+
__f->~__funcAFrame();
149+
/* Deallocating the coroutine frame */
150+
/* Note: The actual argument to delete is __builtin_coro_frame with the promise as parameter */
151+
operator delete(static_cast<void *>(__f), sizeof(__funcAFrame));
152+
}
153+
154+
155+
int main()
156+
{
157+
static_cast<const std::coroutine_handle<void> &&>(funcA().data).resume();
158+
return 0;
159+
}

0 commit comments

Comments
 (0)