diff --git a/include/exec/sequence/ignore_all_values.hpp b/include/exec/sequence/ignore_all_values.hpp index 23c3d7fa5..24e861098 100644 --- a/include/exec/sequence/ignore_all_values.hpp +++ b/include/exec/sequence/ignore_all_values.hpp @@ -285,15 +285,18 @@ namespace exec { constexpr __binder_back operator()() const noexcept { return {{}, {}, {}}; } + }; + struct __ignore_all_values_impl : __sexpr_defaults { template using __completion_sigs = __sequence_completion_signatures_of_t<_Sequence, _Env>; - template _Sender, class _Env> - static auto get_completion_signatures(_Sender&& __sndr, _Env&&) - -> __completion_sigs<__child_of<_Sender>, _Env> { - return {}; - } + static constexpr auto get_completion_signatures = // + [](_Sender&& __sndr, _Env&&) + -> __completion_sigs<__child_of<_Sender>, _Env> { + static_assert(sender_expr_for<_Sender, ignore_all_values_t>); + return {}; + }; template using _ResultVariant = __result_variant_t<_Child, env_of_t<_Receiver>>; @@ -301,19 +304,25 @@ namespace exec { template using __receiver_t = __t<__receiver<__id<_Receiver>, _ResultVariant<_Child, _Receiver>>>; - template _Sender, receiver _Receiver> + static constexpr auto connect = // + [](_Sender&& __sndr, _Receiver __rcvr) noexcept( + __nothrow_callable<__sexpr_apply_t, _Sender, __connect_fn<_Receiver>>) + -> __call_result_t<__sexpr_apply_t, _Sender, __connect_fn<_Receiver>> requires receiver_of<_Receiver, __completion_sigs<__child_of<_Sender>, env_of_t<_Receiver>>> && sequence_sender_to< __child_of<_Sender>, - __receiver_t<__child_of<_Sender>, _Receiver>> - static auto connect(_Sender&& __sndr, _Receiver __rcvr) noexcept( - __nothrow_callable<__sexpr_apply_t, _Sender, __connect_fn<_Receiver>>) - -> __call_result_t<__sexpr_apply_t, _Sender, __connect_fn<_Receiver>> { + __receiver_t<__child_of<_Sender>, _Receiver>> { + static_assert(sender_expr_for<_Sender, ignore_all_values_t>); return __sexpr_apply((_Sender&&) __sndr, __connect_fn<_Receiver>{__rcvr}); - } + }; }; } using __ignore_all_values::ignore_all_values_t; inline constexpr ignore_all_values_t ignore_all_values{}; +} + +namespace stdexec { + template <> + struct __sexpr_impl : exec::__ignore_all_values::__ignore_all_values_impl {}; } \ No newline at end of file diff --git a/include/stdexec/__detail/__basic_sender.hpp b/include/stdexec/__detail/__basic_sender.hpp index 7fa8d4d0e..b84473416 100644 --- a/include/stdexec/__detail/__basic_sender.hpp +++ b/include/stdexec/__detail/__basic_sender.hpp @@ -59,37 +59,49 @@ namespace stdexec { } }; - STDEXEC_PRAGMA_PUSH() - STDEXEC_PRAGMA_IGNORE_GNU("-Wunused-local-typedefs") + template + struct __desc { + using __tag = _Tag; + using __data = _Data; + using __children = __types<_Child...>; + }; - struct __get_meta { + template + struct __sexpr_uncurry_fn { template - constexpr auto operator()(_Tag, _Data&&, _Child&&...) const noexcept { - struct __meta { - using __tag = _Tag; - using __data = _Data; - using __children = __types<_Child...>; - }; - - return __meta{}; - } + requires __minvocable<_Fn, _Tag, _Data, _Child...> + constexpr auto operator()(_Tag, _Data&&, _Child&&...) const noexcept + -> __minvoke<_Fn, _Tag, _Data, _Child...>; }; - STDEXEC_PRAGMA_POP() + template + using __sexpr_uncurry = + __call_result_t<__impl_of<_Sender>, __copy_cvref_fn<_Sender>, __sexpr_uncurry_fn<_Fn>>; + + template + using __desc_of = __sexpr_uncurry<_Sender, __q<__desc>>; + + using __get_desc = __sexpr_uncurry_fn<__q<__desc>>; + + template + extern __q<__midentity> __name_of_v; + + template + using __name_of_fn = decltype(__name_of_v<_Sender>); template - using __meta_of = __call_result_t<__impl_of<_Sender>, __copy_cvref_fn<_Sender>, __get_meta>; + using __name_of = __minvoke<__name_of_fn<_Sender>, _Sender>; } // namespace __detail template - using tag_of_t = typename __detail::__meta_of<_Sender>::__tag; + using tag_of_t = typename __detail::__desc_of<_Sender>::__tag; template - using __data_of = typename __detail::__meta_of<_Sender>::__data; + using __data_of = typename __detail::__desc_of<_Sender>::__data; template > using __children_of = // - __mapply< _Continuation, typename __detail::__meta_of<_Sender>::__children>; + __mapply< _Continuation, typename __detail::__desc_of<_Sender>::__children>; template using __nth_child_of = __children_of<_Sender, __mbind_front_q<__m_at, _Ny>>; @@ -103,171 +115,91 @@ namespace stdexec { template inline constexpr std::size_t __nbr_children_of = __v<__children_of<_Sender, __msize>>; - namespace __detail { - // Note: This is UB. UBSAN allows it for now. - template - _Parent* __parent_from_child(_Child* __child, _Child _Parent::*__mbr_ptr) noexcept { - alignas(_Parent) char __buf[sizeof(_Parent)]; - _Parent* __parent = (_Parent*) &__buf; - const std::ptrdiff_t __offset = (char*) &(__parent->*__mbr_ptr) - __buf; - return (_Parent*) ((char*) __child - __offset); - } + template + requires __mvalid && __mvalid<__detail::__sexpr_uncurry, _Tp, _Fn> + struct __uncurry_<_Fn, _Tp> { + using __t = __detail::__sexpr_uncurry<_Tp, _Fn>; + }; + + template + struct __sexpr_impl; + + template + using __name_of = __detail::__name_of<_Sender>; + namespace __detail { template struct __op_state; template struct __connect_fn; - struct __default_basis_ops; - - //////////////////////////////////////////////////////////////////////////// - // for testing whether a type has certain named members - struct __with_named_mbrs { - __none_such start; - __none_such connect; - __none_such get_completion_signatures; - __none_such get_attrs; - __none_such get_env; - __none_such get_state; - __none_such complete; - }; - - template - struct __probe_named_mbrs - : _Tag - , __with_named_mbrs { }; - - template - concept __has_get_attrs_member = !requires { - { &__probe_named_mbrs<_Tag>::get_attrs } -> same_as<__none_such __with_named_mbrs::*>; - }; - - template - concept __has_get_env_member = !requires { - { &__probe_named_mbrs<_Tag>::get_env } -> same_as<__none_such __with_named_mbrs::*>; - }; - - template - concept __has_start_member = !requires { - { &__probe_named_mbrs<_Tag>::start } -> same_as<__none_such __with_named_mbrs::*>; - }; - - template - concept __has_connect_member = !requires { - { &__probe_named_mbrs<_Tag>::connect } -> same_as<__none_such __with_named_mbrs::*>; - }; - - template - concept __has_get_state_member = !requires { - { &__probe_named_mbrs<_Tag>::get_state } -> same_as<__none_such __with_named_mbrs::*>; - }; - - template - concept __has_complete_member = !requires { - { &__probe_named_mbrs<_Tag>::complete } -> same_as<__none_such __with_named_mbrs::*>; - }; - - template - using __get_attrs_impl = __if_c<__has_get_attrs_member<_Tag>, _Tag, __default_basis_ops>; - - template - using __get_env_impl = __if_c<__has_get_env_member<_Tag>, _Tag, __default_basis_ops>; - - template - using __start_impl = __if_c<__has_start_member<_Tag>, _Tag, __default_basis_ops>; - - template - using __complete_impl = __if_c<__has_complete_member<_Tag>, _Tag, __default_basis_ops>; - - template - using __get_state_impl = __if_c<__has_get_state_member<_Tag>, _Tag, __default_basis_ops>; - - template - using __connect_impl = __if_c<__has_connect_member<_Tag>, _Tag, __default_basis_ops>; - template - using __state_type_t = __decay_t< - decltype(__get_state_impl<_Tag>::get_state(__declval<_Sexpr>(), __declval<_Receiver&>()))>; + using __state_type_t = __decay_t<__result_of< + __sexpr_impl<_Tag>::get_state, _Sexpr, _Receiver&>>; template - using __env_type_t = decltype(__get_env_impl<_Tag>::get_env( - _Index(), - __declval<__state_type_t<_Tag, _Sexpr, _Receiver>&>(), - __declval())); + using __env_type_t = __result_of< + __sexpr_impl<_Tag>::get_env, _Index, __state_type_t<_Tag, _Sexpr, _Receiver>&, _Receiver&>; template concept __connectable = __callable<__impl_of<_Sexpr>, __copy_cvref_fn<_Sexpr>, __connect_fn<_Sexpr, _Receiver>> && __mvalid<__state_type_t, tag_of_t<_Sexpr>, _Sexpr, _Receiver>; - STDEXEC_ATTRIBUTE((always_inline)) // - auto __get_attrs_fn() noexcept { - return []( - __ignore, __ignore, _Children&&... __child) noexcept -> decltype(auto) { - if constexpr (sizeof...(_Children) == 1) { + // Note: This is UB. UBSAN allows it for now. + template + _Parent* __parent_from_child(_Child* __child, _Child _Parent::*__mbr_ptr) noexcept { + alignas(_Parent) char __buf[sizeof(_Parent)]; + _Parent* __parent = (_Parent*) &__buf; + const std::ptrdiff_t __offset = (char*) &(__parent->*__mbr_ptr) - __buf; + return (_Parent*) ((char*) __child - __offset); + } + + inline constexpr auto __get_attrs = // + [](__ignore, const auto&... __child) noexcept -> decltype(auto) { + if constexpr (sizeof...(__child) == 1) { return stdexec::get_env(__child...); // BUGBUG: should be only the forwarding queries } else { return empty_env(); } STDEXEC_UNREACHABLE(); }; - } - - //////////////////////////////////////////////////////////////////////////// - // default behaviors for the sender/receiver/op state basis operations, - // used by __sexpr - struct __default_basis_ops { - template - STDEXEC_ATTRIBUTE((always_inline)) // - static auto get_attrs(const _Sender& __sndr) noexcept - -> decltype(_Sender::apply(__sndr, __get_attrs_fn())) { - return _Sender::apply(__sndr, __get_attrs_fn()); - } - template - STDEXEC_ATTRIBUTE((always_inline)) // - static auto get_env(__ignore /*index*/, __ignore /*state*/, const _Receiver& __rcvr) noexcept + inline constexpr auto __get_env = // + [](__ignore, __ignore, const _Receiver& __rcvr) noexcept -> env_of_t { return stdexec::get_env(__rcvr); - } + }; - // By default, return the sender's data member - template - STDEXEC_ATTRIBUTE((always_inline)) // - static decltype(auto) get_state(_Sender&& __sndr, __ignore /*receiver*/) noexcept { + inline constexpr auto __get_state = // + [](_Sender&& __sndr, __ignore) noexcept -> decltype(auto) { return __sndr.apply((_Sender&&) __sndr, __get_data()); - } + }; - // By default, return a generic operation state - // TODO: remove me - template - requires __detail::__connectable<_Sender, _Receiver> - static auto connect(_Sender&& __sndr, _Receiver __rcvr) -> __op_state<_Sender, _Receiver> { + inline constexpr auto __connect = // + [](_Sender&& __sndr, _Receiver __rcvr) + -> __op_state<_Sender, _Receiver> + requires __connectable<_Sender, _Receiver> { return __op_state<_Sender, _Receiver>{(_Sender&&) __sndr, (_Receiver&&) __rcvr}; - } + }; - // By default, start child operations in start - template - STDEXEC_ATTRIBUTE((always_inline)) - static void start(_Data&&, _Receiver&&, _ChildOps&... __ops) noexcept { + inline constexpr auto __start = // + [](__ignore, __ignore, _ChildOps&... __ops) noexcept { (stdexec::start(__ops), ...); - } + }; - // By default, forward completions to the outer receiver - template - requires __callable<_SetTag, _Receiver, _Args...> - STDEXEC_ATTRIBUTE((always_inline)) // - static void complete( - _Index, - __ignore /*state*/, - _Receiver& __rcvr, - _SetTag, - _Args&&... __args) noexcept { - static_assert(__v<_Index> == 0, "I don't know how to complete this operation."); - _SetTag()(std::move(__rcvr), (_Args&&) __args...); - } - }; + inline constexpr auto __complete = // + []( + _Index, __ignore, _Receiver& __rcvr, _SetTag, _Args&&... __args) noexcept { + static_assert(__v<_Index> == 0, "I don't know how to complete this operation."); + _SetTag()(std::move(__rcvr), (_Args&&) __args...); + }; + + inline constexpr auto __get_completion_signagures = // + [](__ignore, __ignore) noexcept { + return void(); + }; template struct __receiver { @@ -310,7 +242,7 @@ namespace stdexec { template using __sexpr_connected_with = __mapply< __mbind_front_q<__m_at, typename _Receiver::__index>, - typename __call_result_t<__impl_of, __cp, __get_meta>::__children>; + typename __call_result_t<__impl_of, __cp, __get_desc>::__children>; template struct __op_base : __immovable { @@ -322,7 +254,7 @@ namespace stdexec { __op_base(_Sexpr&& __sndr, _Receiver&& __rcvr) : __rcvr_((_Receiver&&) __rcvr) - , __state_(__get_state_impl<__tag_t>::get_state((_Sexpr&&) __sndr, __rcvr_)) { + , __state_(__sexpr_impl<__tag_t>::get_state((_Sexpr&&) __sndr, __rcvr_)) { } _Receiver& __rcvr() noexcept { @@ -344,7 +276,7 @@ namespace stdexec { STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS __state_t __state_; __op_base(_Sexpr&& __sndr, _Receiver&& __rcvr) - : __state_(__get_state_impl<__tag_t>::get_state((_Sexpr&&) __sndr, __rcvr)) { + : __state_(__sexpr_impl<__tag_t>::get_state((_Sexpr&&) __sndr, __rcvr)) { STDEXEC_ASSERT(this->__rcvr().__op_ == __rcvr.__op_); } @@ -413,16 +345,16 @@ namespace stdexec { template struct __op_state : __op_base<_Sexpr, _Receiver> { - using __meta_t = typename __decay_t<_Sexpr>::__meta_t; - using __tag_t = typename __meta_t::__tag; - using __data_t = typename __meta_t::__data; - using __children_t = typename __meta_t::__children; + using __desc_t = typename __decay_t<_Sexpr>::__desc_t; + using __tag_t = typename __desc_t::__tag; + using __data_t = typename __desc_t::__data; + using __children_t = typename __desc_t::__children; using __state_t = typename __op_state::__state_t; using __connect_t = __connect_fn<_Sexpr, _Receiver>; static auto __connect(__op_state* __self, _Sexpr&& __sexpr) - -> __call_result_t<__impl_of<_Sexpr>, __copy_cvref_fn<_Sexpr>, __connect_t> { - return __sexpr.apply((_Sexpr&&) __sexpr, __connect_t{__self}); + -> __result_of<__sexpr_apply, _Sexpr, __connect_t> { + return __sexpr_apply((_Sexpr&&) __sexpr, __connect_t{__self}); } using __inner_ops_t = decltype(__op_state::__connect(nullptr, __declval<_Sexpr>())); @@ -446,7 +378,7 @@ namespace stdexec { auto&& __rcvr = __self.__rcvr(); __tup::__apply( [&](auto&... __ops) noexcept { - __start_impl<__tag_t>::start(__self.__state_, __rcvr, __ops...); + __sexpr_impl<__tag_t>::start(__self.__state_, __rcvr, __ops...); }, __self.__inner_ops_); } @@ -456,7 +388,7 @@ namespace stdexec { void __complete(_Index, _Tag2, _Args&&... __args) noexcept { using __tag_t = typename __op_state::__tag_t; auto&& __rcvr = this->__rcvr(); - __complete_impl<__tag_t>::complete( + __sexpr_impl<__tag_t>::complete( _Index(), this->__state_, __rcvr, _Tag2(), (_Args&&) __args...); } @@ -464,13 +396,39 @@ namespace stdexec { STDEXEC_ATTRIBUTE((always_inline)) // auto __get_env(_Index) noexcept -> __env_type_t<__tag_t, _Index, _Sexpr, _Receiver> { const auto& __rcvr = this->__rcvr(); - return __get_env_impl<__tag_t>::get_env(_Index(), this->__state_, __rcvr); + return __sexpr_impl<__tag_t>::get_env(_Index(), this->__state_, __rcvr); } }; + + inline constexpr auto __drop_front = // + [](_Fn __fn) noexcept { + return [__fn = std::move(__fn)](auto&&, _Rest&&... __rest) + noexcept(__nothrow_callable) + -> __call_result_t { + return __fn((_Rest&&) __rest...); + }; + }; } // namespace __detail + struct __sexpr_defaults { + static constexpr auto get_attrs = __detail::__get_attrs; + static constexpr auto get_env = __detail::__get_env; + static constexpr auto get_state = __detail::__get_state; + static constexpr auto connect = __detail::__connect; + static constexpr auto start = __detail::__start; + static constexpr auto complete = __detail::__complete; + static constexpr auto get_completion_signagures = __detail::__get_completion_signagures; + }; + + template + struct __sexpr_impl : __sexpr_defaults {}; + using __detail::__enable_receiver_from_this; + template + using __get_attrs_fn = + __result_of<__detail::__drop_front, __mtypeof<__sexpr_impl<_Tag>::get_attrs>>; + ////////////////////////////////////////////////////////////////////////////////////////////////// // __sexpr template @@ -484,12 +442,15 @@ namespace stdexec { using sender_concept = sender_t; using __t = __sexpr; using __id = __sexpr; - using __meta_t = __call_result_t<_ImplFn, __cp, __detail::__get_meta>; - using __tag_t = typename __meta_t::__tag; - using __data_t = typename __meta_t::__data; - using __children_t = typename __meta_t::__children; + using __desc_t = __call_result_t<_ImplFn, __cp, __detail::__get_desc>; + using __tag_t = typename __desc_t::__tag; + using __data_t = typename __desc_t::__data; + using __children_t = typename __desc_t::__children; using __arity_t = __mapply<__msize, __children_t>; + template + using __impl = __sexpr_impl<__meval<__msecond, _Tag, __tag_t>>; + STDEXEC_ATTRIBUTE((always_inline)) // static __tag_t __tag() noexcept { return {}; @@ -506,18 +467,17 @@ namespace stdexec { STDEXEC_ATTRIBUTE((always_inline)) // friend auto tag_invoke(_Tag, const _Self& __self) noexcept // -> __msecond< - __if_c>, // - decltype(__detail::__get_attrs_impl<__tag_t>::get_attrs(__self))> { - static_assert(noexcept(__detail::__get_attrs_impl<__tag_t>::get_attrs(__self))); - return __detail::__get_attrs_impl<__tag_t>::get_attrs(__self); + __if_c && same_as<_Self, __sexpr>>, // + __result_of<__sexpr_apply, const _Self&, __get_attrs_fn<__tag_t>>> { + return __sexpr_apply(__self, __detail::__drop_front(__impl<_Tag>::get_attrs)); } template < same_as _Tag, __decays_to<__sexpr> _Self, class _Env> STDEXEC_ATTRIBUTE((always_inline)) // friend auto tag_invoke(_Tag, _Self&& __self, _Env&& __env) noexcept // -> __msecond< - __if_c>, - decltype(__self.__tag().get_completion_signatures((_Self&&) __self, (_Env&&) __env))> { + __if_c && __decays_to<_Self, __sexpr>>, + __result_of<__impl<_Tag>::get_completion_signatures, _Self, _Env>> { return {}; } @@ -529,12 +489,11 @@ namespace stdexec { STDEXEC_ATTRIBUTE((always_inline)) // friend auto tag_invoke(_Tag, _Self&& __self, _Receiver&& __rcvr) // noexcept(noexcept( - __detail::__connect_impl<__tag_t>::connect((_Self&&) __self, (_Receiver&&) __rcvr))) // + __impl<_Tag>::connect((_Self&&) __self, (_Receiver&&) __rcvr))) // -> __msecond< - __if_c>, - decltype(__detail::__connect_impl< - __tag_t>::connect((_Self&&) __self, (_Receiver&&) __rcvr))> { - return __detail::__connect_impl<__tag_t>::connect((_Self&&) __self, (_Receiver&&) __rcvr); + __if_c && __decays_to<_Self, __sexpr>>, + __result_of<__impl<_Tag>::connect, _Self, _Receiver>> { + return __impl<_Tag>::connect((_Self&&) __self, (_Receiver&&) __rcvr); } template @@ -693,15 +652,6 @@ namespace stdexec { // The __name_of utility defined below is used to pretty-print the type names of // senders in compiler diagnostics. namespace __detail { - template - extern __q<__midentity> __name_of_v; - - template - using __name_of_fn = decltype(__name_of_v<_Sender>); - - template - using __name_of = __minvoke<__name_of_fn<_Sender>, _Sender>; - struct __basic_sender_name { template using __f = // @@ -740,9 +690,6 @@ namespace stdexec { using __remove_rvalue_reference_t = decltype(__detail::__remove_rvalue_reference_fn(__declval<_Ty>())); } // namespace __detail - - template - using __name_of = __detail::__name_of<_Sender>; } // namespace stdexec namespace std { diff --git a/include/stdexec/__detail/__domain.hpp b/include/stdexec/__detail/__domain.hpp index f161e01d1..51c6ba85f 100644 --- a/include/stdexec/__detail/__domain.hpp +++ b/include/stdexec/__detail/__domain.hpp @@ -251,19 +251,5 @@ namespace stdexec { template concept __has_common_domain = // __none_of<__none_such, __common_domain_t<_Senders...>>; - - template - struct __get_env_common_domain { - template _Self> - static auto get_attrs(const _Self& __self) noexcept { - using _Domain = __call_result_t<__sexpr_apply_t, const _Self&, __common_domain_fn>; - if constexpr (same_as<_Domain, default_domain>) { - return empty_env(); - } else { - return __mkprop(__sexpr_apply(__self, __common_domain_fn()), get_domain); - } - STDEXEC_UNREACHABLE(); - } - }; } // namespace __domain } // namespace stdexec diff --git a/include/stdexec/__detail/__execution_fwd.hpp b/include/stdexec/__detail/__execution_fwd.hpp index 9786286a4..a34584c5f 100644 --- a/include/stdexec/__detail/__execution_fwd.hpp +++ b/include/stdexec/__detail/__execution_fwd.hpp @@ -199,10 +199,17 @@ namespace stdexec { namespace v2 { using __on_v2::on_t; } + + namespace __detail { + struct __sexpr_apply_t; + } + + using __detail::__sexpr_apply_t; + extern const __sexpr_apply_t __sexpr_apply; } -// template -// [[deprecated]] void print() {} +template +[[deprecated]] void print() {} -// template -// struct undef; +template +struct undef; diff --git a/include/stdexec/execution.hpp b/include/stdexec/execution.hpp index cbe763ddf..8f0414edf 100644 --- a/include/stdexec/execution.hpp +++ b/include/stdexec/execution.hpp @@ -1910,29 +1910,16 @@ namespace stdexec { inline constexpr __submit_t __submit{}; namespace __inln { - struct __scheduler; - - struct __schedule_t { - static auto get_attrs(__ignore) noexcept - -> __env::__prop<__scheduler(get_completion_scheduler_t)>; - - static auto get_completion_signatures(__ignore, __ignore) noexcept - -> completion_signatures { - return {}; - } - - template - static void start(__ignore, _Receiver& __rcvr) noexcept { - set_value((_Receiver&&) __rcvr); - } - }; + struct __schedule_t { }; struct __scheduler { using __t = __scheduler; using __id = __scheduler; - STDEXEC_ATTRIBUTE((host, device)) friend auto tag_invoke(schedule_t, __scheduler) { - return __make_sexpr<__schedule_t>(); + template + STDEXEC_ATTRIBUTE((host, device)) + friend auto tag_invoke(schedule_t, __scheduler) { + return __make_sexpr<_Tag>(); } friend forward_progress_guarantee @@ -1942,12 +1929,26 @@ namespace stdexec { bool operator==(const __scheduler&) const noexcept = default; }; + } // namespace __inln - inline auto __schedule_t::get_attrs(__ignore) noexcept - -> __env::__prop<__scheduler(get_completion_scheduler_t)> { - return __mkprop(__scheduler{}, get_completion_scheduler); - } - } + template <> + struct __sexpr_impl<__inln::__schedule_t> : __sexpr_defaults { + static constexpr auto get_attrs = // + [](__ignore) noexcept + -> __env::__prop<__inln::__scheduler(get_completion_scheduler_t)> { + return __mkprop(__inln::__scheduler{}, get_completion_scheduler); + }; + + static constexpr auto get_completion_signatures = // + [](__ignore, __ignore) noexcept -> completion_signatures { + return {}; + }; + + static constexpr auto start = // + [](__ignore, _Receiver& __rcvr) noexcept -> void { + set_value((_Receiver&&) __rcvr); + }; + }; ///////////////////////////////////////////////////////////////////////////// // [execution.senders.consumer.start_detached] @@ -2025,35 +2026,30 @@ namespace stdexec { ///////////////////////////////////////////////////////////////////////////// // [execution.senders.factories] namespace __just { - template - struct __just_impl { - template - using __compl_sigs = // - completion_signatures< // - __mapply< // - __qf<_SetTag>, - __decay_t<__data_of<_Sender>>>>; + template + struct __impl : __sexpr_defaults { + using __tag_t = typename _JustTag::__tag_t; + + static constexpr auto get_completion_signatures = + [](_Sender&&, __ignore) noexcept { + static_assert(sender_expr_for<_Sender, _JustTag>); + return completion_signatures<__mapply<__qf<__tag_t>, __decay_t<__data_of<_Sender>>>>{}; + }; - static empty_env get_attrs(__ignore) noexcept { - return {}; - } - template _Sender> - static __compl_sigs<_Sender> get_completion_signatures(_Sender&&, __ignore) { - return {}; - } - - template - static void start(_State& __state, _Receiver& __rcvr) noexcept { + static constexpr auto start = + [](_State& __state, _Receiver& __rcvr) noexcept -> void { __tup::__apply( [&](_Ts&... __ts) noexcept { - _SetTag()(std::move(__rcvr), std::move(__ts)...); + __tag_t()(std::move(__rcvr), std::move(__ts)...); }, __state); - } + }; }; - struct just_t : __just_impl { + struct just_t { + using __tag_t = set_value_t; + template <__movable_value... _Ts> STDEXEC_ATTRIBUTE((host, device)) auto operator()(_Ts&&... __ts) const noexcept((__nothrow_decay_copyable<_Ts> && ...)) { @@ -2061,7 +2057,9 @@ namespace stdexec { } }; - struct just_error_t : __just_impl { + struct just_error_t { + using __tag_t = set_error_t; + template <__movable_value _Error> STDEXEC_ATTRIBUTE((host, device)) auto operator()(_Error&& __err) const noexcept(__nothrow_decay_copyable<_Error>) { @@ -2069,9 +2067,13 @@ namespace stdexec { } }; - struct just_stopped_t : __just_impl { - STDEXEC_ATTRIBUTE((host, device)) auto operator()() const noexcept { - return __make_sexpr(__tuple{}); + struct just_stopped_t { + using __tag_t = set_stopped_t; + + template + STDEXEC_ATTRIBUTE((host, device)) + auto operator()() const noexcept { + return __make_sexpr<_Tag>(__tuple{}); } }; } @@ -2080,6 +2082,15 @@ namespace stdexec { using __just::just_error_t; using __just::just_stopped_t; + template <> + struct __sexpr_impl : __just::__impl { }; + + template <> + struct __sexpr_impl : __just::__impl { }; + + template <> + struct __sexpr_impl : __just::__impl { }; + inline constexpr just_t just{}; inline constexpr just_error_t just_error{}; inline constexpr just_stopped_t just_stopped{}; @@ -2536,28 +2547,36 @@ namespace stdexec { _Sender, _Fun), tag_invoke_t(then_t, _Sender, _Fun)>; - - template _Sender, class _Env> - static auto get_completion_signatures(_Sender&&, _Env&&) - -> __completion_signatures_t<__decay_t<__data_of<_Sender>>, __child_of<_Sender>, _Env> { - return {}; - } - - template - static void - complete(__ignore, _State& __state, _Receiver& __rcvr, _Tag, _Args&&... __args) noexcept { - if constexpr (std::same_as<_Tag, set_value_t>) { - stdexec::__set_value_invoke(std::move(__rcvr), std::move(__state), (_Args&&) __args...); - } else { - _Tag()(std::move(__rcvr), (_Args&&) __args...); - } - } }; } // namespace __then using __then::then_t; inline constexpr then_t then{}; + template <> + struct __sexpr_impl : __sexpr_defaults { + static constexpr auto get_completion_signatures = // + [](_Sender&&, _Env&&) noexcept + -> __then::__completion_signatures_t<__decay_t<__data_of<_Sender>>, __child_of<_Sender>, _Env> { + static_assert(sender_expr_for<_Sender, then_t>); + return {}; + }; + + static constexpr auto complete = // + []( + __ignore, + auto& __state, + auto& __rcvr, + _Tag, + _Args&&... __args) noexcept -> void { + if constexpr (std::same_as<_Tag, set_value_t>) { + stdexec::__set_value_invoke(std::move(__rcvr), std::move(__state), (_Args&&) __args...); + } else { + _Tag()(std::move(__rcvr), (_Args&&) __args...); + } + }; + }; + ///////////////////////////////////////////////////////////////////////////// // [execution.senders.adaptors.upon_error] namespace __upon_error { @@ -2598,28 +2617,37 @@ namespace stdexec { _Sender, _Fun), tag_invoke_t(upon_error_t, _Sender, _Fun)>; - - template _Sender, class _Env> - static auto get_completion_signatures(_Sender&&, _Env&&) - -> __completion_signatures_t<__decay_t<__data_of<_Sender>>, __child_of<_Sender>, _Env> { - return {}; - } - - template - static void - complete(__ignore, _State& __state, _Receiver& __rcvr, _Tag, _Args&&... __args) noexcept { - if constexpr (std::same_as<_Tag, set_error_t>) { - stdexec::__set_value_invoke(std::move(__rcvr), std::move(__state), (_Args&&) __args...); - } else { - _Tag()(std::move(__rcvr), (_Args&&) __args...); - } - } }; - } + } // namespace __upon_error using __upon_error::upon_error_t; inline constexpr upon_error_t upon_error{}; + template <> + struct __sexpr_impl : __sexpr_defaults { + static constexpr auto get_completion_signatures = // + [](_Sender&&, _Env&&) noexcept + -> __upon_error:: + __completion_signatures_t<__decay_t<__data_of<_Sender>>, __child_of<_Sender>, _Env> { + static_assert(sender_expr_for<_Sender, upon_error_t>); + return {}; + }; + + static constexpr auto complete = // + []( + __ignore, + auto& __state, + auto& __rcvr, + _Tag, + _Args&&... __args) noexcept -> void { + if constexpr (std::same_as<_Tag, set_error_t>) { + stdexec::__set_value_invoke(std::move(__rcvr), std::move(__state), (_Args&&) __args...); + } else { + _Tag()(std::move(__rcvr), (_Args&&) __args...); + } + }; + }; + ///////////////////////////////////////////////////////////////////////////// // [execution.senders.adaptors.upon_stopped] namespace __upon_stopped { @@ -2664,28 +2692,37 @@ namespace stdexec { _Sender, _Fun), tag_invoke_t(upon_stopped_t, _Sender, _Fun)>; - - template _Sender, class _Env> - static auto get_completion_signatures(_Sender&&, _Env&&) - -> __completion_signatures_t<__decay_t<__data_of<_Sender>>, __child_of<_Sender>, _Env> { - return {}; - } - - template - static void - complete(__ignore, _State& __state, _Receiver& __rcvr, _Tag, _Args&&... __args) noexcept { - if constexpr (std::same_as<_Tag, set_stopped_t>) { - stdexec::__set_value_invoke(std::move(__rcvr), std::move(__state), (_Args&&) __args...); - } else { - _Tag()(std::move(__rcvr), (_Args&&) __args...); - } - } }; - } + } // namespace __upon_stopped using __upon_stopped::upon_stopped_t; inline constexpr upon_stopped_t upon_stopped{}; + template <> + struct __sexpr_impl : __sexpr_defaults { + static constexpr auto get_completion_signatures = // + [](_Sender&&, _Env&&) noexcept + -> __upon_stopped:: + __completion_signatures_t<__decay_t<__data_of<_Sender>>, __child_of<_Sender>, _Env> { + static_assert(sender_expr_for<_Sender, upon_stopped_t>); + return {}; + }; + + static constexpr auto complete = // + []( + __ignore, + auto& __state, + auto& __rcvr, + _Tag, + _Args&&... __args) noexcept -> void { + if constexpr (std::same_as<_Tag, set_stopped_t>) { + stdexec::__set_value_invoke(std::move(__rcvr), std::move(__state), (_Args&&) __args...); + } else { + _Tag()(std::move(__rcvr), (_Args&&) __args...); + } + }; + }; + ///////////////////////////////////////////////////////////////////////////// // [execution.senders.adaptors.bulk] namespace __bulk { @@ -2756,22 +2793,29 @@ namespace stdexec { _Shape, _Fun), tag_invoke_t(bulk_t, _Sender, _Shape, _Fun)>; + }; + struct __bulk_impl : __sexpr_defaults { template using __fun_t = decltype(__decay_t<__data_of<_Sender>>::__fun_); template using __shape_t = decltype(__decay_t<__data_of<_Sender>>::__shape_); - template _Sender, class _Env> - static auto get_completion_signatures(_Sender&&, _Env&&) + static constexpr auto get_completion_signatures = // + [](_Sender&&, _Env&&) noexcept -> __completion_signatures<__child_of<_Sender>, _Env, __shape_t<_Sender>, __fun_t<_Sender>> { - return {}; - } + static_assert(sender_expr_for<_Sender, bulk_t>); + return {}; + }; - template - static void - complete(__ignore, _State& __state, _Receiver& __rcvr, _Tag, _Args&&... __args) noexcept { + static constexpr auto complete = // + []( + __ignore, + auto& __state, + auto& __rcvr, + _Tag, + _Args&&... __args) noexcept -> void { if constexpr (std::same_as<_Tag, set_value_t>) { using __shape_t = decltype(__state.__shape_); if constexpr (noexcept(__state.__fun_(__shape_t{}, __args...))) { @@ -2792,13 +2836,16 @@ namespace stdexec { } else { _Tag()(std::move(__rcvr), (_Args&&) __args...); } - } + }; }; } using __bulk::bulk_t; inline constexpr bulk_t bulk{}; + template <> + struct __sexpr_impl : __bulk::__bulk_impl { }; + //////////////////////////////////////////////////////////////////////////// // [execution.senders.adaptors.split] namespace __split { @@ -2944,81 +2991,26 @@ namespace stdexec { } }; - struct __split_t { - template - static __split_state<_Sender, _Receiver> get_state(_Sender&& __sndr, _Receiver&) noexcept { - static_assert(sender_expr_for<_Sender, __split_t>); - return __split_state<_Sender, _Receiver>{(_Sender&&) __sndr}; - } - - template - static void start(__split_state<_Sender, _Receiver>& __state, _Receiver& __rcvr) noexcept { - auto* __shared_state = __state.__shared_state_.get(); - std::atomic& __head = __shared_state->__head_; - void* const __completion_state = static_cast(__shared_state); - void* __old = __head.load(std::memory_order_acquire); - - if (__old != __completion_state) { - __state.__on_stop_.emplace( - get_stop_token(get_env(__rcvr)), __on_stop_request{__shared_state->__stop_source_}); - } - - do { - if (__old == __completion_state) { - __state.__notify(&__state); - return; - } - __state.__next_ = static_cast<__split_state_base*>(__old); - } while (!__head.compare_exchange_weak( - __old, - static_cast(&__state), - std::memory_order_release, - std::memory_order_acquire)); - - if (__old == nullptr) { - // the inner sender isn't running - if (__shared_state->__stop_source_.stop_requested()) { - // 1. resets __head to completion state - // 2. notifies waiting threads - // 3. propagates "stopped" signal to `out_r'` - __shared_state->__notify(); - } else { - stdexec::start(__shared_state->__op_state2_); - } - } - } - - template - using __set_value_t = completion_signatures&...)>; + template + using __set_value_t = completion_signatures&...)>; - template - using __set_error_t = completion_signatures&)>; + template + using __set_error_t = completion_signatures&)>; - template - using __completions_t = // - __try_make_completion_signatures< - // NOT TO SPEC: - // See https://github.com/brycelelbach/wg21_p2300_execution/issues/26 - __cvref_t<_CvrefSenderId>, - __env_t<__t<_EnvId>>, - completion_signatures< - set_error_t(const std::exception_ptr&), - set_stopped_t()>, // NOT TO SPEC - __q<__set_value_t>, - __q<__set_error_t>>; - - static inline constexpr auto __get_completion_signatures_fn = - [](auto, const std::shared_ptr<_ShState>&) // - -> __mapply<__q<__completions_t>, __id<_ShState>> { - return {}; - }; + template + using __completions_t = // + __try_make_completion_signatures< + // NOT TO SPEC: + // See https://github.com/brycelelbach/wg21_p2300_execution/issues/26 + __cvref_t<_CvrefSenderId>, + __env_t<__t<_EnvId>>, + completion_signatures< + set_error_t(const std::exception_ptr&), + set_stopped_t()>, // NOT TO SPEC + __q<__set_value_t>, + __q<__set_error_t>>; - template _Self, class _OtherEnv> - static auto get_completion_signatures(_Self&&, _OtherEnv&&) - -> __call_result_t<__sexpr_apply_t, _Self, __mtypeof<__get_completion_signatures_fn>> { - return {}; - } - }; + struct __split_t { }; struct split_t : __split_t { template @@ -3061,11 +3053,79 @@ namespace stdexec { }); } }; + + struct __split_impl : __sexpr_defaults { + static constexpr auto get_state = // + [](_Sender&& __sndr, _Receiver&) noexcept + -> __split_state<_Sender, _Receiver> { + static_assert(sender_expr_for<_Sender, __split_t>); + return __split_state<_Sender, _Receiver>{(_Sender&&) __sndr}; + }; + + static constexpr auto start = // + []( + __split_state<_Sender, _Receiver>& __state, + _Receiver& __rcvr) noexcept -> void { + auto* __shared_state = __state.__shared_state_.get(); + std::atomic& __head = __shared_state->__head_; + void* const __completion_state = static_cast(__shared_state); + void* __old = __head.load(std::memory_order_acquire); + + if (__old != __completion_state) { + __state.__on_stop_.emplace( + get_stop_token(stdexec::get_env(__rcvr)), + __on_stop_request{__shared_state->__stop_source_}); + } + + do { + if (__old == __completion_state) { + __state.__notify(&__state); + return; + } + __state.__next_ = static_cast<__split_state_base*>(__old); + } while (!__head.compare_exchange_weak( + __old, + static_cast(&__state), + std::memory_order_release, + std::memory_order_acquire)); + + if (__old == nullptr) { + // the inner sender isn't running + if (__shared_state->__stop_source_.stop_requested()) { + // 1. resets __head to completion state + // 2. notifies waiting threads + // 3. propagates "stopped" signal to `out_r'` + __shared_state->__notify(); + } else { + stdexec::start(__shared_state->__op_state2_); + } + } + }; + + static constexpr auto __get_completion_signatures_fn = // + [](auto, const std::shared_ptr<_ShState>&) // + -> __mapply<__q<__completions_t>, __id<_ShState>> { + return {}; + }; + + static constexpr auto get_completion_signatures = // + [](_Self&&, _OtherEnv&&) noexcept + -> __call_result_t<__sexpr_apply_t, _Self, __mtypeof<__get_completion_signatures_fn>> { + static_assert(sender_expr_for<_Self, __split_t>); + return {}; + }; + }; } // namespace __split using __split::split_t; inline constexpr split_t split{}; + template <> + struct __sexpr_impl<__split::__split_t> : __split::__split_impl { }; + + template <> + struct __sexpr_impl : __split::__split_impl { }; + ///////////////////////////////////////////////////////////////////////////// // [execution.senders.adaptors.ensure_started] namespace __ensure_started { @@ -3317,10 +3377,10 @@ namespace stdexec { template auto __connect_fn_(_Receiver& __rcvr) noexcept { return [&](auto, __data<_ShState> __dat) // - noexcept(__nothrow_decay_copyable<_Receiver>) - -> __t<__mapply<__mbind_back_q<__operation, __id<_Receiver>>, __id<_ShState>>> { - return {(_Receiver&&) __rcvr, std::move(__dat.__sh_state_)}; - }; + noexcept(__nothrow_decay_copyable<_Receiver>) + -> __t<__mapply<__mbind_back_q<__operation, __id<_Receiver>>, __id<_ShState>>> { + return {(_Receiver&&) __rcvr, std::move(__dat.__sh_state_)}; + }; } template @@ -3330,32 +3390,31 @@ namespace stdexec { inline auto __get_completion_signatures_fn() noexcept { return [](auto, const __data<_ShState>&) // - -> __mapply<__q<__completions_t>, __id<_ShState>> { + -> __mapply<__q<__completions_t>, __id<_ShState>> { return {}; }; } - struct __ensure_started_t { - template - static auto connect(_Self&& __self, _Receiver __rcvr) noexcept( - __nothrow_callable< - __sexpr_apply_t, - _Self, - decltype(__connect_fn(__declval<_Receiver&>()))>) - -> __call_result_t< - __sexpr_apply_t, - _Self, - decltype(__connect_fn(__declval<_Receiver&>()))> { + struct __ensure_started_t { }; + + struct __ensure_started_impl : __sexpr_defaults { + static constexpr auto connect = // + [](_Self&& __self, _Receiver __rcvr) noexcept( + __nothrow_callable< + __sexpr_apply_t, + _Self, + decltype(__connect_fn(__declval<_Receiver&>()))>) + -> __call_result_t< __sexpr_apply_t, _Self, decltype(__connect_fn(__declval<_Receiver&>()))> { static_assert(sender_expr_for<_Self, __ensure_started_t>); return __sexpr_apply((_Self&&) __self, __connect_fn(__rcvr)); - } + }; - template - static auto get_completion_signatures(_Self&&, _OtherEnv&&) + static constexpr auto get_completion_signatures = // + [](_Self&&, _OtherEnv&&) noexcept -> __call_result_t<__sexpr_apply_t, _Self, __result_of<__get_completion_signatures_fn>> { static_assert(sender_expr_for<_Self, __ensure_started_t>); return {}; - } + }; }; struct ensure_started_t { @@ -3420,6 +3479,10 @@ namespace stdexec { using __ensure_started::ensure_started_t; inline constexpr ensure_started_t ensure_started{}; + template <> + struct __sexpr_impl<__ensure_started::__ensure_started_t> + : __ensure_started::__ensure_started_impl { }; + STDEXEC_PRAGMA_PUSH() STDEXEC_PRAGMA_IGNORE_EDG(not_used_in_partial_spec_arg_list) @@ -3668,37 +3731,50 @@ namespace stdexec { _Function), tag_invoke_t(__let_t, _Sender, _Function)>; - template _Sender> - static auto get_attrs(const _Sender& __sndr) noexcept { - return __sexpr_apply( - __sndr, [](__ignore, __ignore, const _Child& __child) noexcept { - return __join_env(__mkprop(_Domain(), get_domain), stdexec::get_env(__child)); - }); + template > _Sender, class _Env> + static decltype(auto) transform_env(_Sender&& __sndr, const _Env& __env) { + return __sexpr_apply((_Sender&&) __sndr, __mk_transform_env_fn<__let_t<_SetTag>>(__env)); } - template _Self, class _Env> - static auto get_completion_signatures(_Self&&, _Env&&) noexcept - -> __completions<__child_of<_Self>, _Env, __let_t, __data_of<_Self>> { - return {}; + template > _Sender, class _Env> + requires same_as<__early_domain_of_t<_Sender>, dependent_domain> + static decltype(auto) transform_sender(_Sender&& __sndr, const _Env& __env) { + return __sexpr_apply((_Sender&&) __sndr, __mk_transform_sender_fn<__let_t<_SetTag>>(__env)); } + }; - template _Sender, class _Receiver> - static auto get_state(_Sender&& __sndr, _Receiver& __rcvr) { - using _Fun = __data_of<_Sender>; - using _Sched = __completion_sched<_Sender, _SetTag>; - using __mk_let_state = __mbind_front_q<__let_state, _Receiver, _Fun, _SetTag, _Sched>; + template + struct __let_impl : __sexpr_defaults { + static constexpr auto get_attrs = // + [](__ignore, const _Child& __child) noexcept { + return __join_env(__mkprop(_Domain(), get_domain), stdexec::get_env(__child)); + }; - using __let_state_t = __gather_completions_for< - _SetTag, - __child_of<_Sender>, - env_of_t<_Receiver>, - __q<__decayed_tuple>, - __mk_let_state>; + static constexpr auto get_completion_signatures = // + [](_Self&&, _Env&&) noexcept + -> __completions<__child_of<_Self>, _Env, __let_t<_SetTag, _Domain>, __data_of<_Self>> { + static_assert(sender_expr_for<_Self, __let_t<_SetTag, _Domain>>); + return {}; + }; - _Sched __sched = query_or( - get_completion_scheduler<_SetTag>, get_env(__sndr), __none_such()); - return __let_state_t{__sndr.apply((_Sender&&) __sndr, __detail::__get_data()), __sched}; - } + static constexpr auto get_state = // + [](_Sender&& __sndr, _Receiver& __rcvr) { + static_assert(sender_expr_for<_Sender, __let_t<_SetTag, _Domain>>); + using _Fun = __data_of<_Sender>; + using _Sched = __completion_sched<_Sender, _SetTag>; + using __mk_let_state = __mbind_front_q<__let_state, _Receiver, _Fun, _SetTag, _Sched>; + + using __let_state_t = __gather_completions_for< + _SetTag, + __child_of<_Sender>, + env_of_t<_Receiver>, + __q<__decayed_tuple>, + __mk_let_state>; + + _Sched __sched = query_or( + get_completion_scheduler<_SetTag>, stdexec::get_env(__sndr), __none_such()); + return __let_state_t{__sndr.apply((_Sender&&) __sndr, __detail::__get_data()), __sched}; + }; template static void __bind(_State&& __state, _Receiver&& __rcvr, _As&&... __as) noexcept { @@ -3710,36 +3786,29 @@ namespace stdexec { __minvoke<__op_state_for<_Receiver, __fun_t, _SetTag, __sched_t>, _As...>; auto& __args = __state.__args_.template emplace<__tuple_t>((_As&&) __as...); auto& __op = __state.__op_state3_.template emplace<__op_state_t>(__conv{[&] { - return connect( + return stdexec::connect( __apply(std::move(__state.__fun_), __args), __state.__get_result_receiver((_Receiver&&) __rcvr)); }}); - start(__op); + stdexec::start(__op); } catch (...) { set_error(std::move(__rcvr), std::current_exception()); } } - template - static void - complete(__ignore, _State& __state, _Receiver& __rcvr, _Tag, _As&&... __as) noexcept { + static constexpr auto complete = // + []( + __ignore, + _State& __state, + _Receiver& __rcvr, + _Tag, + _As&&... __as) noexcept -> void { if constexpr (std::same_as<_Tag, _SetTag>) { __bind((_State&&) __state, (_Receiver&&) __rcvr, (_As&&) __as...); } else { _Tag()((_Receiver&&) __rcvr, (_As&&) __as...); } - } - - template > _Sender, class _Env> - static decltype(auto) transform_env(_Sender&& __sndr, const _Env& __env) { - return __sexpr_apply((_Sender&&) __sndr, __mk_transform_env_fn<__let_t<_SetTag>>(__env)); - } - - template > _Sender, class _Env> - requires same_as<__early_domain_of_t<_Sender>, dependent_domain> - static decltype(auto) transform_sender(_Sender&& __sndr, const _Env& __env) { - return __sexpr_apply((_Sender&&) __sndr, __mk_transform_sender_fn<__let_t<_SetTag>>(__env)); - } + }; }; } // namespace __let @@ -3752,6 +3821,9 @@ namespace stdexec { using let_stopped_t = __let::__let_t; inline constexpr let_stopped_t let_stopped{}; + template + struct __sexpr_impl<__let::__let_t<_SetTag, _Domain>> : __let::__let_impl<_SetTag, _Domain> { }; + ///////////////////////////////////////////////////////////////////////////// // [execution.senders.adaptors.stopped_as_optional] // [execution.senders.adaptors.stopped_as_error] @@ -3766,7 +3838,9 @@ namespace stdexec { __binder_back operator()() const noexcept { return {}; } + }; + struct __stopped_as_optional_impl : __sexpr_defaults { template requires(sizeof...(_Tys) == 1) using __set_value_t = completion_signatures>...)>; @@ -3774,26 +3848,35 @@ namespace stdexec { template using __set_error_t = completion_signatures; - template _Self, class _Env> - static auto get_completion_signatures(_Self&&, _Env&&) -> make_completion_signatures< - __child_of<_Self>, - _Env, - completion_signatures, - __set_value_t, - __set_error_t, - completion_signatures<>> { + static constexpr auto get_completion_signatures = // + [](_Self&&, _Env&&) noexcept // + -> make_completion_signatures< + __child_of<_Self>, + _Env, + completion_signatures, + __set_value_t, + __set_error_t, + completion_signatures<>> { + static_assert(sender_expr_for<_Self, stopped_as_optional_t>); return {}; - } + }; - template _Self, class _Receiver> + static constexpr auto get_state = // + [](_Self&&, _Receiver&) noexcept requires __single_typed_sender<__child_of<_Self>, env_of_t<_Receiver>> - static auto get_state(_Self&&, _Receiver&) noexcept { + { + static_assert(sender_expr_for<_Self, stopped_as_optional_t>); using _Value = __decay_t<__single_sender_value_t<__child_of<_Self>, env_of_t<_Receiver>>>; return __mtype<_Value>(); - } + }; - template - static void complete(__ignore, _State&, _Receiver& __rcvr, _Tag, _Args&&... __args) noexcept { + static constexpr auto complete = // + []( + __ignore, + _State&, + _Receiver& __rcvr, + _Tag, + _Args&&... __args) noexcept -> void { if constexpr (same_as<_Tag, set_value_t>) { try { static_assert(constructible_from<__t<_State>, _Args...>); @@ -3807,7 +3890,7 @@ namespace stdexec { } else { stdexec::set_value((_Receiver&&) __rcvr, std::optional<__t<_State>>{std::nullopt}); } - } + }; }; struct stopped_as_error_t { @@ -3833,6 +3916,9 @@ namespace stdexec { using __stopped_as_xxx::stopped_as_error_t; inline constexpr stopped_as_error_t stopped_as_error{}; + template <> + struct __sexpr_impl : __stopped_as_xxx::__stopped_as_optional_impl { }; + ///////////////////////////////////////////////////////////////////////////// // run_loop namespace __loop { @@ -4276,12 +4362,6 @@ namespace stdexec { __decay_signature, __decay_signature>; - inline auto __get_env_fn() noexcept { - return [](__ignore, const auto& __data, const auto& __child) noexcept -> decltype(auto) { - return __join_env(__data, stdexec::get_env(__child)); - }; - } - struct schedule_from_t { template auto operator()(_Scheduler&& __sched, _Sender&& __sndr) const { @@ -4296,15 +4376,14 @@ namespace stdexec { using _Env = __0; using __legacy_customizations_t = __types< tag_invoke_t(schedule_from_t, get_completion_scheduler_t(_Env&), _Sender)>; + }; -#if STDEXEC_FRIENDSHIP_IS_LEXICAL() - private: - template - friend struct stdexec::__sexpr; -#endif + struct __schedule_from_impl : __sexpr_defaults { + template + using __env_ = __env::__env_join_t<_Data, env_of_t<_Child>>; template - using __env_t = __sexpr_apply_result_t>; + using __env_t = __mapply<__q<__env_>, _Sender>; template using __scheduler_t = @@ -4324,21 +4403,24 @@ namespace stdexec { stdexec::__cvref_id<__child_of<_Sender>>, stdexec::__id<_Receiver>>>; - template _Sender> - static __env_t<_Sender> get_attrs(const _Sender& __sndr) noexcept { - return __sexpr_apply(__sndr, __get_env_fn()); - } + static constexpr auto get_attrs = // + [](const _Data& __data, const _Child& __child) noexcept { + return __join_env(__data, stdexec::get_env(__child)); + }; - template _Sender, class _Env> - static auto get_completion_signatures(_Sender&&, const _Env&) noexcept + static constexpr auto get_completion_signatures = // + [](_Sender&&, const _Env&) noexcept -> __completions_t<__scheduler_t<_Sender>, __child_of<_Sender>, _Env> { + static_assert(sender_expr_for<_Sender, schedule_from_t>); return {}; - } + }; - template _Sender, receiver _Receiver> + static constexpr auto connect = // + [](_Sender && __sndr, _Receiver __rcvr) // + -> __operation_t<_Sender, _Receiver> // requires sender_to<__child_of<_Sender>, __receiver_t<_Sender, _Receiver>> - static auto connect(_Sender&& __sndr, _Receiver __rcvr) // - -> __operation_t<_Sender, _Receiver> { + { + static_assert(sender_expr_for<_Sender, schedule_from_t>); return __sexpr_apply( (_Sender&&) __sndr, [&]( @@ -4346,13 +4428,16 @@ namespace stdexec { auto __sched = get_completion_scheduler(__data); return {__sched, (_Child&&) __child, (_Receiver&&) __rcvr}; }); - } + }; }; } // namespace __schedule_from using __schedule_from::schedule_from_t; inline constexpr schedule_from_t schedule_from{}; + template <> + struct __sexpr_impl : __schedule_from::__schedule_from_impl { }; + ///////////////////////////////////////////////////////////////////////////// // [execution.senders.adaptors.transfer] namespace __transfer { @@ -4365,12 +4450,6 @@ namespace stdexec { using __lowered_t = // __result_of>, __child_of<_Sender>>; - inline auto __get_env_fn() noexcept { - return [](__ignore, const auto& __data, const auto& __child) noexcept { - return __join_env(__data, stdexec::get_env(__child)); - }; - } - struct transfer_t { template auto operator()(_Sender&& __sndr, _Scheduler&& __sched) const { @@ -4398,11 +4477,6 @@ namespace stdexec { get_completion_scheduler_t(_Env)), tag_invoke_t(transfer_t, _Sender, get_completion_scheduler_t(_Env))>; - template _Sender> - static decltype(auto) get_attrs(const _Sender& __sndr) noexcept { - return __sexpr_apply(__sndr, __get_env_fn()); - } - template static auto __transform_sender_fn(const _Env&) { return [&](__ignore, _Data&& __data, _Child&& __child) { @@ -4416,11 +4490,21 @@ namespace stdexec { return __sexpr_apply((_Sender&&) __sndr, __transform_sender_fn(__env)); } }; + + struct __transfer_impl : __sexpr_defaults { + static constexpr auto get_attrs = // + [](const _Data& __data, const _Child& __child) noexcept -> decltype(auto) { + return __join_env(__data, stdexec::get_env(__child)); + }; + }; } // namespace __transfer using __transfer::transfer_t; inline constexpr transfer_t transfer{}; + template <> + struct __sexpr_impl : __transfer::__transfer_impl { }; + ///////////////////////////////////////////////////////////////////////////// // [execution.senders.transfer_just] namespace __transfer_just { @@ -4431,19 +4515,6 @@ namespace stdexec { }; } - inline auto __make_env_fn() noexcept { - return [](const _Scheduler& __sched, const auto&...) noexcept { - using _Env = __t<__schedule_from::__environ<__id<_Scheduler>>>; - return _Env{{__sched}}; - }; - } - - inline auto __get_env_fn() noexcept { - return [](__ignore, const auto& __data) noexcept { - return __apply(__make_env_fn(), __data); - }; - } - template auto __make_transform_fn(const _Env& __env) { return [&](_Scheduler&& __sched, _Values&&... __vals) { @@ -4471,21 +4542,33 @@ namespace stdexec { __make_sexpr(std::tuple{(_Scheduler&&) __sched, (_Values&&) __vals...})); } - template _Sender> - static auto get_attrs(const _Sender& __sndr) noexcept { - return __sexpr_apply((_Sender&&) __sndr, __get_env_fn()); - } - template static auto transform_sender(_Sender&& __sndr, const _Env& __env) { return __sexpr_apply((_Sender&&) __sndr, __transform_sender_fn(__env)); } }; + + inline auto __make_env_fn() noexcept { + return [](const _Scheduler& __sched, const auto&...) noexcept { + using _Env = __t<__schedule_from::__environ<__id<_Scheduler>>>; + return _Env{{__sched}}; + }; + } + + struct __transfer_just_impl : __sexpr_defaults { + static constexpr auto get_attrs = // + [](const _Data& __data) noexcept { + return __apply(__make_env_fn(), __data); + }; + }; } // namespace __transfer_just using __transfer_just::transfer_just_t; inline constexpr transfer_just_t transfer_just{}; + template <> + struct __sexpr_impl : __transfer_just::__transfer_just_impl { }; + ////////////////////////////////////////////////////////////////////////////////////////////////// // __write adaptor namespace __write_ { @@ -4546,13 +4629,9 @@ namespace stdexec { static auto transform_env(const _Self& __self, _Env&& __env) noexcept { return __sexpr_apply(__self, __transform_env_fn((_Env&&) __env)); } + }; -#if STDEXEC_FRIENDSHIP_IS_LEXICAL() - private: - template - friend struct stdexec::__sexpr; -#endif - + struct __write_impl : __sexpr_defaults { template using __receiver_t = __write_::__receiver_t<__id<_Receiver>, __decay_t<__data_of<_Self>>>; @@ -4560,30 +4639,37 @@ namespace stdexec { using __operation_t = __operation<__cvref_id<__child_of<_Self>>, __id<_Receiver>, __decay_t<__data_of<_Self>>>; - template _Self, receiver _Receiver> + static constexpr auto connect = // + [](_Self && __self, _Receiver __rcvr) // + -> __operation_t<_Self, _Receiver> // requires sender_to<__child_of<_Self>, __receiver_t<_Self, _Receiver>> - static auto connect(_Self&& __self, _Receiver __rcvr) -> __operation_t<_Self, _Receiver> { + { + static_assert(sender_expr_for<_Self, __write_t>); return __sexpr_apply( (_Self&&) __self, [&](__write_t, _Env&& __env, _Child&& __child) // -> __operation_t<_Self, _Receiver> { return {(_Child&&) __child, (_Receiver&&) __rcvr, (_Env&&) __env}; }); - } + }; - template _Self, class _Env> - static auto get_completion_signatures(_Self&&, _Env&&) + static constexpr auto get_completion_signatures = // + [](_Self&&, _Env&&) noexcept -> stdexec::__completion_signatures_of_t< __child_of<_Self>, __env::__env_join_t>&, _Env>> { + static_assert(sender_expr_for<_Self, __write_t>); return {}; - } + }; }; } // namespace __write_ using __write_::__write_t; inline constexpr __write_t __write{}; + template <> + struct __sexpr_impl<__write_t> : __write_::__write_impl { }; + namespace __detail { template STDEXEC_ATTRIBUTE((always_inline)) @@ -4815,52 +4901,9 @@ namespace stdexec { requires sender_in<_Sender, _Env> using __into_variant_result_t = value_types_of_t<_Sender, _Env>; - template - struct __receiver { - using _Receiver = stdexec::__t<_ReceiverId>; - - struct __t { - using receiver_concept = receiver_t; - using __id = __receiver; - using _Receiver = stdexec::__t<_ReceiverId>; - _Receiver __rcvr_; - - // Customize set_value by building a variant and passing the result - // to the base class - template _Tag, class... _As> - requires constructible_from<_Variant, std::tuple<_As&&...>> - friend void tag_invoke(_Tag, __t&& __self, _As&&... __as) noexcept { - try { - set_value( - (_Receiver&&) __self.__rcvr_, _Variant{std::tuple<_As&&...>{(_As&&) __as...}}); - } catch (...) { - set_error((_Receiver&&) __self.__rcvr_, std::current_exception()); - } - } - - template _Tag, class _Error> - friend void tag_invoke(_Tag, __t&& __self, _Error&& __err) noexcept { - set_error((_Receiver&&) __self.__rcvr_, (_Error&&) __err); - } - - template _Tag> - friend void tag_invoke(_Tag, __t&& __self) noexcept { - set_stopped((_Receiver&&) __self.__rcvr_); - } - - friend env_of_t<_Receiver> tag_invoke(get_env_t, const __t& __self) noexcept { - return get_env(__self.__rcvr_); - } - }; - }; - template using __variant_t = __try_value_types_of_t<_Sender, _Env>; - template - using __receiver_t = // - stdexec::__t< __receiver<__id<_Receiver>, __variant_t<_Sender, env_of_t<_Receiver>>>>; - template using __variant_completions = completion_signatures< set_value_t(_Variant), set_error_t(std::exception_ptr)>; @@ -4885,37 +4928,44 @@ namespace stdexec { auto operator()() const noexcept { return __binder_back{}; } + }; -#if STDEXEC_FRIENDSHIP_IS_LEXICAL() - private: - template - friend struct stdexec::__sexpr; -#endif + struct __into_variant_impl : __sexpr_defaults { + static constexpr auto get_state = // + [](_Self&&, _Receiver&) noexcept { + using __variant_t = value_types_of_t<__child_of<_Self>, env_of_t<_Receiver>>; + return __mtype<__variant_t>(); + }; - template _Self, receiver _Receiver> - requires sender_to<__child_of<_Self>, __receiver_t<__child_of<_Self>, _Receiver>> - static auto connect(_Self&& __self, _Receiver __rcvr) // - noexcept( - __nothrow_connectable<__child_of<_Self>, __receiver_t<__child_of<_Self>, _Receiver>>) - -> connect_result_t<__child_of<_Self>, __receiver_t<__child_of<_Self>, _Receiver>> { - return __sexpr_apply( - (_Self&&) __self, [&](__ignore, __ignore, _Child&& __child) { - return stdexec::connect( - (_Child&&) __child, __receiver_t<_Child, _Receiver>{(_Receiver&&) __rcvr}); - }); - } + static constexpr auto complete = // + [](__ignore, _State, _Receiver& __rcvr, _Tag, _Args&&... __args) noexcept -> void { + if constexpr (same_as<_Tag, set_value_t>) { + using __variant_t = __t<_State>; + try { + set_value((_Receiver&&) __rcvr, __variant_t{std::tuple<_Args&&...>{(_Args&&) __args...}}); + } catch (...) { + set_error((_Receiver&&) __rcvr, std::current_exception()); + } + } else { + _Tag()((_Receiver&&) __rcvr, (_Args&&) __args...); + } + }; - template _Self, class _Env> - static auto get_completion_signatures(_Self&&, _Env&&) // + static constexpr auto get_completion_signatures = // + [](_Self&&, _Env&&) noexcept // -> __compl_sigs<__child_of<_Self>, _Env> { + static_assert(sender_expr_for<_Self, into_variant_t>); return {}; - } + }; }; } // namespace __into_variant using __into_variant::into_variant_t; inline constexpr into_variant_t into_variant{}; + template <> + struct __sexpr_impl : __into_variant::__into_variant_impl { }; + ///////////////////////////////////////////////////////////////////////////// // [execution.senders.adaptors.when_all] // [execution.senders.adaptors.when_all_with_variant] @@ -5137,7 +5187,7 @@ namespace stdexec { template using __mk_state_fn_t = decltype(__when_all::__mk_state_fn(__declval<_Env>())); - struct when_all_t : __domain::__get_env_common_domain { + struct when_all_t { // Used by the default_domain to find legacy customizations: using _Sender = __1; using __legacy_customizations_t = // @@ -5151,7 +5201,9 @@ namespace stdexec { return stdexec::transform_sender( __domain, __make_sexpr(__(), (_Senders&&) __sndrs...)); } + }; + struct __when_all_impl : __sexpr_defaults { template using __error_t = __mexception< _INVALID_ARGUMENTS_TO_WHEN_ALL_, @@ -5162,26 +5214,43 @@ namespace stdexec { using __completions = // __children_of<_Self, __mbind_front_q<__completions_t, __env_t<_Env>>>; - template - static auto get_completion_signatures(_Self&&, _Env&&) noexcept { - static_assert(sender_expr_for<_Self, when_all_t>); - return __minvoke<__mtry_catch<__q<__completions>, __q<__error_t>>, _Self, _Env>(); - } + static constexpr auto get_attrs = // + [](__ignore, const _Child&...) noexcept { + using _Domain = __domain::__common_domain_t<_Child...>; + if constexpr (same_as<_Domain, default_domain>) { + return empty_env(); + } else { + return __mkprop(_Domain(), get_domain); + } + STDEXEC_UNREACHABLE(); + }; + + static constexpr auto get_completion_signatures = // + [](_Self&&, _Env&&) noexcept { + static_assert(sender_expr_for<_Self, when_all_t>); + return __minvoke<__mtry_catch<__q<__completions>, __q<__error_t>>, _Self, _Env>(); + }; - template - static auto get_env(__ignore /*index*/, _State& __state, const _Receiver& __rcvr) noexcept + static constexpr auto get_env = // + []( + __ignore, + _State& __state, + const _Receiver& __rcvr) noexcept // -> __env_t> { return __mkenv(stdexec::get_env(__rcvr), __state.__stop_source_); - } + }; - template - static auto get_state(_Self&& __self, _Receiver& __rcvr) + static constexpr auto get_state = // + [](_Self&& __self, _Receiver& __rcvr) -> __sexpr_apply_result_t<_Self, __mk_state_fn_t>> { return __sexpr_apply((_Self&&) __self, __when_all::__mk_state_fn(stdexec::get_env(__rcvr))); - } + }; - template - static void start(_State& __state, _Receiver& __rcvr, _Operations&... __child_ops) noexcept { + static constexpr auto start = // + []( + _State& __state, + _Receiver& __rcvr, + _Operations&... __child_ops) noexcept -> void { // register stop callback: __state.__on_stop_.emplace( get_stop_token(stdexec::get_env(__rcvr)), __on_stop_request{__state.__stop_source_}); @@ -5195,7 +5264,7 @@ namespace stdexec { __state.__complete(__rcvr); } } - } + }; template static void __set_error(_State& __state, _Receiver& __rcvr, _Error&& __err) noexcept { @@ -5216,9 +5285,13 @@ namespace stdexec { } } - template - static void - complete(_Index, _State& __state, _Receiver& __rcvr, _SetTag, _Args&&... __args) noexcept { + static constexpr auto complete = // + []( + _Index, + _State& __state, + _Receiver& __rcvr, + _SetTag, + _Args&&... __args) noexcept -> void { if constexpr (same_as<_SetTag, set_error_t>) { __set_error(__state, __rcvr, (_Args&&) __args...); } else if constexpr (same_as<_SetTag, set_stopped_t>) { @@ -5250,10 +5323,10 @@ namespace stdexec { } __state.__arrive(__rcvr); - } + }; }; - struct when_all_with_variant_t : __domain::__get_env_common_domain { + struct when_all_with_variant_t { using _Sender = __1; using __legacy_customizations_t = // __types; @@ -5278,6 +5351,19 @@ namespace stdexec { } }; + struct __when_all_with_variant_impl : __sexpr_defaults { + static constexpr auto get_attrs = // + [](__ignore, const _Child&...) noexcept { + using _Domain = __domain::__common_domain_t<_Child...>; + if constexpr (same_as<_Domain, default_domain>) { + return empty_env(); + } else { + return __mkprop(_Domain(), get_domain); + } + STDEXEC_UNREACHABLE(); + }; + }; + struct transfer_when_all_t { using _Env = __0; using _Sender = __1; @@ -5298,11 +5384,6 @@ namespace stdexec { _Env{{(_Scheduler&&) __sched}}, (_Senders&&) __sndrs...)); } - template _Sender> - static __data_of get_attrs(const _Sender& __self) noexcept { - return __sexpr_apply(__self, __detail::__get_data()); - } - template static auto transform_sender(_Sender&& __sndr, const _Env& __env) { // transform the transfer_when_all into a regular transform | when_all @@ -5317,6 +5398,13 @@ namespace stdexec { } }; + struct __transfer_when_all_impl : __sexpr_defaults { + static constexpr auto get_attrs = // + [](const _Data& __data, const auto&...) noexcept -> const _Data& { + return __data; + }; + }; + struct transfer_when_all_with_variant_t { using _Env = __0; using _Sender = __1; @@ -5337,11 +5425,6 @@ namespace stdexec { _Env{{(_Scheduler&&) __sched}}, (_Senders&&) __sndrs...)); } - template _Sender> - static __data_of get_attrs(const _Sender& __self) noexcept { - return __sexpr_apply(__self, __detail::__get_data()); - } - template static auto transform_sender(_Sender&& __sndr, const _Env& __env) { // transform the transfer_when_all_with_variant into regular transform_when_all @@ -5356,6 +5439,13 @@ namespace stdexec { }); } }; + + struct __transfer_when_all_with_variant_impl : __sexpr_defaults { + static constexpr auto get_attrs = // + [](const _Data& __data, const auto&...) noexcept -> const _Data& { + return __data; + }; + }; } // namespace __when_all using __when_all::when_all_t; @@ -5370,52 +5460,25 @@ namespace stdexec { using __when_all::transfer_when_all_with_variant_t; inline constexpr transfer_when_all_with_variant_t transfer_when_all_with_variant{}; - namespace __read { - template - using __result_t = __call_result_t<_Tag, env_of_t>>; + template <> + struct __sexpr_impl : __when_all::__when_all_impl { }; - template - concept __nothrow_t = __nothrow_callable<_Tag, env_of_t>>; + template <> + struct __sexpr_impl : __when_all::__when_all_with_variant_impl { }; - // NOT TO SPEC: if the query returns a value, store the value in the operation - // state and pass an rvalue reference to it. - template - struct __operation { - using _Receiver = stdexec::__t<_ReceiverId>; + template <> + struct __sexpr_impl : __when_all::__transfer_when_all_impl { }; - struct __t : __immovable { - using __id = __operation; - _Receiver __rcvr_; - std::optional<__result_t<_Tag, _ReceiverId>> __result_; + template <> + struct __sexpr_impl + : __when_all::__transfer_when_all_with_variant_impl { }; - friend void tag_invoke(start_t, __t& __self) noexcept { - constexpr bool _Nothrow = __nothrow_callable<_Tag, env_of_t<_Receiver>>; - auto __query = [&]() noexcept(_Nothrow) -> __result_t<_Tag, _ReceiverId>&& { - __self.__result_.emplace(__conv{[&]() noexcept(_Nothrow) { - return _Tag()(get_env(__self.__rcvr_)); - }}); - return std::move(*__self.__result_); - }; - stdexec::__set_value_invoke(std::move(__self.__rcvr_), __query); - } - }; - }; - - // if the query returns a reference type, pass it straight through to the receiver. + namespace __read { template - requires std::same_as<__result_t<_Tag, _ReceiverId>&&, __result_t<_Tag, _ReceiverId>> - struct __operation<_Tag, _ReceiverId> { - using _Receiver = stdexec::__t<_ReceiverId>; - - struct __t : __immovable { - using __id = __operation; - _Receiver __rcvr_; + using __result_t = __call_result_t<_Tag, env_of_t>>; - friend void tag_invoke(start_t, __t& __self) noexcept { - stdexec::__set_value_invoke(std::move(__self.__rcvr_), _Tag(), get_env(__self.__rcvr_)); - } - }; - }; + template + concept __nothrow_t = __nothrow_callable<_Tag, env_of_t>>; inline constexpr __mstring __query_failed_diag = "The current execution environment doesn't have a value for the given query."__csz; @@ -5440,45 +5503,72 @@ namespace stdexec { set_value_t(__call_result_t<_Tag, _Env>), set_error_t(std::exception_ptr)>>; - template - struct __sender { - using __t = __sender; - using __id = __sender; - using sender_concept = sender_t; + template + struct __state { + using __query = _Tag; + using __result = _Ty; + std::optional<_Ty> __result_; + }; - template - using __completions_t = - __minvoke<__mtry_catch_q<__read::__completions_t, __q<__query_failed_error>>, _Tag, _Env>; + template + requires same_as<_Ty, _Ty&&> + struct __state<_Tag, _Ty> { + using __query = _Tag; + using __result = _Ty; + }; - template - requires receiver_of<_Receiver, __completions_t>> - friend auto tag_invoke(connect_t, __sender, _Receiver __rcvr) // - noexcept(std::is_nothrow_move_constructible_v<_Receiver>) - -> stdexec::__t<__operation<_Tag, stdexec::__id<_Receiver>>> { - return {{}, (_Receiver&&) __rcvr}; + struct __read_t { + template + constexpr auto operator()(_Tag) const noexcept { + return __make_sexpr<__read_t>(_Tag()); } + }; - template - friend auto tag_invoke(get_completion_signatures_t, __sender, _Env&&) - -> __completions_t<_Env> { - return {}; - } + struct __read_impl : __sexpr_defaults { + template + using __completions_t = + __minvoke<__mtry_catch_q<__read::__completions_t, __q<__query_failed_error>>, _Tag, _Env>; - friend empty_env tag_invoke(get_env_t, const __t&) noexcept { + static constexpr auto get_completion_signatures = // + [](const _Self&, _Env&&) noexcept // + -> __completions_t<__data_of<_Self>, _Env> { + static_assert(sender_expr_for<_Self, __read_t>); return {}; - } - }; + }; - struct __read_t { - template - constexpr __sender<_Tag> operator()(_Tag) const noexcept { - return {}; - } + static constexpr auto get_state = // + [](const _Self&, _Receiver& __rcvr) noexcept { + using __query = __data_of<_Self>; + using __result = __call_result_t<__query, env_of_t<_Receiver>>; + return __state<__query, __result>(); + }; + + static constexpr auto start = // + [](_State& __state, _Receiver& __rcvr) noexcept -> void { + using __query = typename _State::__query; + using __result = typename _State::__result; + if constexpr (same_as<__result, __result&&>) { + // The query returns a reference type; pass it straight through to the receiver. + stdexec::__set_value_invoke(std::move(__rcvr), __query(), stdexec::get_env(__rcvr)); + } else { + constexpr bool _Nothrow = __nothrow_callable<__query, env_of_t<_Receiver>>; + auto __query_fn = [&]() noexcept(_Nothrow) -> __result&& { + __state.__result_.emplace(__conv{[&]() noexcept(_Nothrow) { + return __query()(stdexec::get_env(__rcvr)); + }}); + return std::move(*__state.__result_); + }; + stdexec::__set_value_invoke(std::move(__rcvr), __query_fn); + } + }; }; } // namespace __read inline constexpr __read::__read_t read{}; + template <> + struct __sexpr_impl<__read::__read_t> : __read::__read_impl { }; + namespace __queries { inline auto get_scheduler_t::operator()() const noexcept { return read(get_scheduler);