xref: /openbmc/sdbusplus/include/sdbusplus/async/stdexec/__detail/__basic_sender.hpp (revision 10d0b4b7d1498cfd5c3d37edea271a54d1984e41)
1 /*
2  * Copyright (c) 2021-2024 NVIDIA Corporation
3  *
4  * Licensed under the Apache License Version 2.0 with LLVM Exceptions
5  * (the "License"); you may not use this file except in compliance with
6  * the License. You may obtain a copy of the License at
7  *
8  *   https://llvm.org/LICENSE.txt
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #pragma once
17 
18 #include "__execution_fwd.hpp"
19 
20 #include "__concepts.hpp"
21 #include "__diagnostics.hpp"
22 #include "__env.hpp"
23 #include "__meta.hpp"
24 #include "__senders_core.hpp"
25 #include "__sender_introspection.hpp"
26 #include "__tuple.hpp"
27 #include "__type_traits.hpp"
28 
29 #include <utility> // for tuple_size/tuple_element
30 #include <cstddef>
31 #include <new> // IWYU pragma: keep for placement new
32 #include <type_traits>
33 
34 namespace stdexec {
35   /////////////////////////////////////////////////////////////////////////////
36   // Generic __sender type
37   namespace __detail {
38     template <class _Sender>
39     using __impl_of = decltype((__declval<_Sender>().__impl_));
40   } // namespace __detail
41 
42   template <
43     class _Descriptor,
44     auto _DescriptorFn =
45       [] {
46         return _Descriptor();
47       }
48   >
49   inline constexpr auto __descriptor_fn_v = _DescriptorFn;
50 
51   template <class _Tag, class _Data, class... _Child>
__descriptor_fn()52   inline constexpr auto __descriptor_fn() {
53     return __descriptor_fn_v<__detail::__desc<_Tag, _Data, _Child...>>;
54   }
55 
56 #if STDEXEC_EDG()
57 #  define STDEXEC_SEXPR_DESCRIPTOR(_Tag, _Data, _Child)                                            \
58     stdexec::__descriptor_fn<_Tag, _Data, _Child>()
59 #else
60 #  define STDEXEC_SEXPR_DESCRIPTOR(_Tag, _Data, _Child)                                            \
61     stdexec::__descriptor_fn_v<stdexec::__detail::__desc<_Tag, _Data, _Child>>
62 #endif
63 
64   template <class _Tag>
65   struct __sexpr_impl;
66 
67   template <class _Sexpr, class _Receiver>
68   struct __op_state;
69 
70   template <class _ReceiverId, class _Sexpr, std::size_t _Idx>
71   struct __rcvr;
72 
73   namespace __detail {
74     template <class _Sexpr, class _Receiver>
75     struct __connect_fn;
76 
77     template <class _Tag, class _Sexpr, class _Receiver>
78     using __state_type_t =
79       __decay_t<__result_of<__sexpr_impl<_Tag>::get_state, _Sexpr, _Receiver&>>;
80 
81     template <class _Self, class _Tag, class _Index, class _Sexpr, class _Receiver>
82     using __env_type_t = __result_of<
83       __sexpr_impl<__meval<__msecond, _Self, _Tag>>::get_env,
84       _Index,
85       __state_type_t<__meval<__msecond, _Self, _Tag>, _Sexpr, _Receiver>&,
86       _Receiver&
87     >;
88 
89     template <class _Sexpr, class _Receiver>
90     concept __connectable =
91       __callable<__impl_of<_Sexpr>, __copy_cvref_fn<_Sexpr>, __connect_fn<_Sexpr, _Receiver>>
92       && __mvalid<__state_type_t, tag_of_t<_Sexpr>, _Sexpr, _Receiver>;
93 
94     struct __defaults {
95       static constexpr auto get_attrs =
96         [](__ignore, const auto&... __child) noexcept -> decltype(auto) {
97         if constexpr (sizeof...(__child) == 1) {
98           return __env::__fwd_fn()(stdexec::get_env(__child...));
99         } else {
100           return env<>();
101         }
102       };
103 
104       static constexpr auto get_env =
105         []<class _Receiver>(__ignore, __ignore, const _Receiver& __rcvr) noexcept
106         -> env_of_t<const _Receiver&> {
107         return stdexec::get_env(__rcvr);
108       };
109 
110       static constexpr auto get_state =
111         []<class _Sender>(_Sender&& __sndr, __ignore) noexcept -> decltype(auto) {
112         return __sndr.apply(static_cast<_Sender&&>(__sndr), __get_data());
113       };
114 
115       static constexpr auto connect =
116         []<class _Sender, class _Receiver>(_Sender&& __sndr, _Receiver __rcvr) noexcept(
117           __nothrow_constructible_from<__op_state<_Sender, _Receiver>, _Sender, _Receiver>)
118         -> __op_state<_Sender, _Receiver>
119         requires __connectable<_Sender, _Receiver>
120       {
121         return __op_state<_Sender, _Receiver>{
122           static_cast<_Sender&&>(__sndr), static_cast<_Receiver&&>(__rcvr)};
123       };
124 
125       static constexpr auto start = []<class _StartTag = start_t, class... _ChildOps>(
126                                       __ignore,
127                                       __ignore,
128                                       _ChildOps&... __ops) noexcept {
129         (_StartTag()(__ops), ...);
130       };
131 
132       static constexpr auto complete =
133         []<class _Index, class _Receiver, class _SetTag, class... _Args>(
134           _Index,
135           __ignore,
136           _Receiver& __rcvr,
137           _SetTag,
138           _Args&&... __args) noexcept {
139           static_assert(__v<_Index> == 0, "I don't know how to complete this operation.");
140           _SetTag()(std::move(__rcvr), static_cast<_Args&&>(__args)...);
141         };
142 
143       static constexpr auto get_completion_signatures =
144         []<class _Sender>(_Sender&&, auto&&...) noexcept {
145           static_assert(
146             __mnever<tag_of_t<_Sender>>,
147             "No customization of get_completion_signatures for this sender tag type.");
148         };
149     };
150 
151     template <class _Sexpr, class _Receiver>
152     using __state_t = __state_type_t<typename __decay_t<_Sexpr>::__tag_t, _Sexpr, _Receiver>;
153 
154     template <class _Sexpr, class _Receiver>
155     struct __op_base;
156 
157     template <class _Receiver>
158     struct __receiver_box {
159       _Receiver __rcvr_;
160 
STDEXEC_ATTRIBUTEstdexec::__detail::__receiver_box161       STDEXEC_ATTRIBUTE(always_inline) auto __rcvr() & noexcept -> _Receiver& {
162         return this->__rcvr_;
163       }
164 
STDEXEC_ATTRIBUTEstdexec::__detail::__receiver_box165       STDEXEC_ATTRIBUTE(always_inline) auto __rcvr() const & noexcept -> const _Receiver& {
166         return this->__rcvr_;
167       }
168     };
169 
170     template <class _Sexpr, class _Receiver>
171     struct __state_box : __immovable {
172       using __tag_t = __decay_t<_Sexpr>::__tag_t;
173       using __state_t = __state_type_t<__tag_t, _Sexpr, _Receiver>;
174 
__state_boxstdexec::__detail::__state_box175       __state_box(_Sexpr&& __sndr, _Receiver& __rcvr)
176         noexcept(__noexcept_of<__sexpr_impl<__tag_t>::get_state, _Sexpr, _Receiver&>) {
177         ::new (static_cast<void*>(__buf_)) auto(
178           __sexpr_impl<__tag_t>::get_state(static_cast<_Sexpr&&>(__sndr), __rcvr));
179       }
180 
~__state_boxstdexec::__detail::__state_box181       ~__state_box() {
182         reinterpret_cast<__state_t*>(__buf_)->~__state_t();
183       }
184 
STDEXEC_ATTRIBUTEstdexec::__detail::__state_box185       STDEXEC_ATTRIBUTE(always_inline) auto __state() & noexcept -> __state_t& {
186         return *reinterpret_cast<__state_t*>(__buf_);
187       }
188 
STDEXEC_ATTRIBUTEstdexec::__detail::__state_box189       STDEXEC_ATTRIBUTE(always_inline) auto __state() const & noexcept -> const __state_t& {
190         return *reinterpret_cast<const __state_t*>(__buf_);
191       }
192 
193       // We use a buffer to store the state object to make __state_box a standard-layout type
194       // regardless of whether __state_t is standard-layout or not.
195       alignas(__state_t) std::byte __buf_[sizeof(__state_t)]; // NOLINT(modernize-avoid-c-arrays)
196     };
197 
198     template <class _Sexpr, class _Receiver, class _State>
199     struct __enable_receiver_from_this {
200 #if STDEXEC_HAS_FEATURE(undefined_behavior_sanitizer) && STDEXEC_CLANG()
201       // See https://github.com/llvm/llvm-project/issues/101276
202       [[clang::noinline]]
203 #endif
__receiverstdexec::__detail::__enable_receiver_from_this204       auto __receiver() noexcept -> decltype(auto) {
205         void* __state = static_cast<_State*>(this);
206         // The following cast use the pointer-interconvertibility between the __state_box::__buf_
207         // member and the containing __state_box object itself.
208         auto* __sbox = static_cast<__state_box<_Sexpr, _Receiver>*>(__state);
209         return (static_cast<__op_base<_Sexpr, _Receiver>*>(__sbox)->__rcvr_);
210       }
211     };
212 
213     template <class _Sexpr, class _Receiver>
214     concept __state_uses_receiver = derived_from<
215       __state_t<_Sexpr, _Receiver>,
216       __enable_receiver_from_this<_Sexpr, _Receiver, __state_t<_Sexpr, _Receiver>>
217     >;
218 
219     template <class _Sexpr, class _Receiver>
220     struct __op_base : __immovable {
221       using __tag_t = __decay_t<_Sexpr>::__tag_t;
222       using __state_t = __state_type_t<__tag_t, _Sexpr, _Receiver>;
223 
224       STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS
225       _Receiver __rcvr_;
226       STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS
227       __state_t __state_;
228 
__op_basestdexec::__detail::__op_base229       __op_base(_Sexpr&& __sndr, _Receiver&& __rcvr) noexcept(
230         __nothrow_decay_copyable<_Receiver>
231         && noexcept(
232           __state_t(__sexpr_impl<__tag_t>::get_state(static_cast<_Sexpr&&>(__sndr), __rcvr_))))
233         : __rcvr_(static_cast<_Receiver&&>(__rcvr))
234         , __state_(__sexpr_impl<__tag_t>::get_state(static_cast<_Sexpr&&>(__sndr), __rcvr_)) {
235       }
236 
STDEXEC_ATTRIBUTEstdexec::__detail::__op_base237       STDEXEC_ATTRIBUTE(always_inline) auto __state() & noexcept -> __state_t& {
238         return __state_;
239       }
240 
STDEXEC_ATTRIBUTEstdexec::__detail::__op_base241       STDEXEC_ATTRIBUTE(always_inline) auto __state() const & noexcept -> const __state_t& {
242         return __state_;
243       }
244 
STDEXEC_ATTRIBUTEstdexec::__detail::__op_base245       STDEXEC_ATTRIBUTE(always_inline) auto __rcvr() & noexcept -> _Receiver& {
246         return __rcvr_;
247       }
248 
STDEXEC_ATTRIBUTEstdexec::__detail::__op_base249       STDEXEC_ATTRIBUTE(always_inline) auto __rcvr() const & noexcept -> const _Receiver& {
250         return __rcvr_;
251       }
252     };
253 
254     template <class _Sexpr, class _Receiver>
255       requires __state_uses_receiver<_Sexpr, _Receiver>
256     struct __op_base<_Sexpr, _Receiver>
257       : __receiver_box<_Receiver>
258       , __state_box<_Sexpr, _Receiver> {
259       using __tag_t = __decay_t<_Sexpr>::__tag_t;
260       using __state_t = __state_type_t<__tag_t, _Sexpr, _Receiver>;
261 
262       STDEXEC_IMMOVABLE(__op_base);
263 
__op_basestdexec::__detail::__op_base264       __op_base(_Sexpr&& __sndr, _Receiver&& __rcvr)
265         noexcept(__nothrow_decay_copyable<_Receiver> && __nothrow_move_constructible<__state_t>)
266         : __receiver_box<_Receiver>{static_cast<_Receiver&&>(__rcvr)}
267         , __state_box<_Sexpr, _Receiver>{static_cast<_Sexpr&&>(__sndr), this->__rcvr_} {
268         // This is necessary to ensure that the state object is pointer-interconvertible
269         // with the __state_box object for the sake of __enable_receiver_from_this.
270         static_assert(std::is_standard_layout_v<__state_box<_Sexpr, _Receiver>>);
271       }
272     };
273 
274     STDEXEC_PRAGMA_PUSH()
275     STDEXEC_PRAGMA_IGNORE_GNU("-Wmissing-braces")
276 
277     template <class _Sexpr, class _Receiver>
278     struct __connect_fn {
279       template <std::size_t _Idx>
280       using __receiver_t = __t<__rcvr<__id<_Receiver>, _Sexpr, _Idx>>;
281 
282       __op_state<_Sexpr, _Receiver>* __op_;
283 
284       struct __impl {
285         __op_state<_Sexpr, _Receiver>* __op_;
286 
287         template <std::size_t... _Is, class... _Child>
operator ()stdexec::__detail::__connect_fn::__impl288         auto operator()(__indices<_Is...>, _Child&&... __child) const
289           noexcept((__nothrow_connectable<_Child, __receiver_t<_Is>> && ...))
290             -> __tuple_for<connect_result_t<_Child, __receiver_t<_Is>>...> {
291           return __tuple{connect(static_cast<_Child&&>(__child), __receiver_t<_Is>{__op_})...};
292         }
293       };
294 
295       template <class... _Child>
operator ()stdexec::__detail::__connect_fn296       auto operator()(__ignore, __ignore, _Child&&... __child) const
297         noexcept(__nothrow_callable<__impl, __indices_for<_Child...>, _Child...>)
298           -> __call_result_t<__impl, __indices_for<_Child...>, _Child...> {
299         return __impl{__op_}(__indices_for<_Child...>(), static_cast<_Child&&>(__child)...);
300       }
301 
operator ()stdexec::__detail::__connect_fn302       auto operator()(__ignore, __ignore) const noexcept -> __tuple_for<> {
303         return {};
304       }
305     };
306 
STDEXEC_PRAGMA_POP()307     STDEXEC_PRAGMA_POP()
308 
309     inline constexpr auto __drop_front = []<class _Fn>(_Fn __fn) noexcept {
310       return [__fn = std::move(__fn)]<class... _Rest>(auto&&, _Rest&&... __rest) noexcept(
311                __nothrow_callable<const _Fn&, _Rest...>) -> __call_result_t<const _Fn&, _Rest...> {
312         return __fn(static_cast<_Rest&&>(__rest)...);
313       };
314     };
315 
316     template <class _Tag, class... _Captures>
STDEXEC_ATTRIBUTE(host,device,always_inline)317     STDEXEC_ATTRIBUTE(host, device, always_inline)
318     constexpr auto __captures(_Tag, _Captures&&... __captures2) {
319       return
320         [... __captures3 = static_cast<_Captures&&>(__captures2)]<class _Cvref, class _Fun>(
321           _Cvref,
322           _Fun&&
323             __fun) mutable noexcept(__nothrow_callable<_Fun, _Tag, __minvoke<_Cvref, _Captures>...>)
324           -> __call_result_t<_Fun, _Tag, __minvoke<_Cvref, _Captures>...>
325           requires __callable<_Fun, _Tag, __minvoke<_Cvref, _Captures>...>
326       {
327         // The use of decltype(__captures3) here instead of _Captures is a workaround for
328         // a codegen bug in nvc++.
329         return static_cast<_Fun&&>(
330           __fun)(_Tag(), const_cast<__minvoke<_Cvref, decltype(__captures3)>&&>(__captures3)...);
331       };
332     }
333 
334     template <class _Tag, class _Data, class... _Child>
335     using __captures_t =
336       decltype(__detail::__captures(_Tag(), __declval<_Data>(), __declval<_Child>()...));
337 
338     template <class, class, class... _Child>
339     using __tuple_size_t = char[sizeof...(_Child) + 2]; // NOLINT(modernize-avoid-c-arrays)
340 
341     template <std::size_t _Idx, class _Descriptor>
342     concept __in_range = (_Idx < sizeof(__minvoke<_Descriptor, __q<__tuple_size_t>>));
343 
344   } // namespace __detail
345 
346   using __sexpr_defaults = __detail::__defaults;
347 
348   template <class _ReceiverId, class _Sexpr, std::size_t _Idx>
349   struct __rcvr {
350     using _Receiver = stdexec::__t<_ReceiverId>;
351 
352     struct __t {
353       using receiver_concept = receiver_t;
354       using __id = __rcvr;
355 
356       using __index = __msize_t<_Idx>;
357       using __parent_op_t = __op_state<_Sexpr, _Receiver>;
358       using __tag_t = tag_of_t<_Sexpr>;
359 
360       // A pointer to the parent operation state, which contains the one created with
361       // this receiver.
362       __parent_op_t* __op_;
363 
364       template <class... _Args>
STDEXEC_ATTRIBUTEstdexec::__rcvr::__t365       STDEXEC_ATTRIBUTE(always_inline)
366       void set_value(_Args&&... __args) noexcept {
367         __op_->__complete(__index(), stdexec::set_value, static_cast<_Args&&>(__args)...);
368       }
369 
370       template <class _Error>
STDEXEC_ATTRIBUTEstdexec::__rcvr::__t371       STDEXEC_ATTRIBUTE(always_inline)
372       void set_error(_Error&& __err) noexcept {
373         __op_->__complete(__index(), stdexec::set_error, static_cast<_Error&&>(__err));
374       }
375 
STDEXEC_ATTRIBUTEstdexec::__rcvr::__t376       STDEXEC_ATTRIBUTE(always_inline) void set_stopped() noexcept {
377         __op_->__complete(__index(), stdexec::set_stopped);
378       }
379 
380       template <__same_as<__t> _Self = __t>
STDEXEC_ATTRIBUTEstdexec::__rcvr::__t381       STDEXEC_ATTRIBUTE(always_inline)
382       auto get_env() const noexcept
383         -> __detail::__env_type_t<_Self, __tag_t, __index, _Sexpr, _Receiver> {
384         return __op_->__get_env(__index());
385       }
386     };
387   };
388 
389   template <class _Sexpr, class _Receiver>
390   struct __op_state : __detail::__op_base<_Sexpr, _Receiver> {
391     using __desc_t = __decay_t<_Sexpr>::__desc_t;
392     using __tag_t = __desc_t::__tag;
393     using __data_t = __desc_t::__data;
394     using __state_t = __op_state::__op_base::__state_t;
395     using __inner_ops_t =
396       __result_of<__sexpr_apply, _Sexpr, __detail::__connect_fn<_Sexpr, _Receiver>>;
397 
398     __inner_ops_t __inner_ops_;
399 
__op_statestdexec::__op_state400     __op_state(_Sexpr&& __sexpr, _Receiver __rcvr) noexcept(
401       __nothrow_constructible_from<__detail::__op_base<_Sexpr, _Receiver>, _Sexpr, _Receiver>
402       && __noexcept_of<__sexpr_apply, _Sexpr, __detail::__connect_fn<_Sexpr, _Receiver>>)
403       : __op_state::__op_base{static_cast<_Sexpr&&>(__sexpr), static_cast<_Receiver&&>(__rcvr)}
404       , __inner_ops_(__sexpr_apply(
405           static_cast<_Sexpr&&>(__sexpr),
406           __detail::__connect_fn<_Sexpr, _Receiver>{this})) {
407     }
408 
STDEXEC_ATTRIBUTEstdexec::__op_state409     STDEXEC_ATTRIBUTE(always_inline) void start() & noexcept {
410       using __tag_t = __op_state::__tag_t;
411       auto&& __rcvr = this->__rcvr();
412       __inner_ops_.apply(
413         [&](auto&... __ops) noexcept {
414           __sexpr_impl<__tag_t>::start(this->__state(), __rcvr, __ops...);
415         },
416         __inner_ops_);
417     }
418 
419     template <class _Index, class _Tag2, class... _Args>
STDEXEC_ATTRIBUTEstdexec::__op_state420     STDEXEC_ATTRIBUTE(always_inline)
421     void __complete(_Index, _Tag2, _Args&&... __args) noexcept {
422       using __tag_t = __op_state::__tag_t;
423       auto&& __rcvr = this->__rcvr();
424       using _CompleteFn = __mtypeof<__sexpr_impl<__tag_t>::complete>;
425       if constexpr (__callable<_CompleteFn, _Index, __op_state&, _Tag2, _Args...>) {
426         __sexpr_impl<__tag_t>::complete(_Index(), *this, _Tag2(), static_cast<_Args&&>(__args)...);
427       } else {
428         __sexpr_impl<__tag_t>::complete(
429           _Index(), this->__state(), __rcvr, _Tag2(), static_cast<_Args&&>(__args)...);
430       }
431     }
432 
433     template <class _Index>
STDEXEC_ATTRIBUTEstdexec::__op_state434     STDEXEC_ATTRIBUTE(always_inline)
435     auto __get_env(_Index) const noexcept
436       -> __detail::__env_type_t<_Index, __tag_t, _Index, _Sexpr, _Receiver> {
437       const auto& __rcvr = this->__rcvr();
438       return __sexpr_impl<__tag_t>::get_env(_Index(), this->__state(), __rcvr);
439     }
440   };
441 
442   template <class _Tag>
443   struct __sexpr_impl : __detail::__defaults {
444     using not_specialized = void;
445   };
446 
447   using __detail::__enable_receiver_from_this;
448 
449   template <class _Tag>
450   using __get_attrs_fn =
451     __result_of<__detail::__drop_front, __mtypeof<__sexpr_impl<_Tag>::get_attrs>>;
452 
453   //! A dummy type used only for diagnostic purposes.
454   //! See `__sexpr` for the implementation of P2300's _`basic-sender`_.
455   template <class...>
456   struct __basic_sender {
457     // See MAINTAINERS.md#class-template-parameters for `__id` and `__t`.
458     using __id = __basic_sender;
459     using __t = __basic_sender;
460   };
461 
462   namespace {
463     //! A struct template to aid in creating senders.
464     //! This struct closely resembles P2300's [_`basic-sender`_](https://eel.is/c++draft/exec#snd.expos-24),
465     //! but is not an exact implementation.
466     //! Note: The struct named `__basic_sender` is just a dummy type and is also not _`basic-sender`_.
467     template <auto _DescriptorFn>
468     struct __sexpr {
469       using sender_concept = sender_t;
470 
471       // See MAINTAINERS.md#class-template-parameters for `__id` and `__t`.
472       using __id = __sexpr;
473       using __t = __sexpr;
474       using __desc_t = decltype(_DescriptorFn());
475       using __tag_t = __desc_t::__tag;
476       using __captures_t = __minvoke<__desc_t, __q<__detail::__captures_t>>;
477 
478       mutable __captures_t __impl_;
479 
480       template <class _Tag, class _Data, class... _Child>
STDEXEC_ATTRIBUTEstdexec::__anon673c7d0f0211::__sexpr481       STDEXEC_ATTRIBUTE(host, device, always_inline)
482       explicit __sexpr(_Tag, _Data&& __data, _Child&&... __child)
483         : __impl_(
484             __detail::__captures(
485               _Tag(),
486               static_cast<_Data&&>(__data),
487               static_cast<_Child&&>(__child)...)) {
488       }
489 
490       template <class _Self>
491       using __impl = __sexpr_impl<__meval<__msecond, _Self, __tag_t>>;
492 
493       template <class _Self = __sexpr>
STDEXEC_ATTRIBUTEstdexec::__anon673c7d0f0211::__sexpr494       STDEXEC_ATTRIBUTE(always_inline)
495       auto get_env() const noexcept
496         -> __result_of<__sexpr_apply, const _Self&, __get_attrs_fn<__tag_t>> {
497         return __sexpr_apply(*this, __detail::__drop_front(__impl<_Self>::get_attrs));
498       }
499 
500       template <__decays_to<__sexpr> _Self, class... _Env>
STDEXEC_ATTRIBUTEstdexec::__anon673c7d0f0211::__sexpr501       STDEXEC_ATTRIBUTE(always_inline)
502       static auto get_completion_signatures(_Self&&, _Env&&...) noexcept -> __msecond<
503         __if_c<__decays_to<_Self, __sexpr>>,
504         __result_of<__impl<_Self>::get_completion_signatures, _Self, _Env...>
505       > {
506         return {};
507       }
508 
509       // BUGBUG fix receiver constraint here:
510       template <__decays_to<__sexpr> _Self, /*receiver*/ class _Receiver>
STDEXEC_ATTRIBUTEstdexec::__anon673c7d0f0211::__sexpr511       STDEXEC_ATTRIBUTE(always_inline)
512       static auto connect(_Self&& __self, _Receiver&& __rcvr)
513         noexcept(__noexcept_of<__impl<_Self>::connect, _Self, _Receiver>) -> __msecond<
514           __if_c<__decays_to<_Self, __sexpr>>,
515           __result_of<__impl<_Self>::connect, _Self, _Receiver>
516         > {
517         return __impl<_Self>::connect(
518           static_cast<_Self&&>(__self), static_cast<_Receiver&&>(__rcvr));
519       }
520 
521       template <__decays_to<__sexpr> _Self, /*receiver*/ class _Receiver>
STDEXEC_ATTRIBUTEstdexec::__anon673c7d0f0211::__sexpr522       STDEXEC_ATTRIBUTE(always_inline)
523       static auto submit(_Self&& __self, _Receiver&& __rcvr)
524         noexcept(__noexcept_of<__impl<_Self>::submit, _Self, _Receiver>) -> __msecond<
525           __if_c<__decays_to<_Self, __sexpr>>,
526           __result_of<__impl<_Self>::submit, _Self, _Receiver>
527         > {
528         return __impl<_Self>::submit(
529           static_cast<_Self&&>(__self), static_cast<_Receiver&&>(__rcvr));
530       }
531 
532       template <class _Sender, class _ApplyFn>
STDEXEC_ATTRIBUTEstdexec::__anon673c7d0f0211::__sexpr533       STDEXEC_ATTRIBUTE(always_inline)
534       static auto apply(_Sender&& __sndr, _ApplyFn&& __fun) noexcept(
535         __nothrow_callable<__detail::__impl_of<_Sender>, __copy_cvref_fn<_Sender>, _ApplyFn>)
536         -> __call_result_t<__detail::__impl_of<_Sender>, __copy_cvref_fn<_Sender>, _ApplyFn> {
537         return static_cast<_Sender&&>(__sndr)
538           .__impl_(__copy_cvref_fn<_Sender>(), static_cast<_ApplyFn&&>(__fun));
539       }
540 
541       template <std::size_t _Idx, __decays_to_derived_from<__sexpr> _Self>
STDEXEC_ATTRIBUTE(always_inline)542       STDEXEC_ATTRIBUTE(always_inline)
543       friend auto get(_Self&& __self) noexcept -> decltype(auto)
544         requires __detail::__in_range<_Idx, __desc_t>
545       {
546         if constexpr (_Idx == 0) {
547           return __tag_t();
548         } else {
549           return __self.__impl_(__copy_cvref_fn<_Self>(), __nth_pack_element<_Idx>);
550         }
551       }
552     };
553 
554     template <class _Tag, class _Data, class... _Child>
555     STDEXEC_ATTRIBUTE(host, device)
556     __sexpr(_Tag, _Data, _Child...) -> __sexpr<STDEXEC_SEXPR_DESCRIPTOR(_Tag, _Data, _Child...)>;
557   } // namespace
558 
559   template <class _Tag, class _Data, class... _Child>
560   using __sexpr_t = __sexpr<STDEXEC_SEXPR_DESCRIPTOR(_Tag, _Data, _Child...)>;
561 
562   //////////////////////////////////////////////////////////////////////////////////////////////////
563   // __make_sexpr
564   //! A tagged function-object
565   //! Takes data and children and
566   //! returns `__sexpr_t<_Tag, _Data, _Child...>{_Tag(), data, children...}`.
567   namespace __detail {
568     template <class _Tag>
569     struct __make_sexpr_t {
570       template <class _Data = __, class... _Child>
operator ()stdexec::__detail::__make_sexpr_t571       constexpr auto operator()(_Data __data = {}, _Child... __child) const {
572         return __sexpr_t<_Tag, _Data, _Child...>{
573           _Tag(), static_cast<_Data&&>(__data), static_cast<_Child&&>(__child)...};
574       }
575     };
576   } // namespace __detail
577 
578   template <class _Tag>
579   inline constexpr __detail::__make_sexpr_t<_Tag> __make_sexpr{};
580 
581   // The __name_of utility defined below is used to pretty-print the type names of
582   // senders in compiler diagnostics.
583   namespace __detail {
584     struct __basic_sender_name {
585       template <class _Tag, class _Data, class... _Child>
586       using __result = __basic_sender<_Tag, _Data, __name_of<_Child>...>;
587 
588       template <class _Sender>
589       using __f = __minvoke<typename __decay_t<_Sender>::__desc_t, __q<__result>>;
590     };
591 
592     struct __id_name {
593       template <class _Sender>
594       using __f = __name_of<__id<_Sender>>;
595     };
596 
597     template <class _Sender>
598     extern __mcompose<__cplr, __name_of_fn<_Sender>> __name_of_v<_Sender&>;
599 
600     template <class _Sender>
601     extern __mcompose<__cprr, __name_of_fn<_Sender>> __name_of_v<_Sender&&>;
602 
603     template <class _Sender>
604     extern __mcompose<__cpclr, __name_of_fn<_Sender>> __name_of_v<const _Sender&>;
605 
606     template <auto _Descriptor>
607     extern __basic_sender_name __name_of_v<__sexpr<_Descriptor>>;
608 
609     template <__has_id _Sender>
610       requires(!same_as<__id<_Sender>, _Sender>)
611     extern __id_name __name_of_v<_Sender>;
612   } // namespace __detail
613 } // namespace stdexec
614 
615 namespace std {
616   template <auto _Descriptor>
617   struct tuple_size<stdexec::__sexpr<_Descriptor>>
618     : integral_constant<
619         size_t,
620         stdexec::__v<stdexec::__minvoke<stdexec::__result_of<_Descriptor>, stdexec::__msize>>
621       > { };
622 
623   template <size_t _Idx, auto _Descriptor>
624   struct tuple_element<_Idx, stdexec::__sexpr<_Descriptor>> {
625     using type = stdexec::__remove_rvalue_reference_t<stdexec::__call_result_t<
626       stdexec::__detail::__impl_of<stdexec::__sexpr<_Descriptor>>,
627       stdexec::__cp,
628       stdexec::__nth_pack_element_t<_Idx>
629     >>;
630   };
631 } // namespace std
632