xref: /openbmc/sdbusplus/include/sdbusplus/async/stdexec/sequence_senders.hpp (revision 10d0b4b7d1498cfd5c3d37edea271a54d1984e41)
1 /*
2  * Copyright (c) 2023 Maikel Nadolski
3  * Copyright (c) 2023 NVIDIA Corporation
4  *
5  * Licensed under the Apache License Version 2.0 with LLVM Exceptions
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  *   https://llvm.org/LICENSE.txt
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 #pragma once
18 
19 #include "../stdexec/execution.hpp"
20 #include "../stdexec/__detail/__concepts.hpp"
21 #include "../stdexec/__detail/__meta.hpp"
22 
23 namespace exec {
24   struct sequence_sender_t : stdexec::sender_t { };
25 
26   using sequence_tag [[deprecated("Renamed to exec::sequence_sender_t")]] = exec::sequence_sender_t;
27 
28   namespace __sequence_sndr {
29     using namespace stdexec;
30 
31     template <class _Haystack>
32     struct __mall_contained_in_impl {
33       template <class... _Needles>
34       using __f = __mand<__mapply<__mcontains<_Needles>, _Haystack>...>;
35     };
36     template <class _Needles, class _Haystack>
37     using __mall_contained_in_t = __mapply<__mall_contained_in_impl<_Haystack>, _Needles>;
38 
39     template <class _Needles, class _Haystack>
40     concept __all_contained_in = __v<__mall_contained_in_t<_Needles, _Haystack>>;
41   } // namespace __sequence_sndr
42 
43   // This concept checks if a given sender satisfies the requirements to be returned from `set_next`.
44   template <class _Sender, class _Env = stdexec::env<>>
45   concept next_sender =
46     stdexec::sender_in<_Sender, _Env>
47     && __sequence_sndr::__all_contained_in<
48       stdexec::completion_signatures_of_t<_Sender, _Env>,
49       stdexec::completion_signatures<stdexec::set_value_t(), stdexec::set_stopped_t()>
50     >;
51 
52   namespace __sequence_sndr {
53 
54     template <class _Receiver, class _Item>
55     concept __has_set_next_member = requires(_Receiver& __rcvr, _Item&& __item) {
56       __rcvr.set_next(static_cast<_Item&&>(__item));
57     };
58 
59     // This is a sequence-receiver CPO that is used to apply algorithms on an input sender and it
60     // returns a next-sender. `set_next` is usually called in a context where a sender will be
61     // connected to a receiver. Since calling `set_next` usually involves constructing senders it
62     // is allowed to throw an excpetion, which needs to be handled by a calling sequence-operation.
63     // The returned object is a sender that can complete with `set_value_t()` or `set_stopped_t()`.
64     struct set_next_t {
65       template <receiver _Receiver, sender _Item>
66         requires __has_set_next_member<_Receiver, _Item>
operator ()exec::__sequence_sndr::set_next_t67       auto operator()(_Receiver& __rcvr, _Item&& __item) const
68         noexcept(noexcept(__rcvr.set_next(static_cast<_Item&&>(__item))))
69           -> decltype(__rcvr.set_next(static_cast<_Item&&>(__item))) {
70         return __rcvr.set_next(static_cast<_Item&&>(__item));
71       }
72 
73       template <receiver _Receiver, sender _Item>
74         requires(!__has_set_next_member<_Receiver, _Item>)
75              && tag_invocable<set_next_t, _Receiver&, _Item>
operator ()exec::__sequence_sndr::set_next_t76       auto operator()(_Receiver& __rcvr, _Item&& __item) const
77         noexcept(nothrow_tag_invocable<set_next_t, _Receiver&, _Item>)
78           -> tag_invoke_result_t<set_next_t, _Receiver&, _Item> {
79         static_assert(
80           next_sender<tag_invoke_result_t<set_next_t, _Receiver&, _Item>>,
81           "The sender returned from set_next is required to complete with set_value_t() or "
82           "set_stopped_t()");
83         return tag_invoke(*this, __rcvr, static_cast<_Item&&>(__item));
84       }
85     };
86   } // namespace __sequence_sndr
87 
88   using __sequence_sndr::set_next_t;
89   inline constexpr set_next_t set_next;
90 
91   template <class _Receiver, class _Sequence>
92   using next_sender_of_t = decltype(exec::set_next(
93     stdexec::__declval<stdexec::__decay_t<_Receiver>&>(),
94     stdexec::__declval<_Sequence>()));
95 
96   namespace __sequence_sndr {
97 
98     template <class _ReceiverId>
99     struct __stopped_means_break {
100       struct __t {
101         using receiver_concept = stdexec::receiver_t;
102         using __id = __stopped_means_break;
103         using _Receiver = stdexec::__t<_ReceiverId>;
104         using __token_t = stop_token_of_t<env_of_t<_Receiver>>;
105         STDEXEC_ATTRIBUTE(no_unique_address) _Receiver __rcvr_;
106 
get_envexec::__sequence_sndr::__stopped_means_break::__t107         auto get_env() const noexcept -> env_of_t<_Receiver> {
108           return stdexec::get_env(__rcvr_);
109         }
110 
set_valueexec::__sequence_sndr::__stopped_means_break::__t111         void set_value() noexcept
112           requires __callable<set_value_t, _Receiver>
113         {
114           return stdexec::set_value(static_cast<_Receiver&&>(__rcvr_));
115         }
116 
set_stoppedexec::__sequence_sndr::__stopped_means_break::__t117         void set_stopped() noexcept
118           requires __callable<set_value_t, _Receiver>
119                 && (unstoppable_token<__token_t> || __callable<set_stopped_t, _Receiver>)
120         {
121           if constexpr (unstoppable_token<__token_t>) {
122             stdexec::set_value(static_cast<_Receiver&&>(__rcvr_));
123           } else {
124             auto __token = stdexec::get_stop_token(stdexec::get_env(__rcvr_));
125             if (__token.stop_requested()) {
126               stdexec::set_stopped(static_cast<_Receiver&&>(__rcvr_));
127             } else {
128               stdexec::set_value(static_cast<_Receiver&&>(__rcvr_));
129             }
130           }
131         }
132       };
133     };
134 
135     template <class _Rcvr>
136     using __stopped_means_break_t = __t<__stopped_means_break<__id<__decay_t<_Rcvr>>>>;
137   } // namespace __sequence_sndr
138 
139   template <class _Sequence>
140   concept __enable_sequence_sender = requires {
141     typename _Sequence::sender_concept;
142   } && stdexec::derived_from<typename _Sequence::sender_concept, sequence_sender_t>;
143 
144   template <class _Sequence>
145   inline constexpr bool enable_sequence_sender = __enable_sequence_sender<_Sequence>;
146 
147   template <class... _Senders>
148   struct item_types { };
149 
150   template <class _Tp>
151   concept __has_item_typedef = requires { typename _Tp::item_types; };
152 
153   namespace __debug {
154     using namespace stdexec::__debug;
155 
156     struct __item_types { };
157   } // namespace __debug
158 
159   namespace __errs {
160     using namespace stdexec;
161     inline constexpr __mstring __unrecognized_sequence_type_diagnostic =
162       "The given type cannot be used as a sequence with the given environment "
163       "because the attempt to compute the item types failed."_mstr;
164   } // namespace __errs
165 
166   template <class _Sequence>
167   struct _WITH_SEQUENCE_;
168 
169   template <class... _Sequences>
170   struct _WITH_SEQUENCES_;
171 
172   template <stdexec::__mstring _Diagnostic = __errs::__unrecognized_sequence_type_diagnostic>
173   struct _UNRECOGNIZED_SEQUENCE_TYPE_;
174 
175   /////////////////////////////////////////////////////////////////////////////
176   // [execution.seqtraits]
177   namespace __sequence_sndr {
178     struct get_item_types_t;
179 
180     template <class _Sequence, class... _Env>
181     using __item_types_of_t = __call_result_t<get_item_types_t, _Sequence, _Env...>;
182 
183     template <class _Sequence, class... _Env>
184     using __unrecognized_sequence_error_t = __mexception<
185       _UNRECOGNIZED_SEQUENCE_TYPE_<>,
186       _WITH_SEQUENCE_<_Sequence>,
187       _WITH_ENVIRONMENT_<_Env>...
188     >;
189 
190     template <class _Sequence, class _Env>
191     using __member_result_t = decltype(__declval<_Sequence>().get_item_types(__declval<_Env>()));
192 
193     template <class _Sequence, class _Env>
194     using __static_member_result_t = decltype(STDEXEC_REMOVE_REFERENCE(
195       _Sequence)::get_item_types(__declval<_Sequence>(), __declval<_Env>()));
196 
197     template <class _Sequence, class _Env>
198     using __tfx_sequence_t =
199       transform_sender_result_t<__late_domain_of_t<_Sequence, _Env>, _Sequence, _Env>;
200 
201     template <class _Sequence, class _Env>
202     concept __with_tag_invoke =
203       tag_invocable<get_item_types_t, __tfx_sequence_t<_Sequence, _Env>, _Env>;
204 
205     template <class _Sequence, class _Env>
206     using __member_alias_t = __decay_t<__tfx_sequence_t<_Sequence, _Env>>::item_types;
207 
208     template <class _Sequence, class _Env>
209     concept __with_member_alias = __mvalid<__member_alias_t, _Sequence, _Env>;
210 
211     template <class _Sequence, class _Env>
212     concept __with_static_member = __mvalid<__static_member_result_t, _Sequence, _Env>;
213 
214     template <class _Sequence, class... _Env>
215     concept __with_member = __mvalid<__member_result_t, _Sequence, _Env...>;
216 
217     struct get_item_types_t {
218       template <class _Sequence, class _Env>
__implexec::__sequence_sndr::get_item_types_t219       static auto __impl() {
220         static_assert(sizeof(_Sequence), "Incomplete type used with get_item_types");
221         static_assert(sizeof(_Env), "Incomplete type used with get_item_types");
222         using __tfx_sequence_t = __tfx_sequence_t<_Sequence, _Env>;
223         if constexpr (__merror<__tfx_sequence_t>) {
224           // Computing the type of the transformed sender returned an error type. Propagate it.
225           return static_cast<__tfx_sequence_t (*)()>(nullptr);
226         } else if constexpr (__with_member_alias<__tfx_sequence_t, _Env>) {
227           using __result_t = __member_alias_t<__tfx_sequence_t, _Env>;
228           return static_cast<__result_t (*)()>(nullptr);
229         } else if constexpr (__with_static_member<__tfx_sequence_t, _Env>) {
230           using __result_t = __static_member_result_t<__tfx_sequence_t, _Env>;
231           return static_cast<__result_t (*)()>(nullptr);
232         } else if constexpr (__with_member<__tfx_sequence_t, _Env>) {
233           using __result_t = decltype(__declval<__tfx_sequence_t>()
234                                         .get_item_types(__declval<_Env>()));
235           return static_cast<__result_t (*)()>(nullptr);
236         } else if constexpr (__with_tag_invoke<__tfx_sequence_t, _Env>) {
237           using __result_t = tag_invoke_result_t<get_item_types_t, __tfx_sequence_t, _Env>;
238           return static_cast<__result_t (*)()>(nullptr);
239         } else if constexpr (
240           sender_in<__tfx_sequence_t, _Env>
241           && !enable_sequence_sender<stdexec::__decay_t<__tfx_sequence_t>>) {
242           using __result_t = item_types<stdexec::__decay_t<__tfx_sequence_t>>;
243           return static_cast<__result_t (*)()>(nullptr);
244         } else if constexpr (__is_debug_env<_Env>) {
245           using __tag_invoke::tag_invoke;
246           // This ought to cause a hard error that indicates where the problem is.
247           using __item_types_t
248             [[maybe_unused]] = tag_invoke_result_t<get_item_types_t, __tfx_sequence_t, _Env>;
249           return static_cast<__debug::__item_types (*)()>(nullptr);
250         } else {
251           using __result_t = __unrecognized_sequence_error_t<_Sequence, _Env>;
252           return static_cast<__result_t (*)()>(nullptr);
253         }
254       }
255 
256       template <class _Sequence, class _Env = env<>>
operator ()exec::__sequence_sndr::get_item_types_t257       constexpr auto operator()(_Sequence&&, _Env&& = {}) const noexcept
258         -> decltype(__impl<_Sequence, _Env>()()) {
259         return {};
260       }
261     };
262   } // namespace __sequence_sndr
263 
264   using __sequence_sndr::get_item_types_t;
265   inline constexpr get_item_types_t get_item_types{};
266 
267   template <class _Sequence, class... _Env>
268   concept sequence_sender = stdexec::sender_in<_Sequence, _Env...>
269                          && enable_sequence_sender<stdexec::__decay_t<_Sequence>>;
270 
271   template <class _Sequence, class... _Env>
272   concept has_sequence_item_types = requires(_Sequence&& __sequence, _Env&&... __env) {
273     { get_item_types(static_cast<_Sequence&&>(__sequence), static_cast<_Env&&>(__env)...) };
274   };
275 
276   template <class _Sequence, class... _Env>
277   concept sequence_sender_in = sequence_sender<_Sequence, _Env...>
278                             && has_sequence_item_types<_Sequence, _Env...>;
279 
280   template <class _Sequence, class... _Env>
281   using __item_types_of_t =
282     decltype(get_item_types(stdexec::__declval<_Sequence>(), stdexec::__declval<_Env>()...));
283 
284 
285   template <class _Item>
286   struct _SEQUENCE_ITEM_IS_NOT_A_WELL_FORMED_SENDER_ { };
287 
288   template <class _Sequence, class _Item>
289   auto __check_item(_Item*) -> stdexec::__mexception<
290     _SEQUENCE_ITEM_IS_NOT_A_WELL_FORMED_SENDER_<_Item>,
291     _WITH_SEQUENCE_<_Sequence>
292   >;
293 
294   template <class _Sequence, class _Item>
295     requires stdexec::__well_formed_sender<_Item>
296   auto __check_item(_Item*) -> stdexec::__msuccess;
297 
298   template <class _Sequence, class _Items>
299     requires stdexec::__merror<_Items>
300   auto __check_items(_Items*) -> _Items;
301 
302   template <class _Item>
303   struct _SEQUENCE_GET_ITEM_TYPES_RESULT_IS_NOT_WELL_FORMED_ { };
304 
305   template <class _Sequence, class _Items>
306     requires(!stdexec::__merror<_Items>)
307   auto __check_items(_Items*) -> stdexec::__mexception<
308     _SEQUENCE_GET_ITEM_TYPES_RESULT_IS_NOT_WELL_FORMED_<_Items>,
309     _WITH_SEQUENCE_<_Sequence>
310   >;
311 
312   template <class _Sequence, class... _Items>
313   auto __check_items(exec::item_types<_Items...>*) -> decltype((
314     stdexec::__msuccess(),
315     ...,
316     exec::__check_item<_Sequence>(static_cast<_Items*>(nullptr))));
317 
318   template <class _Sequence>
319     requires stdexec::__merror<_Sequence>
320   auto __check_sequence(_Sequence*) -> _Sequence;
321 
322   struct _SEQUENCE_GET_ITEM_TYPES_IS_NOT_WELL_FORMED_ { };
323 
324   template <class _Sequence>
325     requires(!stdexec::__merror<_Sequence>) && (!stdexec::__mvalid<__item_types_of_t, _Sequence>)
326   auto __check_sequence(_Sequence*) -> stdexec::__mexception<
327     _SEQUENCE_GET_ITEM_TYPES_IS_NOT_WELL_FORMED_,
328     _WITH_SEQUENCE_<_Sequence>
329   >;
330 
331   template <class _Sequence>
332     requires(!stdexec::__merror<_Sequence>) && stdexec::__mvalid<__item_types_of_t, _Sequence>
333   auto __check_sequence(_Sequence*) -> decltype(exec::__check_items<_Sequence>(
334     static_cast<__item_types_of_t<_Sequence>*>(nullptr)));
335 
336   template <class _Sequence>
337   concept __well_formed_item_senders = has_sequence_item_types<stdexec::__decay_t<_Sequence>>
338                                     && requires(stdexec::__decay_t<_Sequence>* __sequence) {
339                                          { exec::__check_sequence(__sequence) } -> stdexec::__ok;
340                                        };
341 
342   template <class _Sequence>
343   concept __well_formed_sequence_sender = stdexec::__well_formed_sender<_Sequence>
344                                        && enable_sequence_sender<stdexec::__decay_t<_Sequence>>
345                                        && __well_formed_item_senders<_Sequence>;
346 
347   template <class _Receiver>
348   struct _WITH_RECEIVER_ { };
349 
350   template <class _Item>
351   struct _MISSING_SET_NEXT_OVERLOAD_FOR_ITEM_ { };
352 
353   template <class _Receiver, class _Item>
354   auto __try_item(_Item*) -> stdexec::__mexception<
355     _MISSING_SET_NEXT_OVERLOAD_FOR_ITEM_<_Item>,
356     _WITH_RECEIVER_<_Receiver>
357   >;
358 
359   template <class _Receiver, class _Item>
360     requires stdexec::__callable<set_next_t, _Receiver&, _Item>
361   auto __try_item(_Item*) -> stdexec::__msuccess;
362 
363   template <class _Receiver, class... _Items>
364   auto __try_items(exec::item_types<_Items...>*) -> decltype((
365     stdexec::__msuccess(),
366     ...,
367     exec::__try_item<_Receiver>(static_cast<_Items*>(nullptr))));
368 
369   template <class _Receiver, class _Items>
370   concept __sequence_receiver_of = requires(_Items* __items) {
371     { exec::__try_items<stdexec::__decay_t<_Receiver>>(__items) } -> stdexec::__ok;
372   };
373 
374   template <class _Receiver, class _SequenceItems>
375   concept sequence_receiver_of = stdexec::receiver<_Receiver>
376                               && __sequence_receiver_of<_Receiver, _SequenceItems>;
377 
378   template <class _Completions>
379   using __to_sequence_completions_t = stdexec::__transform_completion_signatures<
380     _Completions,
381     stdexec::__mconst<stdexec::completion_signatures<stdexec::set_value_t()>>::__f,
382     stdexec::__sigs::__default_set_error,
383     stdexec::completion_signatures<stdexec::set_stopped_t()>,
384     stdexec::__concat_completion_signatures
385   >;
386 
387   template <class _Sender, class... _Env>
388   using __item_completion_signatures_t = stdexec::transform_completion_signatures<
389     stdexec::__completion_signatures_of_t<_Sender, _Env...>,
390     stdexec::completion_signatures<stdexec::set_value_t()>,
391     stdexec::__mconst<stdexec::completion_signatures<>>::__f
392   >;
393 
394   template <class _Sequence, class... _Env>
395   using __sequence_completion_signatures_t = stdexec::transform_completion_signatures<
396     stdexec::__completion_signatures_of_t<_Sequence, _Env...>,
397     stdexec::completion_signatures<stdexec::set_value_t()>,
398     stdexec::__mconst<stdexec::completion_signatures<>>::__f
399   >;
400 
401   template <class _Sequence, class... _Env>
402   using __sequence_completion_signatures_of_t = stdexec::__mapply<
403     stdexec::__mtransform<
404       stdexec::__mbind_back_q<__item_completion_signatures_t, _Env...>,
405       stdexec::__mbind_back<
406         stdexec::__mtry_q<stdexec::__concat_completion_signatures>,
407         __sequence_completion_signatures_t<_Sequence, _Env...>
408       >
409     >,
410     __item_types_of_t<_Sequence, _Env...>
411   >;
412 
413   template <class _Receiver, class _Sequence>
414   concept sequence_receiver_from = stdexec::receiver<_Receiver>
415                                 && stdexec::sender_in<_Sequence, stdexec::env_of_t<_Receiver>>
416                                 && sequence_receiver_of<
417                                      _Receiver,
418                                      __item_types_of_t<_Sequence, stdexec::env_of_t<_Receiver>>
419                                 >
420                                 && ((sequence_sender_in<_Sequence, stdexec::env_of_t<_Receiver>>
421                                      && stdexec::receiver_of<
422                                        _Receiver,
423                                        stdexec::completion_signatures_of_t<
424                                          _Sequence,
425                                          stdexec::env_of_t<_Receiver>
426                                        >
427                                      >)
428                                     || (!sequence_sender_in<_Sequence, stdexec::env_of_t<_Receiver>> && stdexec::__receiver_from<__sequence_sndr::__stopped_means_break_t<_Receiver>, next_sender_of_t<_Receiver, _Sequence>>) );
429 
430   namespace __sequence_sndr {
431     struct subscribe_t;
432 
433     struct _NO_USABLE_SUBSCRIBE_CUSTOMIZATION_FOUND_ {
434       void operator()() const noexcept = delete;
435     };
436 
437     template <class _Env>
438     using __next_sender_completion_sigs_t = __if_c<
439       unstoppable_token<stop_token_of_t<_Env>>,
440       completion_signatures<set_value_t()>,
441       completion_signatures<set_value_t(), set_stopped_t()>
442     >;
443 
444     template <class _Sender, class _Receiver>
445     concept __next_connectable =
446       receiver<_Receiver> && sender_in<_Sender, env_of_t<_Receiver>>
447       && !sequence_sender_in<_Sender, env_of_t<_Receiver>>
448       && sequence_receiver_of<_Receiver, item_types<stdexec::__decay_t<_Sender>>>
449       && sender_to<next_sender_of_t<_Receiver, _Sender>, __stopped_means_break_t<_Receiver>>;
450 
451     template <class _Sequence, class _Receiver>
452     concept __subscribable_with_static_member =
453       receiver<_Receiver> && sequence_sender_in<_Sequence, env_of_t<_Receiver>>
454       && sequence_receiver_from<_Receiver, _Sequence>
455       && requires(_Sequence&& __sequence, _Receiver&& __rcvr) {
456            {
457              STDEXEC_REMOVE_REFERENCE(_Sequence)
458              ::subscribe(static_cast<_Sequence&&>(__sequence), static_cast<_Receiver&&>(__rcvr))
459            };
460          };
461 
462     template <class _Sequence, class _Receiver>
463     concept __subscribable_with_member = receiver<_Receiver>
464                                       && sequence_sender_in<_Sequence, env_of_t<_Receiver>>
465                                       && sequence_receiver_from<_Receiver, _Sequence>
466                                       && requires(_Sequence&& __sequence, _Receiver&& __rcvr) {
467                                            {
468                                              static_cast<_Sequence&&>(__sequence)
469                                                .subscribe(static_cast<_Receiver&&>(__rcvr))
470                                            };
471                                          };
472 
473     template <class _Sequence, class _Receiver>
474     concept __subscribable_with_tag_invoke = receiver<_Receiver>
475                                           && sequence_sender_in<_Sequence, env_of_t<_Receiver>>
476                                           && sequence_receiver_from<_Receiver, _Sequence>
477                                           && tag_invocable<subscribe_t, _Sequence, _Receiver>;
478 
479     struct subscribe_t {
480       template <class _Sequence, class _Receiver>
481       using __tfx_sequence_t = __tfx_sequence_t<_Sequence, env_of_t<_Receiver>>;
482 
483       template <class _Sequence, class _Receiver>
__select_implexec::__sequence_sndr::subscribe_t484       static constexpr auto __select_impl() noexcept {
485         using __domain_t = __late_domain_of_t<_Sequence, env_of_t<_Receiver&>>;
486         constexpr bool _NothrowTfxSequence =
487           __nothrow_callable<transform_sender_t, __domain_t, _Sequence, env_of_t<_Receiver&>>;
488         using __tfx_sequence_t = __tfx_sequence_t<_Sequence, _Receiver>;
489         if constexpr (__next_connectable<__tfx_sequence_t, _Receiver>) {
490           using __result_t = connect_result_t<
491             next_sender_of_t<_Receiver, __tfx_sequence_t>,
492             __stopped_means_break_t<_Receiver>
493           >;
494           static_assert(
495             operation_state<__result_t>,
496             "stdexec::connect(sender, receiver) must return a type that "
497             "satisfies the operation_state concept");
498           constexpr bool _Nothrow = __nothrow_connectable<
499             next_sender_of_t<_Receiver, __tfx_sequence_t>,
500             __stopped_means_break_t<_Receiver>
501           >;
502           return static_cast<__result_t (*)() noexcept(_Nothrow)>(nullptr);
503         } else if constexpr (__subscribable_with_static_member<__tfx_sequence_t, _Receiver>) {
504           using __result_t = decltype(STDEXEC_REMOVE_REFERENCE(
505             __tfx_sequence_t)::subscribe(__declval<__tfx_sequence_t>(), __declval<_Receiver>()));
506           static_assert(
507             operation_state<__result_t>,
508             "Sequence::subscribe(sender, receiver) must return a type that "
509             "satisfies the operation_state concept");
510           constexpr bool _Nothrow = _NothrowTfxSequence
511                                  && noexcept(STDEXEC_REMOVE_REFERENCE(__tfx_sequence_t)::subscribe(
512                                    __declval<__tfx_sequence_t>(), __declval<_Receiver>()));
513           return static_cast<__result_t (*)() noexcept(_Nothrow)>(nullptr);
514         } else if constexpr (__subscribable_with_member<__tfx_sequence_t, _Receiver>) {
515           using __result_t = decltype(__declval<__tfx_sequence_t>()
516                                         .subscribe(__declval<_Receiver>()));
517           static_assert(
518             operation_state<__result_t>,
519             "Sequence::subscribe(sender, receiver) must return a type that "
520             "satisfies the operation_state concept");
521           constexpr bool _Nothrow = _NothrowTfxSequence
522                                  && noexcept(__declval<__tfx_sequence_t>()
523                                                .subscribe(__declval<_Receiver>()));
524           return static_cast<__result_t (*)() noexcept(_Nothrow)>(nullptr);
525         } else if constexpr (__subscribable_with_tag_invoke<__tfx_sequence_t, _Receiver>) {
526           using __result_t = tag_invoke_result_t<subscribe_t, __tfx_sequence_t, _Receiver>;
527           static_assert(
528             operation_state<__result_t>,
529             "exec::subscribe(sender, receiver) must return a type that "
530             "satisfies the operation_state concept");
531           constexpr bool _Nothrow = _NothrowTfxSequence
532                                  && nothrow_tag_invocable<subscribe_t, __tfx_sequence_t, _Receiver>;
533           return static_cast<__result_t (*)() noexcept(_Nothrow)>(nullptr);
534         } else if constexpr (__is_debug_env<env_of_t<_Receiver>>) {
535           using __result_t = __debug::__debug_operation;
536           return static_cast<__result_t (*)() noexcept(_NothrowTfxSequence)>(nullptr);
537         } else {
538           return _NO_USABLE_SUBSCRIBE_CUSTOMIZATION_FOUND_();
539         }
540       }
541 
542       template <class _Sequence, class _Receiver>
543       using __select_impl_t = decltype(__select_impl<_Sequence, _Receiver>());
544 
545       template <sender _Sequence, receiver _Receiver>
operator ()exec::__sequence_sndr::subscribe_t546       auto operator()(_Sequence&& __sequence, _Receiver&& __rcvr) const
547         noexcept(__nothrow_callable<__select_impl_t<_Sequence, _Receiver>>)
548           -> __call_result_t<__select_impl_t<_Sequence, _Receiver>> {
549         using __tfx_sequence_t = __tfx_sequence_t<_Sequence, _Receiver>;
550         auto&& __env = stdexec::get_env(__rcvr);
551         auto __domain = __get_late_domain(__sequence, __env);
552         if constexpr (__next_connectable<__tfx_sequence_t, _Receiver>) {
553           next_sender_of_t<_Receiver, __tfx_sequence_t> __next = set_next(
554             __rcvr,
555             stdexec::transform_sender(__domain, static_cast<_Sequence&&>(__sequence), __env));
556           return stdexec::connect(
557             static_cast<next_sender_of_t<_Receiver, __tfx_sequence_t>&&>(__next),
558             __stopped_means_break_t<_Receiver>{static_cast<_Receiver&&>(__rcvr)});
559           // NOLINTNEXTLINE(bugprone-branch-clone)
560         } else if constexpr (__subscribable_with_static_member<__tfx_sequence_t, _Receiver>) {
561           auto&& __tfx_sequence =
562             transform_sender(__domain, static_cast<_Sequence&&>(__sequence), __env);
563           return __tfx_sequence.subscribe(
564             static_cast<__tfx_sequence_t&&>(__tfx_sequence), static_cast<_Receiver&&>(__rcvr));
565         } else if constexpr (__subscribable_with_member<__tfx_sequence_t, _Receiver>) {
566           return stdexec::transform_sender(__domain, static_cast<_Sequence&&>(__sequence), __env)
567             .subscribe(static_cast<_Receiver&&>(__rcvr));
568         } else if constexpr (__subscribable_with_tag_invoke<__tfx_sequence_t, _Receiver>) {
569           return stdexec::tag_invoke(
570             subscribe_t{},
571             stdexec::transform_sender(__domain, static_cast<_Sequence&&>(__sequence), __env),
572             static_cast<_Receiver&&>(__rcvr));
573         } else if constexpr (enable_sequence_sender<stdexec::__decay_t<__tfx_sequence_t>>) {
574           // This should generate an instantiate backtrace that contains useful
575           // debugging information.
576           auto&& __tfx_sequence =
577             transform_sender(__domain, static_cast<_Sequence&&>(__sequence), __env);
578           return __tfx_sequence.subscribe(
579             static_cast<__tfx_sequence_t&&>(__tfx_sequence), static_cast<_Receiver&&>(__rcvr));
580         } else {
581           // This should generate an instantiate backtrace that contains useful
582           // debugging information.
583           next_sender_of_t<_Receiver, __tfx_sequence_t> __next = set_next(
584             __rcvr,
585             stdexec::transform_sender(__domain, static_cast<_Sequence&&>(__sequence), __env));
586           return stdexec::connect(
587             static_cast<next_sender_of_t<_Receiver, __tfx_sequence_t>&&>(__next),
588             __stopped_means_break_t<_Receiver>{static_cast<_Receiver&&>(__rcvr)});
589         }
590       }
591 
queryexec::__sequence_sndr::subscribe_t592       static constexpr auto query(stdexec::forwarding_query_t) noexcept -> bool {
593         return false;
594       }
595     };
596 
597     template <class _Sequence, class _Receiver>
598     using subscribe_result_t = __call_result_t<subscribe_t, _Sequence, _Receiver>;
599   } // namespace __sequence_sndr
600 
601   using __sequence_sndr::__next_sender_completion_sigs_t;
602 
603   using __sequence_sndr::subscribe_t;
604   inline constexpr subscribe_t subscribe{};
605 
606   using __sequence_sndr::subscribe_result_t;
607 
608   template <class _Sequence, class _Receiver>
609   concept sequence_sender_to =
610     sequence_receiver_from<_Receiver, _Sequence>
611     && requires(_Sequence&& __sequence, _Receiver&& __rcvr) {
612          subscribe(static_cast<_Sequence&&>(__sequence), static_cast<_Receiver&&>(__rcvr));
613        };
614 
615   template <class _Receiver>
616   concept __stoppable_receiver = stdexec::__callable<stdexec::set_value_t, _Receiver>
617                               && (stdexec::unstoppable_token<
618                                     stdexec::stop_token_of_t<stdexec::env_of_t<_Receiver>>
619                                   >
620                                   || stdexec::__callable<stdexec::set_stopped_t, _Receiver>);
621 
622   template <class _Receiver>
623     requires __stoppable_receiver<_Receiver>
__set_value_unless_stopped(_Receiver && __rcvr)624   void __set_value_unless_stopped(_Receiver&& __rcvr) {
625     using token_type = stdexec::stop_token_of_t<stdexec::env_of_t<_Receiver>>;
626     if constexpr (stdexec::unstoppable_token<token_type>) {
627       stdexec::set_value(static_cast<_Receiver&&>(__rcvr));
628     } else {
629       auto token = stdexec::get_stop_token(stdexec::get_env(__rcvr));
630       if (!token.stop_requested()) {
631         stdexec::set_value(static_cast<_Receiver&&>(__rcvr));
632       } else {
633         stdexec::set_stopped(static_cast<_Receiver&&>(__rcvr));
634       }
635     }
636   }
637 
638   ////////////////////////////////////////////////////////////////////////////////
639 #define STDEXEC_ERROR_GET_ITEM_TYPES_RETURNED_AN_ERROR                                             \
640   "\n"                                                                                             \
641   "\n"                                                                                             \
642   "Trying to compute the sequences's item types resulted in an error. See\n"                       \
643   "the rest of the compiler diagnostic for clues. Look for the string \"_ERROR_\".\n"
644 
645 #define STDEXEC_ERROR_GET_ITEM_TYPES_HAS_INVALID_RETURN_TYPE                                       \
646   "\n"                                                                                             \
647   "\n"                                                                                             \
648   "The member function `get_item_types` of the sequence returned an\n"                             \
649   "invalid type.\n"                                                                                \
650   "\n"                                                                                             \
651   "A sender's `get_item_types` function must return a specialization of\n"                         \
652   "`exec::item_types<...>`, as follows:\n"                                                         \
653   "\n"                                                                                             \
654   "  class MySequence\n"                                                                           \
655   "  {\n"                                                                                          \
656   "  public:\n"                                                                                    \
657   "    using sender_concept = exec::sequence_sender_t;\n"                                          \
658   "\n"                                                                                             \
659   "    template <class... _Env>\n"                                                                 \
660   "    auto get_item_types(_Env&&...) -> exec::item_types<\n"                                      \
661   "      // This sequence produces void items...\n"                                                \
662   "      stdexec::__call_result_t<stdexec::just_t>>\n"                                             \
663   "    {\n"                                                                                        \
664   "    return {};\n"                                                                               \
665   "    }\n"                                                                                        \
666   "    ...\n"                                                                                      \
667   "  };\n"
668 
669   // Used to report a meaningful error message when the sender_in<Sndr, Env>
670   // concept check fails.
671   template <class _Sequence, class... _Env>
__diagnose_sequence_concept_failure()672   auto __diagnose_sequence_concept_failure() {
673     if constexpr (!enable_sequence_sender<stdexec::__decay_t<_Sequence>>) {
674       static_assert(enable_sequence_sender<_Sequence>, STDEXEC_ERROR_ENABLE_SENDER_IS_FALSE);
675     } else if constexpr (!stdexec::__detail::__consistent_completion_domains<_Sequence>) {
676       static_assert(
677         stdexec::__detail::__consistent_completion_domains<_Sequence>,
678         "The completion schedulers of the sequence do not have "
679         "consistent domains. This is likely a "
680         "bug in the sequence implementation.");
681     } else if constexpr (!std::move_constructible<stdexec::__decay_t<_Sequence>>) {
682       static_assert(
683         std::move_constructible<stdexec::__decay_t<_Sequence>>,
684         "The sequence type is not move-constructible.");
685     } else if constexpr (!std::constructible_from<stdexec::__decay_t<_Sequence>, _Sequence>) {
686       static_assert(
687         std::constructible_from<stdexec::__decay_t<_Sequence>, _Sequence>,
688         "The sequence cannot be decay-copied. Did you forget a std::move?");
689     } else {
690       using __items_t = __item_types_of_t<_Sequence, _Env...>;
691       if constexpr (stdexec::__same_as<
692                       __items_t,
693                       __sequence_sndr::__unrecognized_sequence_error_t<_Sequence, _Env...>
694                     >) {
695         static_assert(
696           stdexec::__mnever<__items_t>, STDEXEC_ERROR_CANNOT_COMPUTE_COMPLETION_SIGNATURES);
697       } else if constexpr (stdexec::__merror<__items_t>) {
698         static_assert(
699           !stdexec::__merror<__items_t>, STDEXEC_ERROR_GET_ITEM_TYPES_RETURNED_AN_ERROR);
700       } else if constexpr (!__well_formed_item_senders<_Sequence>) {
701         static_assert(
702           __well_formed_item_senders<_Sequence>,
703           STDEXEC_ERROR_GET_ITEM_TYPES_HAS_INVALID_RETURN_TYPE);
704       } else {
705         stdexec::__diagnose_sender_concept_failure<_Sequence, _Env...>();
706       }
707     }
708   }
709 
710   namespace __debug {
711 
712     template <class... _Items>
713     struct __valid_next {
714       template <class _Item>
715         requires stdexec::__one_of<_Item, _Items...>
STDEXEC_ATTRIBUTEexec::__debug::__valid_next716       STDEXEC_ATTRIBUTE(host, device)
717       stdexec::__call_result_t<stdexec::just_t> set_next(_Item&&) noexcept {
718         STDEXEC_TERMINATE();
719         return stdexec::just();
720       }
721     };
722 
723     template <class _CvrefSequenceId, class _Env, class _Completions, class _ItemTypes>
724     struct __debug_sequence_sender_receiver {
725       using __t = __debug_sequence_sender_receiver;
726       using __id = __debug_sequence_sender_receiver;
727       using receiver_concept = stdexec::receiver_t;
728     };
729 
730     template <class _CvrefSequenceId, class _Env, class... _Sigs, class... _Items>
731     struct __debug_sequence_sender_receiver<
732       _CvrefSequenceId,
733       _Env,
734       stdexec::completion_signatures<_Sigs...>,
735       item_types<_Items...>
736     >
737       : __valid_completions<__normalize_sig_t<_Sigs>...>
738       , __valid_next<_Items...> {
739       using __t = __debug_sequence_sender_receiver;
740       using __id = __debug_sequence_sender_receiver;
741       using receiver_concept = stdexec::receiver_t;
742 
STDEXEC_ATTRIBUTEexec::__debug::__debug_sequence_sender_receiver743       STDEXEC_ATTRIBUTE(host, device) auto get_env() const noexcept -> __debug_env_t<_Env> {
744         STDEXEC_TERMINATE();
745       }
746     };
747 
748     template <class _Env = stdexec::env<>, class _Sequence>
__debug_sequence_sender(_Sequence && __sequence,const _Env &={})749     void __debug_sequence_sender(_Sequence&& __sequence, const _Env& = {}) {
750       if constexpr (!__is_debug_env<_Env>) {
751         if constexpr (sequence_sender_in<_Sequence, _Env>) {
752           using __sigs_t = stdexec::__completion_signatures_of_t<_Sequence, __debug_env_t<_Env>>;
753           using __item_types_t = __sequence_sndr::__item_types_of_t<_Sequence, __debug_env_t<_Env>>;
754           using __receiver_t = __debug_sequence_sender_receiver<
755             stdexec::__cvref_id<_Sequence>,
756             _Env,
757             __sigs_t,
758             __item_types_t
759           >;
760           if constexpr (
761             !std::same_as<__sigs_t, __debug::__completion_signatures>
762             || !std::same_as<__item_types_t, __debug::__item_types>) {
763             using __operation_t = exec::subscribe_result_t<_Sequence, __receiver_t>;
764             //static_assert(receiver_of<_Receiver, _Sigs>);
765             if constexpr (!std::same_as<__operation_t, __debug_operation>) {
766               if (sizeof(_Sequence) == ~0ul) { // never true
767                 auto __op = subscribe(static_cast<_Sequence&&>(__sequence), __receiver_t{});
768                 stdexec::start(__op);
769               }
770             }
771           }
772         } else {
773           __diagnose_sequence_concept_failure<_Sequence, _Env>();
774         }
775       }
776     }
777   } // namespace __debug
778   using __debug::__debug_sequence_sender;
779 
780 #if STDEXEC_ENABLE_EXTRA_TYPE_CHECKING()
781   // __checked_completion_signatures is for catching logic bugs in a sender's metadata. If sender<S>
782   // and sender_in<S, Ctx> are both true, then they had better report the same metadata. This
783   // completion signatures wrapper enforces that at compile time.
784   template <class _Sequence, class... _Env>
__checked_item_types(_Sequence && __sequence,_Env &&...__env)785   auto __checked_item_types(_Sequence&& __sequence, _Env&&... __env) noexcept {
786     using __completions_t =
787       decltype(get_item_types(stdexec::__declval<_Sequence>(), stdexec::__declval<_Env>()...));
788     // (void)__sequence;
789     // [](auto&&...){}(__env...);
790     exec::__debug_sequence_sender(static_cast<_Sequence&&>(__sequence), __env...);
791     return __completions_t{};
792   }
793 
794   template <class _Sequence, class... _Env>
795     requires sequence_sender_in<_Sequence, _Env...>
796   using item_types_of_t = decltype(exec::__checked_item_types(
797     stdexec::__declval<_Sequence>(),
798     stdexec::__declval<_Env>()...));
799 #else
800   template <class _Sequence, class... _Env>
801     requires sequence_sender_in<_Sequence, _Env...>
802   using item_types_of_t = __item_types_of_t<_Sequence, _Env...>;
803 #endif
804 } // namespace exec
805