@@ -38,9 +38,7 @@ namespace STDEXEC
3838 // __connect_await
3939 namespace __connect_await
4040 {
41- STDEXEC_PRAGMA_OPTIMIZE_BEGIN ()
42-
43- static constexpr std::size_t __storage_size = 8 * sizeof (void *);
41+ static constexpr std::size_t __storage_size = 5 * sizeof (void *);
4442 static constexpr std::size_t __storage_align = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
4543
4644 // clang-format off
@@ -76,32 +74,33 @@ namespace STDEXEC
7674 __with_await_transform () = default ;
7775 };
7876
79- struct __awaiter_base
77+ struct __final_awaiter
8078 {
8179 static constexpr auto await_ready () noexcept -> bool
8280 {
8381 return false ;
8482 }
8583
84+ template <class _Promise >
85+ static constexpr void await_suspend (__std::coroutine_handle<_Promise> __h) noexcept
86+ {
87+ try
88+ {
89+ __h.promise ().__opstate_ .__on_resume ();
90+ }
91+ catch (...)
92+ {
93+ __std::unreachable ();
94+ }
95+ }
96+
8697 [[noreturn]]
87- inline void await_resume () noexcept
98+ static void await_resume () noexcept
8899 {
89100 __std::unreachable ();
90101 }
91102 };
92103
93- inline void __destroy_coro (__std::coroutine_handle<> __coro) noexcept
94- {
95- # if STDEXEC_MSVC()
96- // MSVCBUG https://developercommunity.visualstudio.com/t/Double-destroy-of-a-local-in-coroutine-d/10456428
97- // Reassign __coro before calling destroy to make the mutation
98- // observable and to hopefully ensure that the compiler does not eliminate it.
99- std::exchange (__coro, {}).destroy ();
100- # else
101- __coro.destroy ();
102- # endif
103- }
104-
105104 template <class _Awaitable , class _Receiver >
106105 struct __opstate ;
107106
@@ -110,71 +109,44 @@ namespace STDEXEC
110109 {
111110 using __opstate_t = __opstate<_Awaitable, _Receiver>;
112111
113- struct __task : __immovable
114- {
115- using promise_type = __promise;
116-
117- ~__task ()
118- {
119- __connect_await::__destroy_coro (__coro_);
120- }
121-
122- __std::coroutine_handle<__promise> __coro_{};
123- };
124-
125- struct __final_awaiter : __awaiter_base
126- {
127- void await_suspend (__std::coroutine_handle<>) noexcept
128- {
129- using __awaitable_t = __result_of<__get_awaitable, _Awaitable, __promise&>;
130- using __awaiter_t = __awaiter_of_t <__awaitable_t >;
131- using __result_t = decltype (__declval<__awaiter_t >().await_resume ());
132-
133- if (__opstate_.__eptr_ )
134- {
135- STDEXEC::set_error (static_cast <_Receiver&&>(__opstate_.__rcvr_ ),
136- std::move (__opstate_.__eptr_ ));
137- }
138- else if constexpr (__same_as<__result_t , void >)
139- {
140- STDEXEC_ASSERT (__opstate_.__result_ .has_value ());
141- STDEXEC::set_value (static_cast <_Receiver&&>(__opstate_.__rcvr_ ));
142- }
143- else
144- {
145- STDEXEC_ASSERT (__opstate_.__result_ .has_value ());
146- STDEXEC::set_value (static_cast <_Receiver&&>(__opstate_.__rcvr_ ),
147- static_cast <__result_t &&>(*__opstate_.__result_ ));
148- }
149- // This coroutine is never resumed; its work is done.
150- }
151-
152- __opstate<_Awaitable, _Receiver>& __opstate_;
153- };
154-
155112 constexpr explicit (!STDEXEC_EDG()) __promise(__opstate_t & __opstate) noexcept
156113 : __opstate_(__opstate)
157114 {}
158115
116+ ~__promise ()
117+ {
118+ // never invoked
119+ __std::unreachable ();
120+ }
121+
159122 static constexpr auto
160123 operator new ([[maybe_unused]] std::size_t __bytes, __opstate_t & __opstate) noexcept -> void *
161124 {
162- STDEXEC_ASSERT (__bytes <= sizeof (__opstate. __storage_ ) );
125+ STDEXEC_ASSERT (__bytes == __storage_size );
163126 return __opstate.__storage_ ;
164127 }
165128
166- static constexpr void operator delete ([[maybe_unused]] void * __ptr ) noexcept
129+ static constexpr void operator delete (void *, std:: size_t ) noexcept
167130 {
168- // no-op
131+ // never invoked
132+ __std::unreachable ();
169133 }
170134
171- constexpr auto get_return_object () noexcept -> __task
135+ constexpr auto get_return_object () noexcept -> __std::coroutine_handle<__promise>
172136 {
173- return __task{{}, __std::coroutine_handle<__promise>::from_promise (*this )};
137+ try
138+ {
139+ return __std::coroutine_handle<__promise>::from_promise (*this );
140+ }
141+ catch (...)
142+ {
143+ __std::unreachable ();
144+ }
174145 }
175146
176147 [[noreturn]]
177- static auto get_return_object_on_allocation_failure () noexcept -> __task
148+ static auto
149+ get_return_object_on_allocation_failure () noexcept -> __std::coroutine_handle<__promise>
178150 {
179151 __std::unreachable ();
180152 }
@@ -184,26 +156,27 @@ namespace STDEXEC
184156 return {};
185157 }
186158
187- void unhandled_exception () noexcept
159+ [[noreturn]]
160+ static void unhandled_exception () noexcept
188161 {
189- __opstate_. __eptr_ = std::current_exception ();
162+ __std::unreachable ();
190163 }
191164
192165 constexpr auto unhandled_stopped () noexcept -> __std::coroutine_handle<>
193166 {
194- STDEXEC::set_stopped ( static_cast <_Receiver&&>( __opstate_.__rcvr_ ) );
167+ __opstate_.__on_stopped ( );
195168 // Returning noop_coroutine here causes the __connect_awaitable
196169 // coroutine to never resume past the point where it co_await's
197170 // the awaitable.
198171 return __std::noop_coroutine ();
199172 }
200173
201- constexpr auto final_suspend () noexcept -> __final_awaiter
174+ static constexpr auto final_suspend () noexcept -> __final_awaiter
202175 {
203- return __final_awaiter{{}, __opstate_ };
176+ return __final_awaiter{};
204177 }
205178
206- static void return_void () noexcept
179+ static constexpr void return_void () noexcept
207180 {
208181 // no-op
209182 }
@@ -216,67 +189,134 @@ namespace STDEXEC
216189
217190 __opstate<_Awaitable, _Receiver>& __opstate_;
218191 };
192+ } // namespace __connect_await
193+ }
194+
195+ template <class _Awaitable , class _Receiver >
196+ struct std ::coroutine_traits<
197+ STDEXEC::__std::coroutine_handle<STDEXEC::__connect_await::__promise<_Awaitable, _Receiver>>,
198+ STDEXEC::__connect_await::__opstate<_Awaitable, _Receiver>&>
199+ {
200+ using promise_type = STDEXEC::__connect_await::__promise<_Awaitable, _Receiver>;
201+ };
219202
203+ namespace STDEXEC
204+ {
205+ namespace __connect_await
206+ {
220207 template <class _Awaitable , class _Receiver >
221208 struct __opstate
222209 {
223210 constexpr explicit __opstate (_Awaitable&& __awaitable, _Receiver&& __rcvr)
224211 noexcept (__is_nothrow)
225212 : __rcvr_(static_cast <_Receiver&&>(__rcvr))
226- , __task_ (__co_impl(*this ))
213+ , __coro (__co_impl(*this ))
227214 , __awaitable1_(static_cast <_Awaitable&&>(__awaitable))
228- , __awaitable2_(
229- __get_awaitable (static_cast <_Awaitable&&>(__awaitable1_), __task_.__coro_.promise()))
215+ , __awaitable2_(__get_awaitable(static_cast <_Awaitable&&>(__awaitable1_), __coro.promise()))
230216 , __awaiter_(__get_awaiter(static_cast <__awaitable_t &&>(__awaitable2_)))
231217 {}
232218
233219 void start () & noexcept
234220 {
235- __task_.__coro_ .resume ();
221+ try
222+ {
223+ if (!__awaiter_.await_ready ())
224+ {
225+ using __suspend_result_t = decltype (__awaiter_.await_suspend (__coro));
226+
227+ // suspended
228+ if constexpr (std::is_void_v<__suspend_result_t >)
229+ {
230+ // void-returning await_suspend means "always suspend"
231+ __awaiter_.await_suspend (__coro);
232+ return ;
233+ }
234+ else if constexpr (std::same_as<bool , __suspend_result_t >)
235+ {
236+ if (__awaiter_.await_suspend (__coro))
237+ {
238+ // returning true from a bool-returning await_suspend means suspend
239+ return ;
240+ }
241+ else
242+ {
243+ // returning false means immediately resume
244+ }
245+ }
246+ else
247+ {
248+ static_assert (__std::convertible_to<__suspend_result_t , __std::coroutine_handle<>>);
249+ auto __resume_target = __awaiter_.await_suspend (__coro);
250+ __resume_target.resume ();
251+ return ;
252+ }
253+ }
254+
255+ // immediate resumption
256+ __on_resume ();
257+ }
258+ catch (...)
259+ {
260+ if constexpr (!noexcept (__awaiter_.await_ready ())
261+ || !noexcept (__awaiter_.await_suspend (__coro)))
262+ {
263+ STDEXEC::set_error (static_cast <_Receiver&&>(__rcvr_), std::current_exception ());
264+ }
265+ }
236266 }
237267
238268 private:
239269 using __promise_t = __promise<_Awaitable, _Receiver>;
240- using __task_t = __promise_t ::__task;
241270 using __awaitable_t = __result_of<__get_awaitable, _Awaitable, __promise_t &>;
242271 using __awaiter_t = __awaiter_of_t <__awaitable_t >;
243- using __result_t = decltype (__declval<__awaiter_t >().await_resume());
244272
245273 friend __promise_t ;
274+ friend __final_awaiter;
246275
247276 static constexpr bool __is_nothrow = __nothrow_move_constructible<_Awaitable>
248277 && __noexcept_of<__get_awaitable, _Awaitable, __promise_t &>
249278 && __noexcept_of<__get_awaiter, __awaitable_t >;
250279
251- static constexpr std::size_t __storage_size = __connect_await::__storage_size
252- + sizeof (__manual_lifetime<__result_t >)
253- - __same_as<__result_t , void >;
280+ static auto __co_impl (__opstate&) noexcept -> __std::coroutine_handle<__promise_t>
281+ {
282+ co_return ;
283+ }
254284
255- static auto __co_impl (__opstate& __op ) noexcept -> __task_t
285+ constexpr void __on_resume ( ) noexcept
256286 {
257- using __awaiter_t = decltype (__op.__awaiter_ );
258- if constexpr (__same_as<decltype (*__op.__result_ ), void >)
287+ try
259288 {
260- co_await static_cast <__awaiter_t &&>(__op.__awaiter_ );
261- __op.__result_ .emplace ();
289+ if constexpr (std::is_void_v<decltype (__awaiter_.await_resume ())>)
290+ {
291+ __awaiter_.await_resume ();
292+ STDEXEC::set_value (static_cast <_Receiver&&>(__rcvr_));
293+ }
294+ else
295+ {
296+ STDEXEC::set_value (static_cast <_Receiver&&>(__rcvr_), __awaiter_.await_resume ());
297+ }
262298 }
263- else
299+ catch (...)
264300 {
265- __op.__result_ .emplace (co_await static_cast <__awaiter_t &&>(__op.__awaiter_ ));
301+ if constexpr (!noexcept (__awaiter_.await_resume ()))
302+ {
303+ STDEXEC::set_error (static_cast <_Receiver&&>(__rcvr_), std::current_exception ());
304+ }
266305 }
267306 }
268307
308+ constexpr void __on_stopped () noexcept
309+ {
310+ STDEXEC::set_stopped (static_cast <_Receiver&&>(__rcvr_));
311+ }
312+
269313 alignas (__storage_align) std::byte __storage_[__storage_size];
270- _Receiver __rcvr_;
271- __promise_t ::__task __task_;
272- _Awaitable __awaitable1_;
273- __awaitable_t __awaitable2_;
274- __awaiter_t __awaiter_;
275- std::exception_ptr __eptr_{};
276- __optional<__result_t > __result_{};
314+ _Receiver __rcvr_;
315+ __std::coroutine_handle<__promise_t > __coro;
316+ _Awaitable __awaitable1_;
317+ __awaitable_t __awaitable2_;
318+ __awaiter_t __awaiter_;
277319 };
278-
279- STDEXEC_PRAGMA_OPTIMIZE_END ()
280320 } // namespace __connect_await
281321
282322 struct __connect_awaitable_t
0 commit comments