xref: /openbmc/sdbusplus/include/sdbusplus/async/stdexec/__detail/__basic_sender.hpp (revision 36137e09614746b13603b5fbae79e6f70819c46b)
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 "__manual_lifetime.hpp"
23 #include "__meta.hpp"
24 #include "__sender_introspection.hpp"
25 #include "__senders_core.hpp"
26 #include "__tuple.hpp"
27 #include "__type_traits.hpp"
28 
29 #include <cstddef>
30 #include <type_traits>
31 #include <utility> // for tuple_size/tuple_element
32 
33 namespace stdexec
34 {
35 /////////////////////////////////////////////////////////////////////////////
36 // Generic __sender type
37 namespace __detail
38 {
39 template <class _Sender>
40 using __impl_of = decltype((__declval<_Sender>().__impl_));
41 
42 struct __get_data
43 {
44     template <class _Data>
45     STDEXEC_ATTRIBUTE((always_inline))
operator ()stdexec::__detail::__get_data46     _Data&& operator()(__ignore, _Data&& __data, auto&&...) const noexcept
47     {
48         return static_cast<_Data&&>(__data);
49     }
50 };
51 } // namespace __detail
52 
53 namespace
54 {
55 template <class _Descriptor, auto _DescriptorFn = [] { return _Descriptor(); }>
56 inline constexpr auto __descriptor_fn_v = _DescriptorFn;
57 
58 template <class _Tag, class _Data, class... _Child>
__descriptor_fn()59 inline constexpr auto __descriptor_fn()
60 {
61     return __descriptor_fn_v<__detail::__desc<_Tag, _Data, _Child...>>;
62 }
63 } // namespace
64 
65 #if STDEXEC_EDG()
66 #define STDEXEC_SEXPR_DESCRIPTOR(_Tag, _Data, _Child)                          \
67     stdexec::__descriptor_fn<_Tag, _Data, _Child>()
68 #else
69 #define STDEXEC_SEXPR_DESCRIPTOR(_Tag, _Data, _Child)                          \
70     stdexec::__descriptor_fn_v<stdexec::__detail::__desc<_Tag, _Data, _Child>>
71 #endif
72 
73 template <class _Tag>
74 struct __sexpr_impl;
75 
76 namespace __detail
77 {
78 template <class _Sexpr, class _Receiver>
79 struct __op_state;
80 
81 template <class _Sexpr, class _Receiver>
82 struct __connect_fn;
83 
84 template <class _Tag, class _Sexpr, class _Receiver>
85 using __state_type_t =
86     __decay_t<__result_of<__sexpr_impl<_Tag>::get_state, _Sexpr, _Receiver&>>;
87 
88 template <class _Self, class _Tag, class _Index, class _Sexpr, class _Receiver>
89 using __env_type_t = __result_of<
90     __sexpr_impl<__meval<__msecond, _Self, _Tag>>::get_env, _Index,
91     __state_type_t<__meval<__msecond, _Self, _Tag>, _Sexpr, _Receiver>&,
92     _Receiver&>;
93 
94 template <class _Sexpr, class _Receiver>
95 concept __connectable =
96     __callable<__impl_of<_Sexpr>, __copy_cvref_fn<_Sexpr>,
97                __connect_fn<_Sexpr, _Receiver>> &&
98     __mvalid<__state_type_t, tag_of_t<_Sexpr>, _Sexpr, _Receiver>;
99 
100 // // Note: This is UB. UBSAN allows it for now.
101 // template <class _Parent, class _Child>
102 // _Parent* __parent_from_child(_Child* __child, _Child _Parent::*__mbr_ptr)
103 // noexcept {
104 //   alignas(_Parent) char __buf[sizeof(_Parent)];
105 //   _Parent* __parent = (_Parent*) &__buf;
106 //   const std::ptrdiff_t __offset = (char*) &(__parent->*__mbr_ptr) - __buf;
107 //   return (_Parent*) (static_cast<char*>(__child) - __offset);
108 // }
109 
110 inline constexpr auto __get_attrs = //
111     [](__ignore, const auto&... __child) noexcept -> decltype(auto) {
112     if constexpr (sizeof...(__child) == 1)
113     {
114         return stdexec::get_env(
115             __child...); // BUGBUG: should be only the forwarding queries
116     }
117     else
118     {
119         return empty_env();
120     }
121 };
122 
123 inline constexpr auto __get_env = //
124     []<class _Receiver>(__ignore, __ignore, const _Receiver& __rcvr) noexcept
125     -> env_of_t<const _Receiver&> { return stdexec::get_env(__rcvr); };
126 
127 inline constexpr auto __get_state = //
128     []<class _Sender>(_Sender&& __sndr, __ignore) noexcept -> decltype(auto) {
129     return __sndr.apply(static_cast<_Sender&&>(__sndr), __get_data());
130 };
131 
132 inline constexpr auto __connect =                                          //
133     []<class _Sender, class _Receiver>(_Sender&& __sndr, _Receiver __rcvr) //
134     noexcept(__nothrow_constructible_from<__op_state<_Sender, _Receiver>,
135                                           _Sender, _Receiver>)
136     -> __op_state<_Sender, _Receiver>
137     requires __connectable<_Sender, _Receiver>
138 {
139     return __op_state<_Sender, _Receiver>{static_cast<_Sender&&>(__sndr),
140                                           static_cast<_Receiver&&>(__rcvr)};
141 };
142 
143 inline constexpr auto __start = //
144     []<class _StartTag = start_t, class... _ChildOps>(
145         __ignore, __ignore, _ChildOps&... __ops) noexcept {
146         (_StartTag()(__ops), ...);
147     };
148 
149 inline constexpr auto __complete = //
150     []<class _Index, class _Receiver, class _SetTag, class... _Args>(
151         _Index, __ignore, _Receiver& __rcvr, _SetTag,
152         _Args&&... __args) noexcept {
153         static_assert(__v<_Index> == 0,
154                       "I don't know how to complete this operation.");
155         _SetTag()(std::move(__rcvr), static_cast<_Args&&>(__args)...);
156     };
157 
158 inline constexpr auto __sigs = //
159     []<class _Sender>(_Sender&& __sndr, __ignore = {}) noexcept {
160         static_assert(
161             __mnever<tag_of_t<_Sender>>,
162             "No customization of get_completion_signatures for this sender tag type.");
163     };
164 
165 template <class _ReceiverId, class _Sexpr, class _Idx>
166 struct __receiver
167 {
168     struct __t
169     {
170         using receiver_concept = receiver_t;
171         using _Receiver = stdexec::__t<_ReceiverId>;
172         using __sexpr = _Sexpr;
173         using __index = _Idx;
174         using __id = __receiver;
175         using __parent_op_t = __op_state<_Sexpr, _Receiver>;
176         using __tag_t = tag_of_t<_Sexpr>;
177 
178         // A pointer to the parent operation state, which contains the one
179         // created with this receiver.
180         __parent_op_t* __op_;
181 
182         // template <class _ChildSexpr, class _ChildReceiver>
183         // static __t __from_op_state(__op_state<_ChildSexpr, _ChildReceiver>*
184         // __child) noexcept {
185         //   using __parent_op_t = __op_state<_Sexpr, _Receiver>;
186         //   std::ptrdiff_t __offset = __parent_op_t::template
187         //   __get_child_op_offset<__v<_Idx>>();
188         //   __parent_op_t* __parent = (__parent_op_t*)
189         //   (static_cast<char*>(__child) - __offset); return __t{__parent};
190         // }
191 
192         template <class... _Args>
193         STDEXEC_ATTRIBUTE((always_inline))
set_valuestdexec::__detail::__receiver::__t194         void set_value(_Args&&... __args) noexcept
195         {
196             __op_->__complete(_Idx(), stdexec::set_value,
197                               static_cast<_Args&&>(__args)...);
198         }
199 
200         template <class _Error>
201         STDEXEC_ATTRIBUTE((always_inline))
set_errorstdexec::__detail::__receiver::__t202         void set_error(_Error&& __err) noexcept
203         {
204             __op_->__complete(_Idx(), stdexec::set_error,
205                               static_cast<_Error&&>(__err));
206         }
207 
208         STDEXEC_ATTRIBUTE((always_inline))
set_stoppedstdexec::__detail::__receiver::__t209         void set_stopped() noexcept
210         {
211             __op_->__complete(_Idx(), stdexec::set_stopped);
212         }
213 
214         template <__same_as<__t> _Self = __t>
215         STDEXEC_ATTRIBUTE((always_inline))
get_envstdexec::__detail::__receiver::__t216         auto get_env() const noexcept
217             -> __env_type_t<_Self, __tag_t, _Idx, _Sexpr, _Receiver>
218         {
219             return __op_->__get_env(_Idx());
220         }
221     };
222 };
223 
224 // template <class _Receiver>
225 // using __sexpr_connected_with = __mapply<
226 //   __mbind_front_q<__m_at, typename _Receiver::__index>,
227 //   typename __call_result_t<__impl_of<typename _Receiver::__sexpr>, __cp,
228 //   __get_desc>::__children>;
229 
230 template <class _Sexpr, class _Receiver>
231 using __state_t = //
232     __state_type_t<typename __decay_t<_Sexpr>::__tag_t, _Sexpr, _Receiver>;
233 
234 template <class _Sexpr, class _Receiver>
235 struct __op_base;
236 
237 template <class _Receiver>
238 struct __receiver_box
239 {
240     _Receiver __rcvr_;
241 
242     STDEXEC_ATTRIBUTE((always_inline))
__rcvrstdexec::__detail::__receiver_box243     auto __rcvr() & noexcept -> _Receiver&
244     {
245         return this->__rcvr_;
246     }
247 
248     STDEXEC_ATTRIBUTE((always_inline))
__rcvrstdexec::__detail::__receiver_box249     auto __rcvr() const& noexcept -> const _Receiver&
250     {
251         return this->__rcvr_;
252     }
253 };
254 
255 template <class _Sexpr, class _Receiver>
256 struct __state_box
257 {
258     using __tag_t = typename __decay_t<_Sexpr>::__tag_t;
259     using __state_t = __state_type_t<__tag_t, _Sexpr, _Receiver>;
260 
__state_boxstdexec::__detail::__state_box261     __state_box(_Sexpr&& __sndr, _Receiver& __rcvr) //
262         noexcept(__nothrow_callable<decltype(__sexpr_impl<__tag_t>::get_state),
263                                     _Sexpr, _Receiver>) :
264         __state_(__sexpr_impl<__tag_t>::get_state(static_cast<_Sexpr&&>(__sndr),
265                                                   __rcvr))
266     {}
267 
268     __state_t __state_;
269 };
270 
271 template <class _Sexpr, class _Receiver, class _State>
272 struct __enable_receiver_from_this
273 {
274 #if STDEXEC_HAS_FEATURE(undefined_behavior_sanitizer) && STDEXEC_CLANG()
275     // See https://github.com/llvm/llvm-project/issues/101276
276     [[clang::noinline]]
277 #endif
__receiverstdexec::__detail::__enable_receiver_from_this278     auto __receiver() noexcept -> decltype(auto)
279     {
280         void* __state = static_cast<_State*>(this);
281         auto* __sbox = static_cast<__state_box<_Sexpr, _Receiver>*>(__state);
282         return (static_cast<__op_base<_Sexpr, _Receiver>*>(__sbox)->__rcvr_);
283     }
284 };
285 
286 template <class _Sexpr, class _Receiver>
287 concept __state_uses_receiver = //
288     derived_from<__state_t<_Sexpr, _Receiver>,
289                  __enable_receiver_from_this<_Sexpr, _Receiver,
290                                              __state_t<_Sexpr, _Receiver>>>;
291 
292 template <class _Sexpr, class _Receiver>
293 struct __op_base : __immovable
294 {
295     using __tag_t = typename __decay_t<_Sexpr>::__tag_t;
296     using __state_t = __state_type_t<__tag_t, _Sexpr, _Receiver>;
297 
298     STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS
299     _Receiver __rcvr_;
300     STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS
301     __state_t __state_;
302 
__op_basestdexec::__detail::__op_base303     __op_base(_Sexpr&& __sndr, _Receiver&& __rcvr) //
304         noexcept(__nothrow_decay_copyable<_Receiver> &&
305                  __nothrow_callable<decltype(__sexpr_impl<__tag_t>::get_state),
306                                     _Sexpr, _Receiver>) :
307         __rcvr_(static_cast<_Receiver&&>(__rcvr)),
308         __state_(__sexpr_impl<__tag_t>::get_state(static_cast<_Sexpr&&>(__sndr),
309                                                   __rcvr_))
310     {}
311 
312     STDEXEC_ATTRIBUTE((always_inline))
__rcvrstdexec::__detail::__op_base313     auto __rcvr() & noexcept -> _Receiver&
314     {
315         return __rcvr_;
316     }
317 
318     STDEXEC_ATTRIBUTE((always_inline))
__rcvrstdexec::__detail::__op_base319     auto __rcvr() const& noexcept -> const _Receiver&
320     {
321         return __rcvr_;
322     }
323 };
324 
325 template <class _Sexpr, class _Receiver>
326     requires __state_uses_receiver<_Sexpr, _Receiver>
327 struct __op_base<_Sexpr, _Receiver> :
328     __receiver_box<_Receiver>,
329     __state_box<_Sexpr, _Receiver>
330 {
331     using __tag_t = typename __decay_t<_Sexpr>::__tag_t;
332     using __state_t = __state_type_t<__tag_t, _Sexpr, _Receiver>;
333 
334     STDEXEC_IMMOVABLE(__op_base);
335 
__op_basestdexec::__detail::__op_base336     __op_base(_Sexpr&& __sndr, _Receiver&& __rcvr) //
337         noexcept(__nothrow_decay_copyable<_Receiver> &&
338                  __nothrow_move_constructible<__state_t>) :
339         __receiver_box<_Receiver>{static_cast<_Receiver&&>(__rcvr)},
340         __state_box<_Sexpr, _Receiver>{static_cast<_Sexpr&&>(__sndr),
341                                        this->__rcvr_}
342     {}
343 };
344 
345 // template <class _Sexpr, class _Receiver>
346 //   requires __is_instance_of<__id<_Receiver>, __receiver>
347 //         && __decays_to<_Sexpr, __sexpr_connected_with<_Receiver>>
348 // struct __op_base<_Sexpr, _Receiver> : __immovable {
349 //   using __tag_t = typename __decay_t<_Sexpr>::__tag_t;
350 //   using __state_t = __state_type_t<__tag_t, _Sexpr, _Receiver>;
351 
352 //   STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS __state_t __state_;
353 
354 //   __op_base(_Sexpr&& __sndr, _Receiver&& __rcvr)
355 //     :
356 //     __state_(__sexpr_impl<__tag_t>::get_state(static_cast<_Sexpr&&>(__sndr),
357 //     __rcvr)) { STDEXEC_ASSERT(this->__rcvr().__op_ == __rcvr.__op_);
358 //   }
359 
360 //   _Receiver __rcvr() const noexcept {
361 //     return _Receiver::__from_op_state(             //
362 //       static_cast<__op_state<_Sexpr, _Receiver>*>( //
363 //         const_cast<__op_base*>(this)));
364 //   }
365 // };
366 
367 STDEXEC_PRAGMA_PUSH()
368 STDEXEC_PRAGMA_IGNORE_GNU("-Wmissing-braces")
369 
370 template <class _Sexpr, class _Receiver>
371 struct __connect_fn
372 {
373     template <std::size_t _Idx>
374     using __receiver_t =
375         __t<__receiver<__id<_Receiver>, _Sexpr, __msize_t<_Idx>>>;
376 
377     __op_state<_Sexpr, _Receiver>* __op_;
378 
379     struct __impl
380     {
381         __op_state<_Sexpr, _Receiver>* __op_;
382 
383         template <std::size_t... _Is, class... _Child>
operator ()stdexec::__detail::__connect_fn::__impl384         auto operator()(__indices<_Is...>, _Child&&... __child) const
385             noexcept((__nothrow_connectable<_Child, __receiver_t<_Is>> && ...))
386                 -> __tuple_for<connect_result_t<_Child, __receiver_t<_Is>>...>
387         {
388             return __tuple{connect(static_cast<_Child&&>(__child),
389                                    __receiver_t<_Is>{__op_})...};
390         }
391     };
392 
393     template <class... _Child>
operator ()stdexec::__detail::__connect_fn394     auto operator()(__ignore, __ignore, _Child&&... __child) const noexcept(
395         __nothrow_callable<__impl, __indices_for<_Child...>, _Child...>)
396         -> __call_result_t<__impl, __indices_for<_Child...>, _Child...>
397     {
398         return __impl{__op_}(__indices_for<_Child...>(),
399                              static_cast<_Child&&>(__child)...);
400     }
401 
operator ()stdexec::__detail::__connect_fn402     auto operator()(__ignore, __ignore) const noexcept -> __tuple_for<>
403     {
404         return {};
405     }
406 };
407 STDEXEC_PRAGMA_POP()
408 
409 template <class _Sexpr, class _Receiver>
410 struct __op_state : __op_base<_Sexpr, _Receiver>
411 {
412     using __desc_t = typename __decay_t<_Sexpr>::__desc_t;
413     using __tag_t = typename __desc_t::__tag;
414     using __data_t = typename __desc_t::__data;
415     // using __children_t = typename __desc_t::__children;
416     using __state_t = typename __op_state::__state_t;
417     using __inner_ops_t =
418         __result_of<__sexpr_apply, _Sexpr, __connect_fn<_Sexpr, _Receiver>>;
419 
420     __inner_ops_t __inner_ops_;
421 
422     // template <std::size_t _Idx>
423     // static std::ptrdiff_t __get_child_op_offset() noexcept {
424     //   __op_state* __self = (__op_state*) &__self;
425     //   return (std::ptrdiff_t)((char*) &__tup::get<_Idx>(__self->__inner_ops_)
426     //   - static_cast<char*>(__self));
427     // }
428 
__op_statestdexec::__detail::__op_state429     __op_state(_Sexpr&& __sexpr, _Receiver __rcvr) //
430         noexcept(__nothrow_constructible_from<__op_base<_Sexpr, _Receiver>,
431                                               _Sexpr&&, _Receiver&&> &&
432                  __nothrow_callable<__sexpr_apply_t, _Sexpr&&,
433                                     __connect_fn<_Sexpr, _Receiver>>) :
434         __op_state::__op_base{static_cast<_Sexpr&&>(__sexpr),
435                               static_cast<_Receiver&&>(__rcvr)},
436         __inner_ops_(__sexpr_apply(static_cast<_Sexpr&&>(__sexpr),
437                                    __connect_fn<_Sexpr, _Receiver>{this}))
438     {}
439 
440     STDEXEC_ATTRIBUTE((always_inline))
startstdexec::__detail::__op_state441     void start() & noexcept
442     {
443         using __tag_t = typename __op_state::__tag_t;
444         auto&& __rcvr = this->__rcvr();
445         __inner_ops_.apply(
446             [&](auto&... __ops) noexcept {
447                 __sexpr_impl<__tag_t>::start(this->__state_, __rcvr, __ops...);
448             },
449             __inner_ops_);
450     }
451 
452     template <class _Index, class _Tag2, class... _Args>
453     STDEXEC_ATTRIBUTE((always_inline))
__completestdexec::__detail::__op_state454     void __complete(_Index, _Tag2, _Args&&... __args) noexcept
455     {
456         using __tag_t = typename __op_state::__tag_t;
457         auto&& __rcvr = this->__rcvr();
458         using _CompleteFn = __mtypeof<__sexpr_impl<__tag_t>::complete>;
459         if constexpr (__callable<_CompleteFn, _Index, __op_state&, _Tag2,
460                                  _Args...>)
461         {
462             __sexpr_impl<__tag_t>::complete(_Index(), *this, _Tag2(),
463                                             static_cast<_Args&&>(__args)...);
464         }
465         else
466         {
467             __sexpr_impl<__tag_t>::complete(_Index(), this->__state_, __rcvr,
468                                             _Tag2(),
469                                             static_cast<_Args&&>(__args)...);
470         }
471     }
472 
473     template <class _Index>
474     STDEXEC_ATTRIBUTE((always_inline))
__get_envstdexec::__detail::__op_state475     auto __get_env(_Index) const noexcept
476         -> __env_type_t<_Index, __tag_t, _Index, _Sexpr, _Receiver>
477     {
478         const auto& __rcvr = this->__rcvr();
479         return __sexpr_impl<__tag_t>::get_env(_Index(), this->__state_, __rcvr);
480     }
481 };
482 
483 inline constexpr auto __drop_front = //
484     []<class _Fn>(_Fn __fn) noexcept {
485         return [__fn = std::move(__fn)]<class... _Rest>(auto&&,
486                                                         _Rest&&... __rest) //
487                noexcept(__nothrow_callable<const _Fn&, _Rest...>)
488                    -> __call_result_t<const _Fn&, _Rest...> {
489                    return __fn(static_cast<_Rest&&>(__rest)...);
490                };
491     };
492 
493 template <class _Tag, class... _Captures>
494 STDEXEC_ATTRIBUTE((host, device, always_inline))
__captures(_Tag,_Captures &&...__captures2)495 constexpr auto __captures(_Tag, _Captures&&... __captures2)
496 {
497     return
498         [... __captures3 =
499              static_cast<_Captures&&>(__captures2)]<class _Cvref, class _Fun>(
500             _Cvref, _Fun&& __fun) mutable                                    //
501         noexcept(
502             __nothrow_callable<_Fun, _Tag, __minvoke<_Cvref, _Captures>...>) //
503         -> __call_result_t<_Fun, _Tag, __minvoke<_Cvref, _Captures>...>
504             requires __callable<_Fun, _Tag, __minvoke<_Cvref, _Captures>...>
505     {
506         // The use of decltype(__captures3) here instead of _Captures is a
507         // workaround for a codegen bug in nvc++.
508         return static_cast<_Fun&&>(__fun)(
509             _Tag(), const_cast<__minvoke<_Cvref, decltype(__captures3)>&&>(
510                         __captures3)...);
511     };
512 }
513 
514 template <class _Tag, class _Data, class... _Child>
515 using __captures_t = decltype(__detail::__captures(_Tag(), __declval<_Data>(),
516                                                    __declval<_Child>()...));
517 
518 template <class, class, class... _Child>
519 using __tuple_size_t = char[sizeof...(_Child) + 2];
520 
521 template <std::size_t _Idx, class _Descriptor>
522 concept __in_range =
523     (_Idx < sizeof(__minvoke<_Descriptor, __q<__tuple_size_t>>));
524 
525 } // namespace __detail
526 
527 struct __sexpr_defaults
528 {
529     static constexpr auto get_attrs = __detail::__get_attrs;
530     static constexpr auto get_env = __detail::__get_env;
531     static constexpr auto get_state = __detail::__get_state;
532     static constexpr auto connect = __detail::__connect;
533     static constexpr auto start = __detail::__start;
534     static constexpr auto complete = __detail::__complete;
535     static constexpr auto get_completion_signatures = __detail::__sigs;
536 };
537 
538 template <class _Tag>
539 struct __sexpr_impl : __sexpr_defaults
540 {
541     using not_specialized = void;
542 };
543 
544 using __detail::__enable_receiver_from_this;
545 
546 template <class _Tag>
547 using __get_attrs_fn = __result_of<__detail::__drop_front,
548                                    __mtypeof<__sexpr_impl<_Tag>::get_attrs>>;
549 
550 //! A dummy type used only for diagnostic purposes.
551 //! See `__sexpr` for the implementation of P2300's _`basic-sender`_.
552 template <class...>
553 struct __basic_sender
554 {
555     // See MAINTAINERS.md#class-template-parameters for `__id` and `__t`.
556     using __id = __basic_sender;
557     using __t = __basic_sender;
558 };
559 
560 //! A struct template to aid in creating senders.
561 //! This struct closely resembles P2300's
562 //! [_`basic-sender`_](https://eel.is/c++draft/exec#snd.expos-24), but is not an
563 //! exact implementation. Note: The struct named `__basic_sender` is just a
564 //! dummy type and is also not _`basic-sender`_.
565 template <auto _DescriptorFn, class = __anon>
566 struct __sexpr
567 {
568     using sender_concept = sender_t;
569 
570     // See MAINTAINERS.md#class-template-parameters for `__id` and `__t`.
571     using __id = __sexpr;
572     using __t = __sexpr;
573     using __desc_t = decltype(_DescriptorFn());
574     using __tag_t = typename __desc_t::__tag;
575     using __captures_t = __minvoke<__desc_t, __q<__detail::__captures_t>>;
576 
577     mutable __captures_t __impl_;
578 
579     template <class _Tag, class _Data, class... _Child>
580     STDEXEC_ATTRIBUTE((host, device, always_inline))
__sexprstdexec::__sexpr581     explicit __sexpr(_Tag, _Data&& __data, _Child&&... __child) :
582         __impl_(__detail::__captures(_Tag(), static_cast<_Data&&>(__data),
583                                      static_cast<_Child&&>(__child)...))
584     {}
585 
586     template <class _Self>
587     using __impl = __sexpr_impl<__meval<__msecond, _Self, __tag_t>>;
588 
589     template <class _Self = __sexpr>
590     STDEXEC_ATTRIBUTE((always_inline))
get_envstdexec::__sexpr591     auto get_env() const noexcept
592         -> __result_of<__sexpr_apply, const _Self&, __get_attrs_fn<__tag_t>>
593     {
594         return __sexpr_apply(*this,
595                              __detail::__drop_front(__impl<_Self>::get_attrs));
596     }
597 
598     template <__decays_to<__sexpr> _Self, class... _Env>
599     STDEXEC_ATTRIBUTE((always_inline))
get_completion_signaturesstdexec::__sexpr600     static auto get_completion_signatures(_Self&&, _Env&&...) noexcept //
601         -> __msecond<__if_c<__decays_to<_Self, __sexpr>>,
602                      __result_of<__impl<_Self>::get_completion_signatures,
603                                  _Self, _Env...>>
604     {
605         return {};
606     }
607 
608     // BUGBUG fix receiver constraint here:
609     template <__decays_to<__sexpr> _Self, /*receiver*/ class _Receiver>
610     STDEXEC_ATTRIBUTE((always_inline))
connectstdexec::__sexpr611     static auto connect(_Self&& __self, _Receiver&& __rcvr)               //
612         noexcept(__noexcept_of<__impl<_Self>::connect, _Self, _Receiver>) //
613         -> __msecond<__if_c<__decays_to<_Self, __sexpr>>,
614                      __result_of<__impl<_Self>::connect, _Self, _Receiver>>
615     {
616         return __impl<_Self>::connect(static_cast<_Self&&>(__self),
617                                       static_cast<_Receiver&&>(__rcvr));
618     }
619 
620     template <class _Sender, class _ApplyFn>
621     STDEXEC_ATTRIBUTE((always_inline))
applystdexec::__sexpr622     static auto apply(_Sender&& __sndr, _ApplyFn&& __fun)                //
623         noexcept(__nothrow_callable<__detail::__impl_of<_Sender>,
624                                     __copy_cvref_fn<_Sender>, _ApplyFn>) //
625         -> __call_result_t<__detail::__impl_of<_Sender>,
626                            __copy_cvref_fn<_Sender>, _ApplyFn>
627     {                                                                    //
628         return static_cast<_Sender&&>(__sndr).__impl_(
629             __copy_cvref_fn<_Sender>(), static_cast<_ApplyFn&&>(__fun)); //
630     }
631 
632     template <std::size_t _Idx, __decays_to_derived_from<__sexpr> _Self>
633     STDEXEC_ATTRIBUTE((always_inline))
get(_Self && __self)634     friend auto get(_Self&& __self) noexcept -> decltype(auto)
635         requires __detail::__in_range<_Idx, __desc_t>
636     {
637         if constexpr (_Idx == 0)
638         {
639             return __tag_t();
640         }
641         else
642         {
643             return __self.__impl_(__copy_cvref_fn<_Self>(),
644                                   __nth_pack_element<_Idx>);
645         }
646     }
647 };
648 
649 template <class _Tag, class _Data, class... _Child>
650 STDEXEC_ATTRIBUTE((host, device))
651 __sexpr(_Tag, _Data, _Child...)
652     -> __sexpr<STDEXEC_SEXPR_DESCRIPTOR(_Tag, _Data, _Child...)>;
653 
654 template <class _Tag, class _Data, class... _Child>
655 using __sexpr_t = __sexpr<STDEXEC_SEXPR_DESCRIPTOR(_Tag, _Data, _Child...)>;
656 
657 //////////////////////////////////////////////////////////////////////////////////////////////////
658 // __make_sexpr
659 //! A tagged function-object
660 //! Takes data and children and
661 //! returns `__sexpr_t<_Tag, _Data, _Child...>{_Tag(), data, children...}`.
662 namespace __detail
663 {
664 template <class _Tag>
665 struct __make_sexpr_t
666 {
667     template <class _Data = __, class... _Child>
operator ()stdexec::__detail::__make_sexpr_t668     constexpr auto operator()(_Data __data = {}, _Child... __child) const
669     {
670         return __sexpr_t<_Tag, _Data, _Child...>{
671             _Tag(), static_cast<_Data&&>(__data),
672             static_cast<_Child&&>(__child)...};
673     }
674 };
675 } // namespace __detail
676 
677 template <class _Tag>
678 inline constexpr __detail::__make_sexpr_t<_Tag> __make_sexpr{};
679 
680 // The __name_of utility defined below is used to pretty-print the type names of
681 // senders in compiler diagnostics.
682 namespace __detail
683 {
684 struct __basic_sender_name
685 {
686     template <class _Tag, class _Data, class... _Child>
687     using __result = __basic_sender<_Tag, _Data, __name_of<_Child>...>;
688 
689     template <class _Sender>
690     using __f = __minvoke<typename __decay_t<_Sender>::__desc_t, __q<__result>>;
691 };
692 
693 struct __id_name
694 {
695     template <class _Sender>
696     using __f = __name_of<__id<_Sender>>;
697 };
698 
699 template <class _Sender>
700 extern __mcompose<__cplr, __name_of_fn<_Sender>> __name_of_v<_Sender&>;
701 
702 template <class _Sender>
703 extern __mcompose<__cprr, __name_of_fn<_Sender>> __name_of_v<_Sender&&>;
704 
705 template <class _Sender>
706 extern __mcompose<__cpclr, __name_of_fn<_Sender>> __name_of_v<const _Sender&>;
707 
708 template <auto _Descriptor>
709 extern __basic_sender_name __name_of_v<__sexpr<_Descriptor>>;
710 
711 template <__has_id _Sender>
712     requires(!same_as<__id<_Sender>, _Sender>)
713 extern __id_name __name_of_v<_Sender>;
714 } // namespace __detail
715 } // namespace stdexec
716 
717 namespace std
718 {
719 template <auto _Descriptor>
720 struct tuple_size<stdexec::__sexpr<_Descriptor>> :
721     integral_constant<size_t,
722                       stdexec::__v<stdexec::__minvoke<
723                           stdexec::__result_of<_Descriptor>, stdexec::__msize>>>
724 {};
725 
726 template <size_t _Idx, auto _Descriptor>
727 struct tuple_element<_Idx, stdexec::__sexpr<_Descriptor>>
728 {
729     using type =                              //
730         stdexec::__remove_rvalue_reference_t< //
731             stdexec::__call_result_t<         //
732                 stdexec::__detail::__impl_of<stdexec::__sexpr<_Descriptor>>,
733                 stdexec::__cp, stdexec::__nth_pack_element_t<_Idx>>>;
734 };
735 } // namespace std
736