2828
2929#include < utility> // for tuple_size/tuple_element
3030#include < cstddef>
31+ #include < new> // IWYU pragma: keep for placement new
3132#include < type_traits>
3233
3334namespace stdexec {
@@ -235,11 +236,26 @@ namespace stdexec {
235236 using __state_t = __state_type_t <__tag_t , _Sexpr, _Receiver>;
236237
237238 __state_box (_Sexpr&& __sndr, _Receiver& __rcvr) //
238- noexcept (__nothrow_callable<decltype (__sexpr_impl<__tag_t >::get_state), _Sexpr, _Receiver>)
239- : __state_(__sexpr_impl<__tag_t >::get_state(static_cast <_Sexpr&&>(__sndr), __rcvr)) {
239+ noexcept (__nothrow_callable<decltype (__sexpr_impl<__tag_t >::get_state), _Sexpr, _Receiver>) {
240+ ::new (static_cast <void *>(__buf_)) auto (
241+ __sexpr_impl<__tag_t >::get_state (static_cast <_Sexpr&&>(__sndr), __rcvr));
240242 }
241243
242- __state_t __state_;
244+ ~__state_box () {
245+ reinterpret_cast <__state_t *>(__buf_)->~__state_t ();
246+ }
247+
248+ STDEXEC_ATTRIBUTE ((always_inline)) auto __state () & noexcept -> __state_t& {
249+ return *reinterpret_cast <__state_t *>(__buf_);
250+ }
251+
252+ STDEXEC_ATTRIBUTE ((always_inline)) auto __state () const & noexcept -> const __state_t& {
253+ return *reinterpret_cast <const __state_t *>(__buf_);
254+ }
255+
256+ // We use a buffer to store the state object to make __state_box a standard-layout type
257+ // regardless of whether __state_t is standard-layout or not.
258+ alignas (__state_t ) std::byte __buf_[sizeof (__state_t )]; // NOLINT(modernize-avoid-c-arrays)
243259 };
244260
245261 template <class _Sexpr , class _Receiver , class _State >
@@ -250,6 +266,8 @@ namespace stdexec {
250266#endif
251267 auto __receiver () noexcept -> decltype(auto ) {
252268 void * __state = static_cast <_State*>(this );
269+ // The following cast use the pointer-interconvertibility between the __state_box::__buf_
270+ // member and the containing __state_box object itself.
253271 auto * __sbox = static_cast <__state_box<_Sexpr, _Receiver>*>(__state);
254272 return (static_cast <__op_base<_Sexpr, _Receiver>*>(__sbox)->__rcvr_ );
255273 }
@@ -279,6 +297,14 @@ namespace stdexec {
279297 , __state_(__sexpr_impl<__tag_t >::get_state(static_cast <_Sexpr&&>(__sndr), __rcvr_)) {
280298 }
281299
300+ STDEXEC_ATTRIBUTE ((always_inline)) auto __state () & noexcept -> __state_t& {
301+ return __state_;
302+ }
303+
304+ STDEXEC_ATTRIBUTE ((always_inline)) auto __state () const & noexcept -> const __state_t& {
305+ return __state_;
306+ }
307+
282308 STDEXEC_ATTRIBUTE ((always_inline)) auto __rcvr () & noexcept -> _Receiver& {
283309 return __rcvr_;
284310 }
@@ -302,6 +328,9 @@ namespace stdexec {
302328 noexcept (__nothrow_decay_copyable<_Receiver> && __nothrow_move_constructible<__state_t >)
303329 : __receiver_box<_Receiver>{static_cast <_Receiver&&>(__rcvr)}
304330 , __state_box<_Sexpr, _Receiver>{static_cast <_Sexpr&&>(__sndr), this ->__rcvr_ } {
331+ // This is necessary to ensure that the state object is pointer-interconvertible
332+ // with the __state_box object for the sake of __enable_receiver_from_this.
333+ static_assert (std::is_standard_layout_v<__state_box<_Sexpr, _Receiver>>);
305334 }
306335 };
307336
@@ -391,7 +420,7 @@ namespace stdexec {
391420 auto && __rcvr = this ->__rcvr ();
392421 __inner_ops_.apply (
393422 [&](auto &... __ops) noexcept {
394- __sexpr_impl<__tag_t >::start (this ->__state_ , __rcvr, __ops...);
423+ __sexpr_impl<__tag_t >::start (this ->__state () , __rcvr, __ops...);
395424 },
396425 __inner_ops_);
397426 }
@@ -405,15 +434,15 @@ namespace stdexec {
405434 __sexpr_impl<__tag_t >::complete (_Index (), *this , _Tag2 (), static_cast <_Args&&>(__args)...);
406435 } else {
407436 __sexpr_impl<__tag_t >::complete (
408- _Index (), this ->__state_ , __rcvr, _Tag2 (), static_cast <_Args&&>(__args)...);
437+ _Index (), this ->__state () , __rcvr, _Tag2 (), static_cast <_Args&&>(__args)...);
409438 }
410439 }
411440
412441 template <class _Index >
413442 STDEXEC_ATTRIBUTE ((always_inline)) auto __get_env (_Index) const noexcept
414443 -> __env_type_t<_Index, __tag_t, _Index, _Sexpr, _Receiver> {
415444 const auto & __rcvr = this ->__rcvr ();
416- return __sexpr_impl<__tag_t >::get_env (_Index (), this ->__state_ , __rcvr);
445+ return __sexpr_impl<__tag_t >::get_env (_Index (), this ->__state () , __rcvr);
417446 }
418447 };
419448
0 commit comments