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 "__concepts.hpp"
19 #include "__diagnostics.hpp"
20 #include "__env.hpp"
21 #include "__execution_fwd.hpp"
22 #include "__meta.hpp"
23 #include "__sender_introspection.hpp"
24 #include "__senders_core.hpp"
25 #include "__tuple.hpp"
26 #include "__type_traits.hpp"
27 
28 #include <cstddef>
29 #include <type_traits>
30 #include <utility> // for tuple_size/tuple_element
31 
32 namespace stdexec
33 {
34 /////////////////////////////////////////////////////////////////////////////
35 // Generic __sender type
36 namespace __detail
37 {
38 template <class _Sender>
39 using __impl_of = decltype((__declval<_Sender>().__impl_));
40 
41 struct __get_data
42 {
43     template <class _Data>
44     STDEXEC_ATTRIBUTE((always_inline))
operator ()stdexec::__detail::__get_data45     _Data&& operator()(__ignore, _Data&& __data, auto&&...) const noexcept
46     {
47         return static_cast<_Data&&>(__data);
48     }
49 };
50 } // namespace __detail
51 
52 namespace
53 {
54 template <class _Descriptor, auto _DescriptorFn = [] { return _Descriptor(); }>
55 inline constexpr auto __descriptor_fn_v = _DescriptorFn;
56 
57 template <class _Tag, class _Data, class... _Child>
__descriptor_fn()58 inline constexpr auto __descriptor_fn()
59 {
60     return __descriptor_fn_v<__detail::__desc<_Tag, _Data, _Child...>>;
61 }
62 } // namespace
63 
64 #if STDEXEC_NVHPC()
65 #define STDEXEC_SEXPR_DESCRIPTOR(_Tag, _Data, _Child)                          \
66     stdexec::__descriptor_fn<_Tag, _Data, _Child>()
67 #else
68 #define STDEXEC_SEXPR_DESCRIPTOR(_Tag, _Data, _Child)                          \
69     stdexec::__descriptor_fn_v<stdexec::__detail::__desc<_Tag, _Data, _Child>>
70 #endif
71 
72 template <class _Tag>
73 struct __sexpr_impl;
74 
75 namespace __detail
76 {
77 template <class _Sexpr, class _Receiver>
78 struct __op_state;
79 
80 template <class _Sexpr, class _Receiver>
81 struct __connect_fn;
82 
83 template <class _Tag, class _Sexpr, class _Receiver>
84 using __state_type_t =
85     __decay_t<__result_of<__sexpr_impl<_Tag>::get_state, _Sexpr, _Receiver&>>;
86 
87 template <class _Self, class _Tag, class _Index, class _Sexpr, class _Receiver>
88 using __env_type_t = __result_of<
89     __sexpr_impl<__meval<__msecond, _Self, _Tag>>::get_env, _Index,
90     __state_type_t<__meval<__msecond, _Self, _Tag>, _Sexpr, _Receiver>&,
91     _Receiver&>;
92 
93 template <class _Sexpr, class _Receiver>
94 concept __connectable =
95     __callable<__impl_of<_Sexpr>, __copy_cvref_fn<_Sexpr>,
96                __connect_fn<_Sexpr, _Receiver>> &&
97     __mvalid<__state_type_t, tag_of_t<_Sexpr>, _Sexpr, _Receiver>;
98 
99 // // Note: This is UB. UBSAN allows it for now.
100 // template <class _Parent, class _Child>
101 // _Parent* __parent_from_child(_Child* __child, _Child _Parent::*__mbr_ptr)
102 // noexcept {
103 //   alignas(_Parent) char __buf[sizeof(_Parent)];
104 //   _Parent* __parent = (_Parent*) &__buf;
105 //   const std::ptrdiff_t __offset = (char*) &(__parent->*__mbr_ptr) - __buf;
106 //   return (_Parent*) (static_cast<char*>(__child) - __offset);
107 // }
108 
109 inline constexpr auto __get_attrs = //
110     [](__ignore, const auto&... __child) noexcept -> decltype(auto) {
111     if constexpr (sizeof...(__child) == 1)
112     {
113         return stdexec::get_env(
114             __child...); // BUGBUG: should be only the forwarding queries
115     }
116     else
117     {
118         return empty_env();
119     }
120 };
121 
122 inline constexpr auto __get_env = //
123     []<class _Receiver>(__ignore, __ignore, const _Receiver& __rcvr) noexcept
124     -> env_of_t<const _Receiver&> { return stdexec::get_env(__rcvr); };
125 
126 inline constexpr auto __get_state = //
127     []<class _Sender>(_Sender&& __sndr, __ignore) noexcept -> decltype(auto) {
128     return __sndr.apply(static_cast<_Sender&&>(__sndr), __get_data());
129 };
130 
131 inline constexpr auto __connect =                                          //
132     []<class _Sender, class _Receiver>(_Sender&& __sndr, _Receiver __rcvr) //
133     noexcept(__nothrow_constructible_from<__op_state<_Sender, _Receiver>,
134                                           _Sender, _Receiver>)
135     -> __op_state<_Sender, _Receiver>
136     requires __connectable<_Sender, _Receiver>
137 {
138     return __op_state<_Sender, _Receiver>{static_cast<_Sender&&>(__sndr),
139                                           static_cast<_Receiver&&>(__rcvr)};
140 };
141 
142 inline constexpr auto __start = //
143     []<class _StartTag = start_t, class... _ChildOps>(
144         __ignore, __ignore, _ChildOps&... __ops) noexcept {
145         (_StartTag()(__ops), ...);
146     };
147 
148 inline constexpr auto __complete = //
149     []<class _Index, class _Receiver, class _SetTag, class... _Args>(
150         _Index, __ignore, _Receiver& __rcvr, _SetTag,
151         _Args&&... __args) noexcept {
152         static_assert(__v<_Index> == 0,
153                       "I don't know how to complete this operation.");
154         _SetTag()(std::move(__rcvr), static_cast<_Args&&>(__args)...);
155     };
156 
157 inline constexpr auto __sigs = //
158     []<class _Sender>(_Sender&& __sndr, __ignore = {}) noexcept {
159         static_assert(
160             __mnever<tag_of_t<_Sender>>,
161             "No customization of get_completion_signatures for this sender tag type.");
162     };
163 
164 template <class _ReceiverId, class _Sexpr, class _Idx>
165 struct __receiver
166 {
167     struct __t
168     {
169         using receiver_concept = receiver_t;
170         using _Receiver = stdexec::__t<_ReceiverId>;
171         using __sexpr = _Sexpr;
172         using __index = _Idx;
173         using __id = __receiver;
174         using __parent_op_t = __op_state<_Sexpr, _Receiver>;
175         using __tag_t = tag_of_t<_Sexpr>;
176 
177         // A pointer to the parent operation state, which contains the one
178         // created with this receiver.
179         __parent_op_t* __op_;
180 
181         // template <class _ChildSexpr, class _ChildReceiver>
182         // static __t __from_op_state(__op_state<_ChildSexpr, _ChildReceiver>*
183         // __child) noexcept {
184         //   using __parent_op_t = __op_state<_Sexpr, _Receiver>;
185         //   std::ptrdiff_t __offset = __parent_op_t::template
186         //   __get_child_op_offset<__v<_Idx>>();
187         //   __parent_op_t* __parent = (__parent_op_t*)
188         //   (static_cast<char*>(__child) - __offset); return __t{__parent};
189         // }
190 
191         template <class... _Args>
192         STDEXEC_ATTRIBUTE((always_inline))
set_valuestdexec::__detail::__receiver::__t193         void set_value(_Args&&... __args) noexcept
194         {
195             __op_->__complete(_Idx(), stdexec::set_value,
196                               static_cast<_Args&&>(__args)...);
197         }
198 
199         template <class _Error>
200         STDEXEC_ATTRIBUTE((always_inline))
set_errorstdexec::__detail::__receiver::__t201         void set_error(_Error&& __err) noexcept
202         {
203             __op_->__complete(_Idx(), stdexec::set_error,
204                               static_cast<_Error&&>(__err));
205         }
206 
207         STDEXEC_ATTRIBUTE((always_inline))
set_stoppedstdexec::__detail::__receiver::__t208         void set_stopped() noexcept
209         {
210             __op_->__complete(_Idx(), stdexec::set_stopped);
211         }
212 
213         template <__same_as<__t> _Self = __t>
214         STDEXEC_ATTRIBUTE((always_inline))
get_envstdexec::__detail::__receiver::__t215         auto get_env() const noexcept
216             -> __env_type_t<_Self, __tag_t, _Idx, _Sexpr, _Receiver>
217         {
218             return __op_->__get_env(_Idx());
219         }
220     };
221 };
222 
223 // template <class _Receiver>
224 // using __sexpr_connected_with = __mapply<
225 //   __mbind_front_q<__m_at, typename _Receiver::__index>,
226 //   typename __call_result_t<__impl_of<typename _Receiver::__sexpr>, __cp,
227 //   __get_desc>::__children>;
228 
229 template <class _Sexpr, class _Receiver>
230 struct __op_base : __immovable
231 {
232     using __tag_t = typename __decay_t<_Sexpr>::__tag_t;
233     using __state_t = __state_type_t<__tag_t, _Sexpr, _Receiver>;
234 
235     STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS _Receiver __rcvr_;
236     STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS __state_t __state_;
237 
__op_basestdexec::__detail::__op_base238     __op_base(_Sexpr&& __sndr, _Receiver&& __rcvr) //
239         noexcept(__nothrow_decay_copyable<_Receiver> &&
240                  __nothrow_move_constructible<__state_t>) :
241         __rcvr_(static_cast<_Receiver&&>(__rcvr)),
242         __state_(__sexpr_impl<__tag_t>::get_state(static_cast<_Sexpr&&>(__sndr),
243                                                   __rcvr_))
244     {}
245 
__rcvrstdexec::__detail::__op_base246     auto __rcvr() & noexcept -> _Receiver&
247     {
248         return __rcvr_;
249     }
250 
__rcvrstdexec::__detail::__op_base251     auto __rcvr() const& noexcept -> const _Receiver&
252     {
253         return __rcvr_;
254     }
255 };
256 
257 // template <class _Sexpr, class _Receiver>
258 //   requires __is_instance_of<__id<_Receiver>, __receiver>
259 //         && __decays_to<_Sexpr, __sexpr_connected_with<_Receiver>>
260 // struct __op_base<_Sexpr, _Receiver> : __immovable {
261 //   using __tag_t = typename __decay_t<_Sexpr>::__tag_t;
262 //   using __state_t = __state_type_t<__tag_t, _Sexpr, _Receiver>;
263 
264 //   STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS __state_t __state_;
265 
266 //   __op_base(_Sexpr&& __sndr, _Receiver&& __rcvr)
267 //     :
268 //     __state_(__sexpr_impl<__tag_t>::get_state(static_cast<_Sexpr&&>(__sndr),
269 //     __rcvr)) { STDEXEC_ASSERT(this->__rcvr().__op_ == __rcvr.__op_);
270 //   }
271 
272 //   _Receiver __rcvr() const noexcept {
273 //     return _Receiver::__from_op_state(             //
274 //       static_cast<__op_state<_Sexpr, _Receiver>*>( //
275 //         const_cast<__op_base*>(this)));
276 //   }
277 // };
278 
279 STDEXEC_PRAGMA_PUSH()
280 STDEXEC_PRAGMA_IGNORE_GNU("-Winvalid-offsetof")
281 STDEXEC_PRAGMA_IGNORE_EDG(offset_in_non_POD_nonstandard)
282 
283 template <class _Sexpr, class _Receiver>
284 struct __enable_receiver_from_this
285 {
286     using __op_base_t = __op_base<_Sexpr, _Receiver>;
287 
__receiverstdexec::__detail::__enable_receiver_from_this288     auto __receiver() noexcept -> decltype(auto)
289     {
290         using __derived_t = decltype(__op_base_t::__state_);
291         auto* __derived = static_cast<__derived_t*>(this);
292         constexpr std::size_t __offset = offsetof(__op_base_t, __state_);
293         auto* __base = reinterpret_cast<__op_base_t*>(
294             reinterpret_cast<char*>(__derived) - __offset);
295         return __base->__rcvr();
296     }
297 };
298 
299 STDEXEC_PRAGMA_POP()
300 
301 STDEXEC_PRAGMA_PUSH()
302 STDEXEC_PRAGMA_IGNORE_GNU("-Wmissing-braces")
303 
304 template <class _Sexpr, class _Receiver>
305 struct __connect_fn
306 {
307     template <std::size_t _Idx>
308     using __receiver_t =
309         __t<__receiver<__id<_Receiver>, _Sexpr, __msize_t<_Idx>>>;
310 
311     __op_state<_Sexpr, _Receiver>* __op_;
312 
313     struct __impl
314     {
315         __op_state<_Sexpr, _Receiver>* __op_;
316 
317         template <std::size_t... _Is, class... _Child>
operator ()stdexec::__detail::__connect_fn::__impl318         auto operator()(__indices<_Is...>, _Child&&... __child) const
319             noexcept((__nothrow_connectable<_Child, __receiver_t<_Is>> && ...))
320                 -> __tuple_for<connect_result_t<_Child, __receiver_t<_Is>>...>
321         {
322             return __tuple{connect(static_cast<_Child&&>(__child),
323                                    __receiver_t<_Is>{__op_})...};
324         }
325     };
326 
327     template <class... _Child>
operator ()stdexec::__detail::__connect_fn328     auto operator()(__ignore, __ignore, _Child&&... __child) const noexcept(
329         __nothrow_callable<__impl, __indices_for<_Child...>, _Child...>)
330         -> __call_result_t<__impl, __indices_for<_Child...>, _Child...>
331     {
332         return __impl{__op_}(__indices_for<_Child...>(),
333                              static_cast<_Child&&>(__child)...);
334     }
335 
operator ()stdexec::__detail::__connect_fn336     auto operator()(__ignore, __ignore) const noexcept -> __tuple_for<>
337     {
338         return {};
339     }
340 };
341 STDEXEC_PRAGMA_POP()
342 
343 template <class _Sexpr, class _Receiver>
344 struct __op_state : __op_base<_Sexpr, _Receiver>
345 {
346     using __desc_t = typename __decay_t<_Sexpr>::__desc_t;
347     using __tag_t = typename __desc_t::__tag;
348     using __data_t = typename __desc_t::__data;
349     // using __children_t = typename __desc_t::__children;
350     using __state_t = typename __op_state::__state_t;
351     using __inner_ops_t =
352         __result_of<__sexpr_apply, _Sexpr, __connect_fn<_Sexpr, _Receiver>>;
353 
354     __inner_ops_t __inner_ops_;
355 
356     // template <std::size_t _Idx>
357     // static std::ptrdiff_t __get_child_op_offset() noexcept {
358     //   __op_state* __self = (__op_state*) &__self;
359     //   return (std::ptrdiff_t)((char*) &__tup::get<_Idx>(__self->__inner_ops_)
360     //   - static_cast<char*>(__self));
361     // }
362 
__op_statestdexec::__detail::__op_state363     __op_state(_Sexpr&& __sexpr, _Receiver __rcvr) //
364         noexcept(__nothrow_constructible_from<__op_base<_Sexpr, _Receiver>,
365                                               _Sexpr&&, _Receiver&&> &&
366                  __nothrow_callable<__sexpr_apply_t, _Sexpr&&,
367                                     __connect_fn<_Sexpr, _Receiver>>) :
368         __op_state::__op_base{static_cast<_Sexpr&&>(__sexpr),
369                               static_cast<_Receiver&&>(__rcvr)},
370         __inner_ops_(__sexpr_apply(static_cast<_Sexpr&&>(__sexpr),
371                                    __connect_fn<_Sexpr, _Receiver>{this}))
372     {}
373 
374     STDEXEC_ATTRIBUTE((always_inline))
startstdexec::__detail::__op_state375     void start() & noexcept
376     {
377         using __tag_t = typename __op_state::__tag_t;
378         auto&& __rcvr = this->__rcvr();
379         __inner_ops_.apply(
380             [&](auto&... __ops) noexcept {
381                 __sexpr_impl<__tag_t>::start(this->__state_, __rcvr, __ops...);
382             },
383             __inner_ops_);
384     }
385 
386     template <class _Index, class _Tag2, class... _Args>
387     STDEXEC_ATTRIBUTE((always_inline))
__completestdexec::__detail::__op_state388     void __complete(_Index, _Tag2, _Args&&... __args) noexcept
389     {
390         using __tag_t = typename __op_state::__tag_t;
391         auto&& __rcvr = this->__rcvr();
392         using _CompleteFn = __mtypeof<__sexpr_impl<__tag_t>::complete>;
393         if constexpr (__callable<_CompleteFn, _Index, __op_state&, _Tag2,
394                                  _Args...>)
395         {
396             __sexpr_impl<__tag_t>::complete(_Index(), *this, _Tag2(),
397                                             static_cast<_Args&&>(__args)...);
398         }
399         else
400         {
401             __sexpr_impl<__tag_t>::complete(_Index(), this->__state_, __rcvr,
402                                             _Tag2(),
403                                             static_cast<_Args&&>(__args)...);
404         }
405     }
406 
407     template <class _Index>
408     STDEXEC_ATTRIBUTE((always_inline))
__get_envstdexec::__detail::__op_state409     auto __get_env(_Index) const noexcept
410         -> __env_type_t<_Index, __tag_t, _Index, _Sexpr, _Receiver>
411     {
412         const auto& __rcvr = this->__rcvr();
413         return __sexpr_impl<__tag_t>::get_env(_Index(), this->__state_, __rcvr);
414     }
415 };
416 
417 inline constexpr auto __drop_front = //
418     []<class _Fn>(_Fn __fn) noexcept {
419         return [__fn = std::move(__fn)]<class... _Rest>(auto&&,
420                                                         _Rest&&... __rest) //
421                noexcept(__nothrow_callable<const _Fn&, _Rest...>)
422                    -> __call_result_t<const _Fn&, _Rest...> {
423                    return __fn(static_cast<_Rest&&>(__rest)...);
424                };
425     };
426 
427 template <class _Tag, class... _Captures>
428 STDEXEC_ATTRIBUTE((host, device, always_inline))
__captures(_Tag,_Captures &&...__captures2)429 constexpr auto __captures(_Tag, _Captures&&... __captures2)
430 {
431     return
432         [... __captures3 = static_cast<_Captures&&>(
433              __captures2)]<class _Cvref, class _Fun>(_Cvref,
434                                                      _Fun&& __fun) mutable   //
435         noexcept(
436             __nothrow_callable<_Fun, _Tag, __minvoke<_Cvref, _Captures>...>) //
437         -> __call_result_t<_Fun, _Tag, __minvoke<_Cvref, _Captures>...>
438             requires __callable<_Fun, _Tag, __minvoke<_Cvref, _Captures>...>
439     {
440         // The use of decltype(__captures3) here instead of _Captures is a
441         // workaround for a codegen bug in nvc++.
442         return static_cast<_Fun&&>(__fun)(
443             _Tag(), const_cast<__minvoke<_Cvref, decltype(__captures3)>&&>(
444                         __captures3)...);
445     };
446 }
447 
448 template <class _Tag, class _Data, class... _Child>
449 using __captures_t = decltype(__detail::__captures(_Tag(), __declval<_Data>(),
450                                                    __declval<_Child>()...));
451 
452 template <class, class, class... _Child>
453 using __tuple_size_t = char[sizeof...(_Child) + 2];
454 
455 template <std::size_t _Idx, class _Descriptor>
456 concept __in_range =
457     (_Idx < sizeof(__minvoke<_Descriptor, __q<__tuple_size_t>>));
458 
459 } // namespace __detail
460 
461 struct __sexpr_defaults
462 {
463     static constexpr auto get_attrs = __detail::__get_attrs;
464     static constexpr auto get_env = __detail::__get_env;
465     static constexpr auto get_state = __detail::__get_state;
466     static constexpr auto connect = __detail::__connect;
467     static constexpr auto start = __detail::__start;
468     static constexpr auto complete = __detail::__complete;
469     static constexpr auto get_completion_signatures = __detail::__sigs;
470 };
471 
472 template <class _Tag>
473 struct __sexpr_impl : __sexpr_defaults
474 {
475     using not_specialized = void;
476 };
477 
478 using __detail::__enable_receiver_from_this;
479 
480 template <class _Tag>
481 using __get_attrs_fn = __result_of<__detail::__drop_front,
482                                    __mtypeof<__sexpr_impl<_Tag>::get_attrs>>;
483 
484 //////////////////////////////////////////////////////////////////////////////////////////////////
485 // __basic_sender
486 template <class...>
487 struct __basic_sender
488 {
489     using __id = __basic_sender;
490     using __t = __basic_sender;
491 };
492 
493 template <auto _DescriptorFn, class = __anon>
494 struct __sexpr
495 {
496     using sender_concept = sender_t;
497 
498     using __id = __sexpr;
499     using __t = __sexpr;
500     using __desc_t = decltype(_DescriptorFn());
501     using __tag_t = typename __desc_t::__tag;
502     using __captures_t = __minvoke<__desc_t, __q<__detail::__captures_t>>;
503 
504     mutable __captures_t __impl_;
505 
506     template <class _Tag, class _Data, class... _Child>
507     STDEXEC_ATTRIBUTE((host, device, always_inline))
__sexprstdexec::__sexpr508     explicit __sexpr(_Tag, _Data&& __data, _Child&&... __child) :
509         __impl_(__detail::__captures(_Tag(), static_cast<_Data&&>(__data),
510                                      static_cast<_Child&&>(__child)...))
511     {}
512 
513     template <class _Self>
514     using __impl = __sexpr_impl<__meval<__msecond, _Self, __tag_t>>;
515 
516     template <class _Self = __sexpr>
517     STDEXEC_ATTRIBUTE((always_inline))
get_envstdexec::__sexpr518     auto get_env() const noexcept
519         -> __result_of<__sexpr_apply, const _Self&, __get_attrs_fn<__tag_t>>
520     {
521         return __sexpr_apply(*this,
522                              __detail::__drop_front(__impl<_Self>::get_attrs));
523     }
524 
525     template <__decays_to<__sexpr> _Self, class... _Env>
526     STDEXEC_ATTRIBUTE((always_inline))
get_completion_signaturesstdexec::__sexpr527     static auto get_completion_signatures(_Self&&, _Env&&...) noexcept //
528         -> __msecond<__if_c<__decays_to<_Self, __sexpr>>,
529                      __result_of<__impl<_Self>::get_completion_signatures,
530                                  _Self, _Env...>>
531     {
532         return {};
533     }
534 
535     // BUGBUG fix receiver constraint here:
536     template <__decays_to<__sexpr> _Self, /*receiver*/ class _Receiver>
537     STDEXEC_ATTRIBUTE((always_inline))
connectstdexec::__sexpr538     static auto connect(_Self&& __self, _Receiver&& __rcvr)               //
539         noexcept(__noexcept_of<__impl<_Self>::connect, _Self, _Receiver>) //
540         -> __msecond<__if_c<__decays_to<_Self, __sexpr>>,
541                      __result_of<__impl<_Self>::connect, _Self, _Receiver>>
542     {
543         return __impl<_Self>::connect(static_cast<_Self&&>(__self),
544                                       static_cast<_Receiver&&>(__rcvr));
545     }
546 
547     template <class _Sender, class _ApplyFn>
548     STDEXEC_ATTRIBUTE((always_inline))
applystdexec::__sexpr549     static auto apply(_Sender&& __sndr, _ApplyFn&& __fun)                //
550         noexcept(__nothrow_callable<__detail::__impl_of<_Sender>,
551                                     __copy_cvref_fn<_Sender>, _ApplyFn>) //
552         -> __call_result_t<__detail::__impl_of<_Sender>,
553                            __copy_cvref_fn<_Sender>, _ApplyFn>
554     {                                                                    //
555         return static_cast<_Sender&&>(__sndr).__impl_(
556             __copy_cvref_fn<_Sender>(), static_cast<_ApplyFn&&>(__fun)); //
557     }
558 
559     template <std::size_t _Idx, __decays_to_derived_from<__sexpr> _Self>
560     STDEXEC_ATTRIBUTE((always_inline))
get(_Self && __self)561     friend auto get(_Self&& __self) noexcept -> decltype(auto)
562         requires __detail::__in_range<_Idx, __desc_t>
563     {
564         if constexpr (_Idx == 0)
565         {
566             return __tag_t();
567         }
568         else
569         {
570             return __self.__impl_(__copy_cvref_fn<_Self>(),
571                                   __nth_pack_element<_Idx>);
572         }
573     }
574 };
575 
576 template <class _Tag, class _Data, class... _Child>
577 STDEXEC_ATTRIBUTE((host, device))
578 __sexpr(_Tag, _Data,
579         _Child...) -> __sexpr<STDEXEC_SEXPR_DESCRIPTOR(_Tag, _Data, _Child...)>;
580 
581 template <class _Tag, class _Data, class... _Child>
582 using __sexpr_t = __sexpr<STDEXEC_SEXPR_DESCRIPTOR(_Tag, _Data, _Child...)>;
583 
584 //////////////////////////////////////////////////////////////////////////////////////////////////
585 // __make_sexpr
586 namespace __detail
587 {
588 template <class _Tag>
589 struct __make_sexpr_t
590 {
591     template <class _Data = __, class... _Child>
operator ()stdexec::__detail::__make_sexpr_t592     constexpr auto operator()(_Data __data = {}, _Child... __child) const
593     {
594         return __sexpr_t<_Tag, _Data, _Child...>{
595             _Tag(), static_cast<_Data&&>(__data),
596             static_cast<_Child&&>(__child)...};
597     }
598 };
599 } // namespace __detail
600 
601 template <class _Tag>
602 inline constexpr __detail::__make_sexpr_t<_Tag> __make_sexpr{};
603 
604 // The __name_of utility defined below is used to pretty-print the type names of
605 // senders in compiler diagnostics.
606 namespace __detail
607 {
608 struct __basic_sender_name
609 {
610     template <class _Tag, class _Data, class... _Child>
611     using __result = __basic_sender<_Tag, _Data, __name_of<_Child>...>;
612 
613     template <class _Sender>
614     using __f = __minvoke<typename __decay_t<_Sender>::__desc_t, __q<__result>>;
615 };
616 
617 struct __id_name
618 {
619     template <class _Sender>
620     using __f = __name_of<__id<_Sender>>;
621 };
622 
623 template <class _Sender>
624 extern __mcompose<__cplr, __name_of_fn<_Sender>> __name_of_v<_Sender&>;
625 
626 template <class _Sender>
627 extern __mcompose<__cprr, __name_of_fn<_Sender>> __name_of_v<_Sender&&>;
628 
629 template <class _Sender>
630 extern __mcompose<__cpclr, __name_of_fn<_Sender>> __name_of_v<const _Sender&>;
631 
632 template <auto _Descriptor>
633 extern __basic_sender_name __name_of_v<__sexpr<_Descriptor>>;
634 
635 template <__has_id _Sender>
636     requires(!same_as<__id<_Sender>, _Sender>)
637 extern __id_name __name_of_v<_Sender>;
638 } // namespace __detail
639 } // namespace stdexec
640 
641 namespace std
642 {
643 template <auto _Descriptor>
644 struct tuple_size<stdexec::__sexpr<_Descriptor>> :
645     integral_constant<size_t,
646                       stdexec::__v<stdexec::__minvoke<
647                           stdexec::__result_of<_Descriptor>, stdexec::__msize>>>
648 {};
649 
650 template <size_t _Idx, auto _Descriptor>
651 struct tuple_element<_Idx, stdexec::__sexpr<_Descriptor>>
652 {
653     using type =                              //
654         stdexec::__remove_rvalue_reference_t< //
655             stdexec::__call_result_t<         //
656                 stdexec::__detail::__impl_of<stdexec::__sexpr<_Descriptor>>,
657                 stdexec::__cp, stdexec::__nth_pack_element_t<_Idx>>>;
658 };
659 } // namespace std
660