1 /*
2 * Copyright (c) 2021-2022 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 "__detail/__basic_sender.hpp"
19 #include "__detail/__config.hpp"
20 #include "__detail/__cpo.hpp"
21 #include "__detail/__domain.hpp"
22 #include "__detail/__env.hpp"
23 #include "__detail/__execution_fwd.hpp"
24 #include "__detail/__intrusive_ptr.hpp"
25 #include "__detail/__meta.hpp"
26 #include "__detail/__scope.hpp"
27 #include "__detail/__type_traits.hpp"
28 #include "__detail/__utility.hpp"
29 #include "concepts.hpp"
30 #include "coroutine.hpp"
31 #include "functional.hpp"
32 #include "stop_token.hpp"
33
34 #include <atomic>
35 #include <cassert>
36 #include <concepts>
37 #include <condition_variable>
38 #include <cstddef>
39 #include <exception>
40 #include <memory>
41 #include <mutex>
42 #include <optional>
43 #include <stdexcept>
44 #include <system_error>
45 #include <tuple>
46 #include <type_traits>
47 #include <utility>
48 #include <variant>
49
50 STDEXEC_PRAGMA_PUSH()
51 STDEXEC_PRAGMA_IGNORE_GNU("-Wundefined-inline")
52 STDEXEC_PRAGMA_IGNORE_GNU("-Wsubobject-linkage")
53 STDEXEC_PRAGMA_IGNORE_GNU("-Wmissing-braces")
54
55 STDEXEC_PRAGMA_IGNORE_EDG(1302)
56 STDEXEC_PRAGMA_IGNORE_EDG(497)
57 STDEXEC_PRAGMA_IGNORE_EDG(type_qualifiers_ignored_on_reference)
58
59 namespace stdexec
60 {
61 /////////////////////////////////////////////////////////////////////////////
62 template <class _Sender, class _Scheduler, class _Tag = set_value_t>
63 concept __completes_on = __decays_to<
64 __call_result_t<get_completion_scheduler_t<_Tag>, env_of_t<_Sender>>,
65 _Scheduler>;
66
67 /////////////////////////////////////////////////////////////////////////////
68 template <class _Sender, class _Scheduler, class _Env>
69 concept __starts_on =
70 __decays_to<__call_result_t<get_scheduler_t, _Env>, _Scheduler>;
71
72 /////////////////////////////////////////////////////////////////////////////
73 // [execution.receivers]
74 namespace __receivers
75 {
76 struct set_value_t
77 {
78 template <class _Fn, class... _Args>
79 using __f = __minvoke<_Fn, _Args...>;
80
81 template <class _Receiver, class... _As>
82 requires tag_invocable<set_value_t, _Receiver, _As...>
83 STDEXEC_ATTRIBUTE((host, device, always_inline)) void
operator ()stdexec::__receivers::set_value_t84 operator()(_Receiver&& __rcvr, _As&&... __as) const noexcept
85 {
86 static_assert(nothrow_tag_invocable<set_value_t, _Receiver, _As...>);
87 (void)tag_invoke(set_value_t{}, static_cast<_Receiver&&>(__rcvr),
88 static_cast<_As&&>(__as)...);
89 }
90 };
91
92 struct set_error_t
93 {
94 template <class _Fn, class... _Args>
95 requires(sizeof...(_Args) == 1)
96 using __f = __minvoke<_Fn, _Args...>;
97
98 template <class _Receiver, class _Error>
99 requires tag_invocable<set_error_t, _Receiver, _Error>
100 STDEXEC_ATTRIBUTE((host, device, always_inline)) void
operator ()stdexec::__receivers::set_error_t101 operator()(_Receiver&& __rcvr, _Error&& __err) const noexcept
102 {
103 static_assert(nothrow_tag_invocable<set_error_t, _Receiver, _Error>);
104 (void)tag_invoke(set_error_t{}, static_cast<_Receiver&&>(__rcvr),
105 static_cast<_Error&&>(__err));
106 }
107 };
108
109 struct set_stopped_t
110 {
111 template <class _Fn, class... _Args>
112 requires(sizeof...(_Args) == 0)
113 using __f = __minvoke<_Fn, _Args...>;
114
115 template <class _Receiver>
116 requires tag_invocable<set_stopped_t, _Receiver>
117 STDEXEC_ATTRIBUTE((host, device, always_inline)) void
operator ()stdexec::__receivers::set_stopped_t118 operator()(_Receiver&& __rcvr) const noexcept
119 {
120 static_assert(nothrow_tag_invocable<set_stopped_t, _Receiver>);
121 (void)tag_invoke(set_stopped_t{}, static_cast<_Receiver&&>(__rcvr));
122 }
123 };
124 } // namespace __receivers
125
126 using __receivers::set_error_t;
127 using __receivers::set_stopped_t;
128 using __receivers::set_value_t;
129 inline constexpr set_value_t set_value{};
130 inline constexpr set_error_t set_error{};
131 inline constexpr set_stopped_t set_stopped{};
132
133 inline constexpr struct __try_call_t
134 {
135 template <class _Receiver, class _Fun, class... _Args>
136 requires __callable<_Fun, _Args...>
operator ()stdexec::__try_call_t137 void operator()(_Receiver&& __rcvr, _Fun __fun,
138 _Args&&... __args) const noexcept
139 {
140 if constexpr (__nothrow_callable<_Fun, _Args...>)
141 {
142 static_cast<_Fun&&>(__fun)(static_cast<_Args&&>(__args)...);
143 }
144 else
145 {
146 try
147 {
148 static_cast<_Fun&&>(__fun)(static_cast<_Args&&>(__args)...);
149 }
150 catch (...)
151 {
152 set_error(static_cast<_Receiver&&>(__rcvr),
153 std::current_exception());
154 }
155 }
156 }
157 } __try_call{};
158
159 namespace __error__
160 {
161 inline constexpr __mstring __unrecognized_sender_type_diagnostic =
162 "The given type cannot be used as a sender with the given environment "
163 "because the attempt to compute the completion signatures failed."_mstr;
164
165 template <__mstring _Diagnostic = __unrecognized_sender_type_diagnostic>
166 struct _UNRECOGNIZED_SENDER_TYPE_;
167
168 template <class _Sender>
169 struct _WITH_SENDER_;
170
171 template <class... _Senders>
172 struct _WITH_SENDERS_;
173
174 template <class _Env>
175 struct _WITH_ENVIRONMENT_;
176
177 template <class _Ty>
178 struct _WITH_TYPE_;
179
180 template <class _Receiver>
181 struct _WITH_RECEIVER_;
182
183 template <class _Sig>
184 struct _MISSING_COMPLETION_SIGNAL_;
185 } // namespace __error__
186
187 using __error__::_MISSING_COMPLETION_SIGNAL_;
188 using __error__::_UNRECOGNIZED_SENDER_TYPE_;
189 using __error__::_WITH_ENVIRONMENT_;
190 using __error__::_WITH_RECEIVER_;
191 using __error__::_WITH_TYPE_;
192
193 template <class _Sender>
194 using _WITH_SENDER_ = __error__::_WITH_SENDER_<__name_of<_Sender>>;
195
196 template <class... _Senders>
197 using _WITH_SENDERS_ = __error__::_WITH_SENDERS_<__name_of<_Senders>...>;
198
199 /////////////////////////////////////////////////////////////////////////////
200 // completion_signatures
201 namespace __compl_sigs
202 {
203 template <class _Sig>
204 inline constexpr bool __is_compl_sig = false;
205 template <class... _Args>
206 inline constexpr bool __is_compl_sig<set_value_t(_Args...)> = true;
207 template <class _Error>
208 inline constexpr bool __is_compl_sig<set_error_t(_Error)> = true;
209 template <>
210 inline constexpr bool __is_compl_sig<set_stopped_t()> = true;
211 } // namespace __compl_sigs
212
213 template <class _Sig>
214 concept __completion_signature = __compl_sigs::__is_compl_sig<_Sig>;
215
216 template <__completion_signature... _Sigs>
217 struct completion_signatures
218 {};
219
220 namespace __compl_sigs
221 {
222 template <class _TaggedTuple, class _Tag, class... _Ts>
223 auto __as_tagged_tuple_(_Tag (*)(_Ts...), _TaggedTuple*)
224 -> __mconst<__minvoke<_TaggedTuple, _Tag, _Ts...>>;
225
226 template <class _Sig, class _TaggedTuple>
227 using __as_tagged_tuple = decltype(__compl_sigs::__as_tagged_tuple_(
228 static_cast<_Sig*>(nullptr), static_cast<_TaggedTuple*>(nullptr)));
229
230 template <class _TaggedTuple, class _Variant, class... _Sigs>
231 auto __for_all_sigs_(completion_signatures<_Sigs...>*, _TaggedTuple*, _Variant*)
232 -> __mconst<__minvoke<
233 _Variant, __minvoke<__as_tagged_tuple<_Sigs, _TaggedTuple>>...>>;
234
235 template <class _Completions, class _TaggedTuple, class _Variant>
236 using __for_all_sigs = //
237 __minvoke< //
238 decltype(__compl_sigs::__for_all_sigs_( //
239 static_cast<_Completions*>(nullptr),
240 static_cast<_TaggedTuple*>(nullptr),
241 static_cast<_Variant*>(nullptr)))>;
242
243 template <class _Completions, class _TaggedTuple, class _Variant>
244 using __maybe_for_all_sigs =
245 __meval<__for_all_sigs, _Completions, _TaggedTuple, _Variant>;
246 } // namespace __compl_sigs
247
248 template <class _Completions>
249 concept __valid_completion_signatures = //
250 __ok<_Completions> && __is_instance_of<_Completions, completion_signatures>;
251
252 template <class _Completions>
253 using __invalid_completion_signatures_t = //
254 __mbool<!__valid_completion_signatures<_Completions>>;
255
256 template <__mstring _Msg =
257 "Expected an instance of template completion_signatures<>"_mstr>
258 struct _INVALID_COMPLETION_SIGNATURES_TYPE_
259 {
260 template <class... _Completions>
261 using __f = //
262 __mexception<
263 _INVALID_COMPLETION_SIGNATURES_TYPE_<>,
264 __minvoke<__mfind_if<__q<__invalid_completion_signatures_t>,
265 __mcompose<__q<_WITH_TYPE_>, __q<__mfront>>>,
266 _Completions...>>;
267 };
268
269 template <class... _Completions>
270 using __concat_completion_signatures_impl_t = //
271 __minvoke<__if_c<(__valid_completion_signatures<_Completions> && ...),
272 __mconcat<__munique<__q<completion_signatures>>>,
273 _INVALID_COMPLETION_SIGNATURES_TYPE_<>>,
274 _Completions...>;
275
276 template <class... _Completions>
277 struct __concat_completion_signatures_
278 {
279 using __t = __meval<__concat_completion_signatures_impl_t, _Completions...>;
280 };
281
282 template <class... _Completions>
283 using __concat_completion_signatures_t =
284 __t<__concat_completion_signatures_<_Completions...>>;
285
286 /////////////////////////////////////////////////////////////////////////////
287 // [execution.receivers]
288 template <class _Receiver, class _Tag, class... _Args>
289 auto __try_completion(_Tag (*)(_Args...))
290 -> __mexception<_MISSING_COMPLETION_SIGNAL_<_Tag(_Args...)>,
291 _WITH_RECEIVER_<_Receiver>>;
292
293 template <class _Receiver, class _Tag, class... _Args>
294 requires nothrow_tag_invocable<_Tag, _Receiver, _Args...>
295 auto __try_completion(_Tag (*)(_Args...)) -> __msuccess;
296
297 template <class _Receiver, class... _Sigs>
298 auto __try_completions(completion_signatures<_Sigs...>*) -> decltype((
299 __msuccess(), ...,
300 stdexec::__try_completion<_Receiver>(static_cast<_Sigs*>(nullptr))));
301
302 template <class _Sender, class _Env>
303 using __unrecognized_sender_error = //
304 __mexception<_UNRECOGNIZED_SENDER_TYPE_<>, _WITH_SENDER_<_Sender>,
305 _WITH_ENVIRONMENT_<_Env>>;
306
307 template <class _Sender, class _Env>
308 using __completion_signatures_of_t =
309 __call_result_t<get_completion_signatures_t, _Sender, _Env>;
310
311 /////////////////////////////////////////////////////////////////////////////
312 // early sender type-checking
313 template <class _Sender>
314 concept __well_formed_sender = //
315 !__detail::__non_dependent_sender<_Sender> ||
316 __valid_completion_signatures<
317 __completion_signatures_of_t<_Sender, empty_env>>;
318
319 /////////////////////////////////////////////////////////////////////////////
320 // [execution.receivers]
321 struct receiver_t
322 {
323 using receiver_concept = receiver_t; // NOT TO SPEC
324 };
325
326 namespace __detail
327 {
328 template <class _Receiver>
329 concept __enable_receiver = //
330 (STDEXEC_NVHPC(requires { typename _Receiver::receiver_concept; }&&) //
331 derived_from<typename _Receiver::receiver_concept, receiver_t>) ||
332 requires { typename _Receiver::is_receiver; } // back-compat, NOT TO SPEC
333 || STDEXEC_IS_BASE_OF(receiver_t,
334 _Receiver); // NOT TO SPEC, for receiver_adaptor
335 } // namespace __detail
336
337 template <class _Receiver>
338 inline constexpr bool enable_receiver =
339 __detail::__enable_receiver<_Receiver>; // NOT TO SPEC
340
341 template <class _Receiver>
342 concept receiver = enable_receiver<__decay_t<_Receiver>> && //
343 environment_provider<__cref_t<_Receiver>> && //
344 move_constructible<__decay_t<_Receiver>> && //
345 constructible_from<__decay_t<_Receiver>, _Receiver>;
346
347 template <class _Receiver, class _Completions>
348 concept receiver_of = //
349 receiver<_Receiver> && //
350 requires(_Completions* __completions) {
351 {
352 stdexec::__try_completions<__decay_t<_Receiver>>(__completions)
353 } -> __ok;
354 };
355
356 template <class _Receiver, class _Sender>
357 concept __receiver_from =
358 receiver_of<_Receiver,
359 __completion_signatures_of_t<_Sender, env_of_t<_Receiver>>>;
360
361 /////////////////////////////////////////////////////////////////////////////
362 // Some utilities for debugging senders
363 namespace __debug
364 {
365 struct __is_debug_env_t
366 {
tag_invoke(forwarding_query_t,const __is_debug_env_t &)367 friend constexpr auto tag_invoke(forwarding_query_t,
368 const __is_debug_env_t&) noexcept -> bool
369 {
370 return true;
371 }
372 template <class _Env>
373 requires tag_invocable<__is_debug_env_t, const _Env&>
374 auto operator()(const _Env&) const noexcept
375 -> tag_invoke_result_t<__is_debug_env_t, const _Env&>;
376 };
377 template <class _Env>
378 using __debug_env_t =
379 __env::__join_t<__env::__with<bool, __is_debug_env_t>, _Env>;
380
381 template <class _Env>
382 concept __is_debug_env = tag_invocable<__debug::__is_debug_env_t, _Env>;
383
384 struct __completion_signatures
385 {};
386
387 #if STDEXEC_MSVC()
388 // MSVCBUG
389 // https://developercommunity.visualstudio.com/t/Explicit-variable-template-specialisatio/10360032
390 // MSVCBUG
391 // https://developercommunity.visualstudio.com/t/Non-function-type-interpreted-as-functio/10447831
392
393 template <class _Sig>
394 struct __normalize_sig;
395
396 template <class _Tag, class... _Args>
397 struct __normalize_sig<_Tag(_Args...)>
398 {
399 using __type = _Tag (*)(_Args&&...);
400 };
401
402 template <class _Sig>
403 using __normalize_sig_t = typename __normalize_sig<_Sig>::__type;
404 #else
405 template <class _Sig>
406 extern int __normalize_sig;
407
408 template <class _Tag, class... _Args>
409 extern _Tag (*__normalize_sig<_Tag(_Args...)>)(_Args&&...);
410
411 template <class _Sig>
412 using __normalize_sig_t = decltype(__normalize_sig<_Sig>);
413 #endif
414
415 template <class... _Sigs>
416 struct __valid_completions
417 {
418 template <derived_from<__valid_completions> _Self, class _Tag,
419 class... _Args>
420 requires __one_of<_Tag (*)(_Args&&...), _Sigs...>
421 STDEXEC_ATTRIBUTE((host,
tag_invoke(_Tag,_Self &&,_Args &&...)422 device)) friend void tag_invoke(_Tag, _Self&&,
423 _Args&&...) noexcept
424 {
425 STDEXEC_TERMINATE();
426 }
427 };
428
429 template <class _CvrefSenderId, class _Env, class _Completions>
430 struct __debug_receiver
431 {
432 using __t = __debug_receiver;
433 using __id = __debug_receiver;
434 using receiver_concept = receiver_t;
435 };
436
437 template <class _CvrefSenderId, class _Env, class... _Sigs>
438 struct __debug_receiver<_CvrefSenderId, _Env,
439 completion_signatures<_Sigs...>> //
440 : __valid_completions<__normalize_sig_t<_Sigs>...>
441 {
442 using __t = __debug_receiver;
443 using __id = __debug_receiver;
444 using receiver_concept = receiver_t;
445
446 template <same_as<get_env_t> _Tag>
447 STDEXEC_ATTRIBUTE((host, device))
tag_invoke(_Tag,__debug_receiver)448 friend auto tag_invoke(_Tag, __debug_receiver) noexcept
449 -> __debug_env_t<_Env>
450 {
451 STDEXEC_TERMINATE();
452 }
453 };
454
455 struct _COMPLETION_SIGNATURES_MISMATCH_
456 {};
457
458 template <class _Sig>
459 struct _COMPLETION_SIGNATURE_
460 {};
461
462 template <class... _Sigs>
463 struct _IS_NOT_ONE_OF_
464 {};
465
466 template <class _Sender>
467 struct _SIGNAL_SENT_BY_SENDER_
468 {};
469
470 template <class _Warning>
471 [[deprecated(
472 "The sender claims to send a particular set of completions,"
473 " but in actual fact it completes with a result that is not"
474 " one of the declared completion signatures.")]] STDEXEC_ATTRIBUTE((host,
_ATTENTION_()475 device)) void _ATTENTION_() noexcept
476 {}
477
478 template <class _Sig>
479 struct __invalid_completion
480 {
481 struct __t
482 {
483 template <class _CvrefSenderId, class _Env, class... _Sigs>
484 // BUGBUG this works around a recently (aug 2023) introduced regression
485 // in nvc++
486 requires(!__one_of<_Sig, _Sigs...>)
__tstdexec::__debug::__invalid_completion::__t487 __t(__debug_receiver<_CvrefSenderId, _Env,
488 completion_signatures<_Sigs...>>&&) noexcept
489 {
490 using _SenderId = __decay_t<_CvrefSenderId>;
491 using _Sender = stdexec::__t<_SenderId>;
492 using _What = //
493 _WARNING_< //
494 _COMPLETION_SIGNATURES_MISMATCH_,
495 _COMPLETION_SIGNATURE_<_Sig>, _IS_NOT_ONE_OF_<_Sigs...>,
496 _SIGNAL_SENT_BY_SENDER_<__name_of<_Sender>>>;
497 __debug::_ATTENTION_<_What>();
498 }
499 };
500 };
501
502 template <__completion_tag _Tag, class... _Args>
503 STDEXEC_ATTRIBUTE((host, device))
tag_invoke(_Tag,__t<__invalid_completion<_Tag (_Args...)>>,_Args &&...)504 void tag_invoke(_Tag, __t<__invalid_completion<_Tag(_Args...)>>,
505 _Args&&...) noexcept
506 {}
507
508 struct __debug_operation
509 {
510 template <same_as<start_t> _Tag>
tag_invoke(_Tag,__debug_operation &)511 friend void tag_invoke(_Tag, __debug_operation&) noexcept
512 {}
513 };
514
515 ////////////////////////////////////////////////////////////////////////////
516 // `__debug_sender`
517 // ===============
518 //
519 // Understanding why a particular sender doesn't connect to a particular
520 // receiver is nigh impossible in the current design due to limitations in
521 // how the compiler reports overload resolution failure in the presence of
522 // constraints. `__debug_sender` is a utility to assist with the process. It
523 // gives you the deep template instantiation backtrace that you need to
524 // understand where in a chain of senders the problem is occurring.
525 //
526 // ```c++
527 // template <class _Sigs, class _Env = empty_env, class _Sender>
528 // void __debug_sender(_Sender&& __sndr, _Env = {});
529 //
530 // template <class _Env = empty_env, class _Sender>
531 // void __debug_sender(_Sender&& __sndr, _Env = {});
532 // ```
533 //
534 // **Usage:**
535 //
536 // To find out where in a chain of senders a sender is failing to connect
537 // to a receiver, pass it to `__debug_sender`, optionally with an
538 // environment argument; e.g. `__debug_sender(sndr [, env])`
539 //
540 // To find out why a sender will not connect to a receiver of a particular
541 // signature, specify the set of completion signatures as an explicit template
542 // argument that names an instantiation of `completion_signatures`; e.g.:
543 // `__debug_sender<completion_signatures<set_value_t(int)>>(sndr [, env])`.
544 //
545 // **How it works:**
546 //
547 // The `__debug_sender` function `connect`'s the sender to a
548 // `__debug_receiver`, whose environment is augmented with a special
549 // `__is_debug_env_t` query. An additional fall-back overload is added to
550 // the `connect` CPO that recognizes receivers whose environments respond to
551 // that query and lets them through. Then in a non-immediate context, it
552 // looks for a `tag_invoke(connect_t...)` overload for the input sender and
553 // receiver. This will recurse until it hits the `tag_invoke` call that is
554 // causing the failure.
555 //
556 // At least with clang, this gives me a nice backtrace, at the bottom of
557 // which is the faulty `tag_invoke` overload with a mention of the
558 // constraint that failed.
559 template <class _Sigs, class _Env = empty_env, class _Sender>
__debug_sender(_Sender && __sndr,const _Env &={})560 void __debug_sender(_Sender&& __sndr, const _Env& = {})
561 {
562 if constexpr (!__is_debug_env<_Env>)
563 {
564 if (sizeof(_Sender) == ~0u)
565 { // never true
566 using _Receiver =
567 __debug_receiver<__cvref_id<_Sender>, _Env, _Sigs>;
568 using _Operation = connect_result_t<_Sender, _Receiver>;
569 // static_assert(receiver_of<_Receiver, _Sigs>);
570 if constexpr (!same_as<_Operation, __debug_operation>)
571 {
572 auto __op = connect(static_cast<_Sender&&>(__sndr),
573 _Receiver{});
574 start(__op);
575 }
576 }
577 }
578 }
579
580 template <class _Env = empty_env, class _Sender>
__debug_sender(_Sender && __sndr,const _Env &={})581 void __debug_sender(_Sender&& __sndr, const _Env& = {})
582 {
583 if constexpr (!__is_debug_env<_Env>)
584 {
585 if (sizeof(_Sender) == ~0)
586 { // never true
587 using _Sigs =
588 __completion_signatures_of_t<_Sender, __debug_env_t<_Env>>;
589 if constexpr (!same_as<_Sigs, __debug::__completion_signatures>)
590 {
591 using _Receiver =
592 __debug_receiver<__cvref_id<_Sender>, _Env, _Sigs>;
593 using _Operation = connect_result_t<_Sender, _Receiver>;
594 // static_assert(receiver_of<_Receiver, _Sigs>);
595 if constexpr (!same_as<_Operation, __debug_operation>)
596 {
597 auto __op = connect(static_cast<_Sender&&>(__sndr),
598 _Receiver{});
599 start(__op);
600 }
601 }
602 }
603 }
604 }
605 } // namespace __debug
606
607 using __debug::__debug_sender;
608 using __debug::__is_debug_env;
609
610 /////////////////////////////////////////////////////////////////////////////
611 // dependent_domain
612 struct dependent_domain
613 {
614 template <sender_expr _Sender, class _Env>
615 requires same_as<__early_domain_of_t<_Sender>, dependent_domain>
616 STDEXEC_ATTRIBUTE((always_inline)) decltype(auto)
617 transform_sender(_Sender&& __sndr, const _Env& __env) const;
618 };
619
620 /////////////////////////////////////////////////////////////////////////////
621 // [execution.transform_sender]
622 namespace __domain
623 {
624 struct __transform_env
625 {
626 template <class _Domain, class _Sender, class _Env>
627 STDEXEC_ATTRIBUTE((always_inline))
628 /*constexpr*/
operator ()stdexec::__domain::__transform_env629 decltype(auto) operator()(_Domain __dom, _Sender&& __sndr,
630 _Env&& __env) const noexcept
631 {
632 if constexpr (__domain::__has_transform_env<_Domain, _Sender, _Env>)
633 {
634 return __dom.transform_env(static_cast<_Sender&&>(__sndr),
635 static_cast<_Env&&>(__env));
636 }
637 else
638 {
639 return default_domain().transform_env(
640 static_cast<_Sender&&>(__sndr), static_cast<_Env&&>(__env));
641 }
642 }
643 };
644
645 struct __transform_sender_1
646 {
647 template <class _Domain, class _Sender, class... _Env>
648 STDEXEC_ATTRIBUTE((always_inline))
649 /*constexpr*/
operator ()stdexec::__domain::__transform_sender_1650 decltype(auto) operator()(_Domain __dom, _Sender&& __sndr,
651 const _Env&... __env) const
652 {
653 if constexpr (__domain::__has_transform_sender<_Domain, _Sender,
654 _Env...>)
655 {
656 return __dom.transform_sender(static_cast<_Sender&&>(__sndr),
657 __env...);
658 }
659 else
660 {
661 return default_domain().transform_sender(
662 static_cast<_Sender&&>(__sndr), __env...);
663 }
664 }
665 };
666
667 template <class _Ty, class _Uy>
668 concept __decay_same_as = same_as<__decay_t<_Ty>, __decay_t<_Uy>>;
669
670 struct __transform_sender
671 {
672 template <class _Self = __transform_sender, class _Domain, class _Sender,
673 class... _Env>
674 STDEXEC_ATTRIBUTE((always_inline))
675 /*constexpr*/
operator ()stdexec::__domain::__transform_sender676 decltype(auto) operator()(_Domain __dom, _Sender&& __sndr,
677 const _Env&... __env) const
678 {
679 using _Sender2 = __call_result_t<__transform_sender_1, _Domain, _Sender,
680 const _Env&...>;
681 // If the transformation doesn't change the sender's type, then do not
682 // apply the transform recursively.
683 if constexpr (__decay_same_as<_Sender, _Sender2>)
684 {
685 return __transform_sender_1()(__dom, static_cast<_Sender&&>(__sndr),
686 __env...);
687 }
688 else
689 {
690 // We transformed the sender and got back a different sender.
691 // Transform that one too.
692 return _Self()(__dom,
693 __transform_sender_1()(
694 __dom, static_cast<_Sender&&>(__sndr), __env...),
695 __env...);
696 }
697 }
698 };
699
700 struct __transform_dependent_sender
701 {
702 // If we are doing a lazy customization of a type whose domain is
703 // value-dependent (e.g., let_value), first transform the sender to
704 // determine the domain. Then continue transforming the sender with the
705 // requested domain.
706 template <class _Domain, sender_expr _Sender, class _Env>
707 requires same_as<__early_domain_of_t<_Sender>, dependent_domain>
operator ()stdexec::__domain::__transform_dependent_sender708 /*constexpr*/ auto operator()(_Domain __dom, _Sender&& __sndr,
709 const _Env& __env) const -> decltype(auto)
710 {
711 static_assert(__none_of<_Domain, dependent_domain>);
712 return __transform_sender()(__dom,
713 dependent_domain().transform_sender(
714 static_cast<_Sender&&>(__sndr), __env),
715 __env);
716 }
717 };
718 } // namespace __domain
719
720 /////////////////////////////////////////////////////////////////////////////
721 // [execution.transform_sender]
722 inline constexpr struct transform_sender_t :
723 __domain::__transform_sender,
724 __domain::__transform_dependent_sender
725 {
726 using __domain::__transform_sender::operator();
727 using __domain::__transform_dependent_sender::operator();
728 } transform_sender{};
729
730 template <class _Domain, class _Sender, class... _Env>
731 using transform_sender_result_t =
732 __call_result_t<transform_sender_t, _Domain, _Sender, _Env...>;
733
734 inline constexpr __domain::__transform_env transform_env{};
735
736 struct _CHILD_SENDERS_WITH_DIFFERENT_DOMAINS_
737 {};
738
739 template <sender_expr _Sender, class _Env>
740 requires same_as<__early_domain_of_t<_Sender>, dependent_domain>
transform_sender(_Sender && __sndr,const _Env & __env) const741 auto dependent_domain::transform_sender(_Sender&& __sndr,
742 const _Env& __env) const
743 -> decltype(auto)
744 {
745 // apply any algorithm-specific transformation to the environment
746 const auto& __env2 = transform_env(*this, static_cast<_Sender&&>(__sndr),
747 __env);
748
749 // recursively transform the sender to determine the domain
750 return __sexpr_apply(static_cast<_Sender&&>(__sndr),
751 [&]<class _Tag, class _Data, class... _Childs>(
752 _Tag, _Data&& __data, _Childs&&... __childs) {
753 // TODO: propagate meta-exceptions here:
754 auto __sndr2 = __make_sexpr<_Tag>(
755 static_cast<_Data&&>(__data),
756 __domain::__transform_sender()(
757 *this, static_cast<_Childs&&>(__childs), __env2)...);
758 using _Sender2 = decltype(__sndr2);
759
760 auto __domain2 = __sexpr_apply(__sndr2, __domain::__common_domain_fn());
761 using _Domain2 = decltype(__domain2);
762
763 if constexpr (same_as<_Domain2, __none_such>)
764 {
765 return __mexception<_CHILD_SENDERS_WITH_DIFFERENT_DOMAINS_,
766 _WITH_SENDER_<_Sender2>>();
767 }
768 else
769 {
770 return __domain::__transform_sender()(__domain2, std::move(__sndr2),
771 __env);
772 }
773 });
774 }
775
776 /////////////////////////////////////////////////////////////////////////////
777 template <class _Tag, class _Domain, class _Sender, class... _Args>
778 concept __has_implementation_for =
779 __domain::__has_apply_sender<_Domain, _Tag, _Sender, _Args...> ||
780 __domain::__has_apply_sender<default_domain, _Tag, _Sender, _Args...>;
781
782 /////////////////////////////////////////////////////////////////////////////
783 // [execution.apply_sender]
784 inline constexpr struct apply_sender_t
785 {
786 template <class _Domain, class _Tag, class _Sender, class... _Args>
787 requires __has_implementation_for<_Tag, _Domain, _Sender, _Args...>
788 STDEXEC_ATTRIBUTE((always_inline))
789 /*constexpr*/
790 decltype(auto)
operator ()stdexec::apply_sender_t791 operator()(_Domain __dom, _Tag, _Sender&& __sndr,
792 _Args&&... __args) const
793 {
794 if constexpr (__domain::__has_apply_sender<_Domain, _Tag, _Sender,
795 _Args...>)
796 {
797 return __dom.apply_sender(_Tag(), static_cast<_Sender&&>(__sndr),
798 static_cast<_Args&&>(__args)...);
799 }
800 else
801 {
802 return default_domain().apply_sender(
803 _Tag(), static_cast<_Sender&&>(__sndr),
804 static_cast<_Args&&>(__args)...);
805 }
806 }
807 } apply_sender{};
808
809 template <class _Domain, class _Tag, class _Sender, class... _Args>
810 using apply_sender_result_t =
811 __call_result_t<apply_sender_t, _Domain, _Tag, _Sender, _Args...>;
812
813 /////////////////////////////////////////////////////////////////////////////
814 // [execution.sndtraits]
815 namespace __get_completion_signatures
816 {
817 template <class _Sender, class _Env>
818 using __tfx_sender =
819 transform_sender_result_t<__late_domain_of_t<_Sender, _Env>, _Sender, _Env>;
820
821 template <class _Sender, class _Env>
822 concept __with_tag_invoke = //
823 tag_invocable<get_completion_signatures_t, __tfx_sender<_Sender, _Env>,
824 _Env>;
825
826 template <class _Sender, class _Env>
827 using __member_alias_t = //
828 typename __decay_t<__tfx_sender<_Sender, _Env>>::completion_signatures;
829
830 template <class _Sender, class _Env = empty_env>
831 concept __with_member_alias = __mvalid<__member_alias_t, _Sender, _Env>;
832
833 struct get_completion_signatures_t
834 {
835 template <class _Sender, class _Env>
__implstdexec::__get_completion_signatures::get_completion_signatures_t836 static auto __impl()
837 {
838 static_assert(sizeof(_Sender),
839 "Incomplete type used with get_completion_signatures");
840 static_assert(sizeof(_Env),
841 "Incomplete type used with get_completion_signatures");
842
843 // Compute the type of the transformed sender:
844 using _TfxSender = __tfx_sender<_Sender, _Env>;
845
846 if constexpr (__merror<_TfxSender>)
847 {
848 // Computing the type of the transformed sender returned an error
849 // type. Propagate it.
850 return static_cast<_TfxSender (*)()>(nullptr);
851 }
852 else if constexpr (__with_tag_invoke<_Sender, _Env>)
853 {
854 using _Result = tag_invoke_result_t<get_completion_signatures_t,
855 _TfxSender, _Env>;
856 return static_cast<_Result (*)()>(nullptr);
857 }
858 else if constexpr (__with_member_alias<_Sender, _Env>)
859 {
860 using _Result = __member_alias_t<_Sender, _Env>;
861 return static_cast<_Result (*)()>(nullptr);
862 }
863 else if constexpr (__awaitable<_Sender, __env::__promise<_Env>>)
864 {
865 using _AwaitResult =
866 __await_result_t<_Sender, __env::__promise<_Env>>;
867 using _Result = completion_signatures<
868 // set_value_t() or set_value_t(T)
869 __minvoke<__remove<void, __qf<set_value_t>>, _AwaitResult>,
870 set_error_t(std::exception_ptr), set_stopped_t()>;
871 return static_cast<_Result (*)()>(nullptr);
872 }
873 else if constexpr (__is_debug_env<_Env>)
874 {
875 using __tag_invoke::tag_invoke;
876 // This ought to cause a hard error that indicates where the problem
877 // is.
878 using _Completions [[maybe_unused]] =
879 tag_invoke_result_t<get_completion_signatures_t, _Sender, _Env>;
880 return static_cast<__debug::__completion_signatures (*)()>(nullptr);
881 }
882 else
883 {
884 using _Result =
885 __mexception<_UNRECOGNIZED_SENDER_TYPE_<>,
886 _WITH_SENDER_<_Sender>, _WITH_ENVIRONMENT_<_Env>>;
887 return static_cast<_Result (*)()>(nullptr);
888 }
889 }
890
891 // NOT TO SPEC: if we're unable to compute the completion signatures,
892 // return an error type instead of SFINAE.
893 template <class _Sender, class _Env = empty_env>
operator ()stdexec::__get_completion_signatures::get_completion_signatures_t894 constexpr auto operator()(_Sender&&, const _Env&) const noexcept
895 -> decltype(__impl<_Sender, _Env>()())
896 {
897 return {};
898 }
899 };
900 } // namespace __get_completion_signatures
901
902 using __get_completion_signatures::get_completion_signatures_t;
903 inline constexpr get_completion_signatures_t get_completion_signatures{};
904
905 /////////////////////////////////////////////////////////////////////////////
906 // [execution.senders]
907 struct sender_t
908 {
909 using sender_concept = sender_t;
910 };
911
912 namespace __detail
913 {
914 template <class _Sender>
915 concept __enable_sender = //
916 derived_from<typename _Sender::sender_concept, sender_t> ||
917 requires { typename _Sender::is_sender; } // NOT TO SPEC back compat
918 || __awaitable<_Sender, __env::__promise<empty_env>>;
919 } // namespace __detail
920
921 template <class _Sender>
922 inline constexpr bool enable_sender = __detail::__enable_sender<_Sender>;
923
924 template <class _Sender, class _Env = empty_env>
925 concept sender = enable_sender<__decay_t<_Sender>> && //
926 environment_provider<__cref_t<_Sender>> && //
927 __detail::__consistent_completion_domains<_Sender> && //
928 move_constructible<__decay_t<_Sender>> && //
929 constructible_from<__decay_t<_Sender>, _Sender>;
930
931 template <class _Sender, class _Env = empty_env>
932 concept sender_in = //
933 sender<_Sender> && //
934 requires(_Sender&& __sndr, _Env&& __env) {
935 {
936 get_completion_signatures(static_cast<_Sender&&>(__sndr),
937 static_cast<_Env&&>(__env))
938 } -> __valid_completion_signatures;
939 };
940
941 #if STDEXEC_ENABLE_EXTRA_TYPE_CHECKING()
942 // __checked_completion_signatures is for catching logic bugs in a typed
943 // sender's metadata. If sender<S> and sender_in<S, Ctx> are both true, then
944 // they had better report the same metadata. This completion signatures wrapper
945 // enforces that at compile time.
946 template <class _Sender, class _Env>
__checked_completion_signatures(_Sender && __sndr,const _Env & __env)947 auto __checked_completion_signatures(_Sender&& __sndr,
948 const _Env& __env) noexcept
949 {
950 using __completions_t = __completion_signatures_of_t<_Sender, _Env>;
951 stdexec::__debug_sender<__completions_t>(static_cast<_Sender&&>(__sndr),
952 __env);
953 return __completions_t{};
954 }
955
956 template <class _Sender, class _Env = empty_env>
957 requires sender_in<_Sender, _Env>
958 using completion_signatures_of_t =
959 decltype(stdexec::__checked_completion_signatures(__declval<_Sender>(),
960 __declval<_Env>()));
961 #else
962 template <class _Sender, class _Env = empty_env>
963 requires sender_in<_Sender, _Env>
964 using completion_signatures_of_t = __completion_signatures_of_t<_Sender, _Env>;
965 #endif
966
967 struct __not_a_variant
968 {
969 __not_a_variant() = delete;
970 };
971 template <class... _Ts>
972 using __variant = //
973 __minvoke<__if_c<sizeof...(_Ts) != 0,
974 __transform<__q<__decay_t>, __munique<__q<std::variant>>>,
975 __mconst<__not_a_variant>>,
976 _Ts...>;
977
978 using __nullable_variant_t =
979 __munique<__mbind_front_q<std::variant, std::monostate>>;
980
981 template <class... _Ts>
982 using __decayed_tuple = __meval<std::tuple, __decay_t<_Ts>...>;
983
984 template <class _Tag, class _Tuple>
985 struct __select_completions_for
986 {
987 template <same_as<_Tag> _Tag2, class... _Args>
988 using __f = __minvoke<_Tag2, _Tuple, _Args...>;
989 };
990
991 template <class _Tuple>
992 struct __invoke_completions
993 {
994 template <class _Tag, class... _Args>
995 using __f = __minvoke<_Tag, _Tuple, _Args...>;
996 };
997
998 template <class _Tag, class _Tuple>
999 using __select_completions_for_or = //
1000 __with_default<__select_completions_for<_Tag, _Tuple>, __>;
1001
1002 template <class _Tag, class _Completions>
1003 using __only_gather_signal = //
1004 __compl_sigs::__maybe_for_all_sigs<
1005 _Completions, __select_completions_for_or<_Tag, __qf<_Tag>>,
1006 __remove<__, __q<completion_signatures>>>;
1007
1008 template <class _Tag, class _Completions, class _Tuple, class _Variant>
1009 using __gather_signal = //
1010 __compl_sigs::__maybe_for_all_sigs<__only_gather_signal<_Tag, _Completions>,
1011 __invoke_completions<_Tuple>, _Variant>;
1012
1013 template <class _Tag, class _Sender, class _Env, class _Tuple, class _Variant>
1014 using __gather_completions_for = //
1015 __meval< //
1016 __gather_signal, _Tag, __completion_signatures_of_t<_Sender, _Env>,
1017 _Tuple, _Variant>;
1018
1019 template < //
1020 class _Sender, //
1021 class _Env = empty_env, //
1022 class _Tuple = __q<__decayed_tuple>, //
1023 class _Variant = __q<__variant>>
1024 using __try_value_types_of_t = //
1025 __gather_completions_for<set_value_t, _Sender, _Env, _Tuple, _Variant>;
1026
1027 template < //
1028 class _Sender, //
1029 class _Env = empty_env, //
1030 class _Tuple = __q<__decayed_tuple>, //
1031 class _Variant = __q<__variant>>
1032 requires sender_in<_Sender, _Env>
1033 using __value_types_of_t = //
1034 __msuccess_or_t<__try_value_types_of_t<_Sender, _Env, _Tuple, _Variant>>;
1035
1036 template <class _Sender, class _Env = empty_env,
1037 class _Variant = __q<__variant>>
1038 using __try_error_types_of_t =
1039 __gather_completions_for<set_error_t, _Sender, _Env, __q<__midentity>,
1040 _Variant>;
1041
1042 template <class _Sender, class _Env = empty_env,
1043 class _Variant = __q<__variant>>
1044 requires sender_in<_Sender, _Env>
1045 using __error_types_of_t =
1046 __msuccess_or_t<__try_error_types_of_t<_Sender, _Env, _Variant>>;
1047
1048 template < //
1049 class _Sender, //
1050 class _Env = empty_env, //
1051 template <class...> class _Tuple = __decayed_tuple, //
1052 template <class...> class _Variant = __variant>
1053 requires sender_in<_Sender, _Env>
1054 using value_types_of_t =
1055 __value_types_of_t<_Sender, _Env, __q<_Tuple>, __q<_Variant>>;
1056
1057 template <class _Sender, class _Env = empty_env,
1058 template <class...> class _Variant = __variant>
1059 requires sender_in<_Sender, _Env>
1060 using error_types_of_t = __error_types_of_t<_Sender, _Env, __q<_Variant>>;
1061
1062 template <class _Tag, class _Sender, class _Env = empty_env>
1063 using __try_count_of = //
1064 __compl_sigs::__maybe_for_all_sigs<
1065 __completion_signatures_of_t<_Sender, _Env>, __q<__mfront>,
1066 __mcount<_Tag>>;
1067
1068 template <class _Tag, class _Sender, class _Env = empty_env>
1069 requires sender_in<_Sender, _Env>
1070 using __count_of = __msuccess_or_t<__try_count_of<_Tag, _Sender, _Env>>;
1071
1072 template <class _Tag, class _Sender, class _Env = empty_env>
1073 requires __mvalid<__count_of, _Tag, _Sender, _Env>
1074 inline constexpr bool __sends = (__v<__count_of<_Tag, _Sender, _Env>> != 0);
1075
1076 template <class _Sender, class _Env = empty_env>
1077 requires __mvalid<__count_of, set_stopped_t, _Sender, _Env>
1078 inline constexpr bool sends_stopped = __sends<set_stopped_t, _Sender, _Env>;
1079
1080 template <class _Sender, class _Env = empty_env>
1081 using __single_sender_value_t =
1082 __value_types_of_t<_Sender, _Env, __msingle_or<void>, __q<__msingle>>;
1083
1084 template <class _Sender, class _Env = empty_env>
1085 using __single_value_variant_sender_t =
1086 value_types_of_t<_Sender, _Env, __types, __msingle>;
1087
1088 template <class _Sender, class _Env = empty_env>
1089 concept __single_typed_sender =
1090 sender_in<_Sender, _Env> &&
1091 __mvalid<__single_sender_value_t, _Sender, _Env>;
1092
1093 template <class _Sender, class _Env = empty_env>
1094 concept __single_value_variant_sender =
1095 sender_in<_Sender, _Env> &&
1096 __mvalid<__single_value_variant_sender_t, _Sender, _Env>;
1097
1098 template <class... Errs>
1099 using __nofail = __mbool<sizeof...(Errs) == 0>;
1100
1101 template <class _Sender, class _Env = empty_env>
1102 concept __nofail_sender = sender_in<_Sender, _Env> &&
1103 (__v<error_types_of_t<_Sender, _Env, __nofail>>);
1104
1105 /////////////////////////////////////////////////////////////////////////////
1106 namespace __compl_sigs
1107 {
1108 template <class... _Args>
1109 using __default_set_value = completion_signatures<set_value_t(_Args...)>;
1110
1111 template <class _Error>
1112 using __default_set_error = completion_signatures<set_error_t(_Error)>;
1113
1114 template <__valid_completion_signatures... _Sigs>
1115 using __ensure_concat_ =
1116 __minvoke<__mconcat<__q<completion_signatures>>, _Sigs...>;
1117
1118 template <class... _Sigs>
1119 using __ensure_concat = __mtry_eval<__ensure_concat_, _Sigs...>;
1120
1121 template <class _Sender, class _Env, class _Sigs, class _SetVal, class _SetErr,
1122 class _SetStp>
1123 using __compl_sigs_impl = //
1124 __concat_completion_signatures_t<
1125 _Sigs,
1126 __mtry_eval<__try_value_types_of_t, _Sender, _Env, _SetVal,
1127 __q<__ensure_concat>>,
1128 __mtry_eval<__try_error_types_of_t, _Sender, _Env,
1129 __transform<_SetErr, __q<__ensure_concat>>>,
1130 __if<__try_count_of<set_stopped_t, _Sender, _Env>, _SetStp,
1131 completion_signatures<>>>;
1132
1133 template <class _Sender, class _Env, class _Sigs, class _SetVal, class _SetErr,
1134 class _SetStp>
1135 requires __mvalid<__compl_sigs_impl, _Sender, _Env, _Sigs, _SetVal, _SetErr,
1136 _SetStp>
1137 extern __compl_sigs_impl<_Sender, _Env, _Sigs, _SetVal, _SetErr, _SetStp>
1138 __compl_sigs_v;
1139
1140 template <class _Sender, class _Env, class _Sigs, class _SetVal, class _SetErr,
1141 class _SetStp>
1142 using __compl_sigs_t =
1143 decltype(__compl_sigs_v<_Sender, _Env, _Sigs, _SetVal, _SetErr, _SetStp>);
1144
1145 template < //
1146 class _Sender, //
1147 class _Env = empty_env, //
1148 class _Sigs = completion_signatures<>, //
1149 class _SetValue = __q<__default_set_value>, //
1150 class _SetError = __q<__default_set_error>, //
1151 class _SetStopped = completion_signatures<set_stopped_t()>> //
1152 using __try_make_completion_signatures = //
1153 __meval<__compl_sigs_t, _Sender, _Env, _Sigs, _SetValue, _SetError,
1154 _SetStopped>;
1155 } // namespace __compl_sigs
1156
1157 using __compl_sigs::__try_make_completion_signatures;
1158
1159 /////////////////////////////////////////////////////////////////////////////
1160 // NOT TO SPEC
1161 //
1162 // make_completion_signatures
1163 // ==========================
1164 //
1165 // `make_completion_signatures` takes a sender, and environment, and a bunch
1166 // of other template arguments for munging the completion signatures of a
1167 // sender in interesting ways.
1168 //
1169 // ```c++
1170 // template <class... Args>
1171 // using __default_set_value = completion_signatures<set_value_t(Args...)>;
1172 //
1173 // template <class Err>
1174 // using __default_set_error = completion_signatures<set_error_t(Err)>;
1175 //
1176 // template <
1177 // sender Sndr,
1178 // class Env = empty_env,
1179 // class AddlSigs = completion_signatures<>,
1180 // template <class...> class SetValue = __default_set_value,
1181 // template <class> class SetError = __default_set_error,
1182 // class SetStopped = completion_signatures<set_stopped_t()>>
1183 // requires sender_in<Sndr, Env>
1184 // using make_completion_signatures =
1185 // completion_signatures< ... >;
1186 // ```
1187 //
1188 // * `SetValue` : an alias template that accepts a set of value types and
1189 // returns an instance of `completion_signatures`.
1190 // * `SetError` : an alias template that accepts an error types and returns a
1191 // an instance of `completion_signatures`.
1192 // * `SetStopped` : an instantiation of `completion_signatures` with a list
1193 // of completion signatures `Sigs...` to the added to the list if the
1194 // sender can complete with a stopped signal.
1195 // * `AddlSigs` : an instantiation of `completion_signatures` with a list of
1196 // completion signatures `Sigs...` to the added to the list
1197 // unconditionally.
1198 //
1199 // `make_completion_signatures` does the following:
1200 // * Let `VCs...` be a pack of the `completion_signatures` types in the
1201 // `__typelist` named by `value_types_of_t<Sndr, Env, SetValue,
1202 // __typelist>`, and let `Vs...` be the concatenation of the packs that are
1203 // template arguments to each `completion_signature` in `VCs...`.
1204 // * Let `ECs...` be a pack of the `completion_signatures` types in the
1205 // `__typelist` named by `error_types_of_t<Sndr, Env, __errorlist>`, where
1206 // `__errorlist` is an alias template such that `__errorlist<Ts...>` names
1207 // `__typelist<SetError<Ts>...>`, and let `Es...` by the concatenation of
1208 // the packs that are the template arguments to each `completion_signature`
1209 // in `ECs...`.
1210 // * Let `Ss...` be an empty pack if `sends_stopped<Sndr, Env>` is
1211 // `false`; otherwise, a pack containing the template arguments of the
1212 // `completion_signatures` instantiation named by `SetStopped`.
1213 // * Let `MoreSigs...` be a pack of the template arguments of the
1214 // `completion_signatures` instantiation named by `AddlSigs`.
1215 //
1216 // Then `make_completion_signatures<Sndr, Env, AddlSigs, SetValue, SetError,
1217 // SendsStopped>` names the type `completion_signatures< Sigs... >` where
1218 // `Sigs...` is the unique set of types in `[Vs..., Es..., Ss...,
1219 // MoreSigs...]`.
1220 //
1221 // If any of the above type computations are ill-formed,
1222 // `make_completion_signatures<Sndr, Env, AddlSigs, SetValue, SetError,
1223 // SendsStopped>` is an alias for an empty struct
1224 template < //
1225 class _Sender, //
1226 class _Env = empty_env, //
1227 __valid_completion_signatures _Sigs = completion_signatures<>, //
1228 template <class...> class _SetValue = __compl_sigs::__default_set_value, //
1229 template <class> class _SetError = __compl_sigs::__default_set_error, //
1230 __valid_completion_signatures _SetStopped =
1231 completion_signatures<set_stopped_t()>>
1232 requires sender_in<_Sender, _Env>
1233 using make_completion_signatures = //
1234 __msuccess_or_t< //
1235 __try_make_completion_signatures<_Sender, _Env, _Sigs, __q<_SetValue>,
1236 __q<_SetError>, _SetStopped>>;
1237
1238 // Needed fairly often
1239 using __with_exception_ptr =
1240 completion_signatures<set_error_t(std::exception_ptr)>;
1241
1242 /////////////////////////////////////////////////////////////////////////////
1243 // [execution.senders.schedule]
1244 namespace __schedule
1245 {
1246 struct schedule_t
1247 {
1248 template <class _Scheduler>
1249 requires tag_invocable<schedule_t, _Scheduler>
1250 STDEXEC_ATTRIBUTE((host, device)) auto
operator ()stdexec::__schedule::schedule_t1251 operator()(_Scheduler&& __sched) const
1252 noexcept(nothrow_tag_invocable<schedule_t, _Scheduler>)
1253 {
1254 static_assert(sender<tag_invoke_result_t<schedule_t, _Scheduler>>);
1255 return tag_invoke(schedule_t{}, static_cast<_Scheduler&&>(__sched));
1256 }
1257
tag_invoke(forwarding_query_t,schedule_t)1258 friend constexpr auto tag_invoke(forwarding_query_t, schedule_t) -> bool
1259 {
1260 return false;
1261 }
1262 };
1263 } // namespace __schedule
1264
1265 using __schedule::schedule_t;
1266 inline constexpr schedule_t schedule{};
1267
1268 // NOT TO SPEC
1269 template <class _Tag, const auto& _Predicate>
1270 concept tag_category = //
1271 requires {
1272 typename __mbool<bool{_Predicate(_Tag{})}>;
1273 requires bool {
1274 _Predicate(_Tag{})
1275 };
1276 };
1277
1278 /////////////////////////////////////////////////////////////////////////////
1279 // [execution.schedulers]
1280 template <class _Scheduler>
1281 concept __has_schedule = //
1282 requires(_Scheduler&& __sched) {
1283 {
1284 schedule(static_cast<_Scheduler&&>(__sched))
1285 } -> sender;
1286 };
1287
1288 template <class _Scheduler>
1289 concept __sender_has_completion_scheduler =
1290 requires(_Scheduler&& __sched,
1291 get_completion_scheduler_t<set_value_t>&& __tag) {
1292 {
1293 tag_invoke(std::move(__tag),
1294 get_env(schedule(static_cast<_Scheduler&&>(__sched))))
1295 } -> same_as<__decay_t<_Scheduler>>;
1296 };
1297
1298 template <class _Scheduler>
1299 concept scheduler = //
1300 __has_schedule<_Scheduler> && //
1301 __sender_has_completion_scheduler<_Scheduler> && //
1302 equality_comparable<__decay_t<_Scheduler>> && //
1303 copy_constructible<__decay_t<_Scheduler>>;
1304
1305 template <scheduler _Scheduler>
1306 using schedule_result_t = __call_result_t<schedule_t, _Scheduler>;
1307
1308 template <receiver _Receiver>
1309 using __current_scheduler_t =
1310 __call_result_t<get_scheduler_t, env_of_t<_Receiver>>;
1311
1312 template <class _SchedulerProvider>
1313 concept __scheduler_provider = //
1314 requires(const _SchedulerProvider& __sp) {
1315 {
1316 get_scheduler(__sp)
1317 } -> scheduler;
1318 };
1319
1320 /////////////////////////////////////////////////////////////////////////////
1321 // [execution.op_state]
1322 namespace __start
1323 {
1324 struct start_t
1325 {
1326 template <class _Op>
1327 requires tag_invocable<start_t, _Op&>
operator ()stdexec::__start::start_t1328 STDEXEC_ATTRIBUTE((always_inline)) void operator()(_Op& __op) const noexcept
1329 {
1330 static_assert(nothrow_tag_invocable<start_t, _Op&>);
1331 (void)tag_invoke(start_t{}, __op);
1332 }
1333 };
1334 } // namespace __start
1335
1336 using __start::start_t;
1337 inline constexpr start_t start{};
1338
1339 /////////////////////////////////////////////////////////////////////////////
1340 // [execution.op_state]
1341 template <class _Op>
1342 concept operation_state = //
1343 destructible<_Op> && //
1344 std::is_object_v<_Op> && //
1345 requires(_Op& __op) { //
1346 start(__op);
1347 };
1348
1349 #if !STDEXEC_STD_NO_COROUTINES_
1350 /////////////////////////////////////////////////////////////////////////////
1351 // __connect_awaitable_
1352 namespace __connect_awaitable_
1353 {
1354 struct __promise_base
1355 {
initial_suspendstdexec::__connect_awaitable_::__promise_base1356 auto initial_suspend() noexcept -> __coro::suspend_always
1357 {
1358 return {};
1359 }
1360
final_suspendstdexec::__connect_awaitable_::__promise_base1361 [[noreturn]] auto final_suspend() noexcept -> __coro::suspend_always
1362 {
1363 std::terminate();
1364 }
1365
unhandled_exceptionstdexec::__connect_awaitable_::__promise_base1366 [[noreturn]] void unhandled_exception() noexcept
1367 {
1368 std::terminate();
1369 }
1370
return_voidstdexec::__connect_awaitable_::__promise_base1371 [[noreturn]] void return_void() noexcept
1372 {
1373 std::terminate();
1374 }
1375 };
1376
1377 struct __operation_base
1378 {
1379 __coro::coroutine_handle<> __coro_;
1380
__operation_basestdexec::__connect_awaitable_::__operation_base1381 explicit __operation_base(__coro::coroutine_handle<> __hcoro) noexcept :
1382 __coro_(__hcoro)
1383 {}
1384
__operation_basestdexec::__connect_awaitable_::__operation_base1385 __operation_base(__operation_base&& __other) noexcept :
1386 __coro_(std::exchange(__other.__coro_, {}))
1387 {}
1388
~__operation_basestdexec::__connect_awaitable_::__operation_base1389 ~__operation_base()
1390 {
1391 if (__coro_)
1392 {
1393 #if STDEXEC_MSVC()
1394 // MSVCBUG
1395 // https://developercommunity.visualstudio.com/t/Double-destroy-of-a-local-in-coroutine-d/10456428
1396
1397 // Reassign __coro_ before calling destroy to make the mutation
1398 // observable and to hopefully ensure that the compiler does not
1399 // eliminate it.
1400 auto __coro = __coro_;
1401 __coro_ = {};
1402 __coro.destroy();
1403 #else
1404 __coro_.destroy();
1405 #endif
1406 }
1407 }
1408
tag_invoke(start_t,__operation_base & __self)1409 friend void tag_invoke(start_t, __operation_base& __self) noexcept
1410 {
1411 __self.__coro_.resume();
1412 }
1413 };
1414
1415 template <class _ReceiverId>
1416 struct __promise;
1417
1418 template <class _ReceiverId>
1419 struct __operation
1420 {
1421 struct __t : __operation_base
1422 {
1423 using promise_type = stdexec::__t<__promise<_ReceiverId>>;
1424 using __operation_base::__operation_base;
1425 };
1426 };
1427
1428 template <class _ReceiverId>
1429 struct __promise
1430 {
1431 using _Receiver = stdexec::__t<_ReceiverId>;
1432
1433 struct __t : __promise_base
1434 {
1435 using __id = __promise;
1436
__tstdexec::__connect_awaitable_::__promise::__t1437 explicit __t(auto&, _Receiver& __rcvr) noexcept : __rcvr_(__rcvr) {}
1438
unhandled_stoppedstdexec::__connect_awaitable_::__promise::__t1439 auto unhandled_stopped() noexcept -> __coro::coroutine_handle<>
1440 {
1441 set_stopped(static_cast<_Receiver&&>(__rcvr_));
1442 // Returning noop_coroutine here causes the __connect_awaitable
1443 // coroutine to never resume past the point where it co_await's
1444 // the awaitable.
1445 return __coro::noop_coroutine();
1446 }
1447
get_return_objectstdexec::__connect_awaitable_::__promise::__t1448 auto get_return_object() noexcept
1449 -> stdexec::__t<__operation<_ReceiverId>>
1450 {
1451 return stdexec::__t<__operation<_ReceiverId>>{
1452 __coro::coroutine_handle<__t>::from_promise(*this)};
1453 }
1454
1455 template <class _Awaitable>
await_transformstdexec::__connect_awaitable_::__promise::__t1456 auto await_transform(_Awaitable&& __awaitable) noexcept -> _Awaitable&&
1457 {
1458 return static_cast<_Awaitable&&>(__awaitable);
1459 }
1460
1461 template <class _Awaitable>
1462 requires tag_invocable<as_awaitable_t, _Awaitable, __t&>
await_transformstdexec::__connect_awaitable_::__promise::__t1463 auto await_transform(_Awaitable&& __awaitable) //
1464 noexcept(nothrow_tag_invocable<as_awaitable_t, _Awaitable, __t&>)
1465 -> tag_invoke_result_t<as_awaitable_t, _Awaitable, __t&>
1466 {
1467 return tag_invoke(as_awaitable,
1468 static_cast<_Awaitable&&>(__awaitable), *this);
1469 }
1470
1471 // Pass through the get_env receiver query
tag_invokestdexec::__connect_awaitable_::__promise1472 friend auto tag_invoke(get_env_t, const __t& __self) noexcept
1473 -> env_of_t<_Receiver>
1474 {
1475 return get_env(__self.__rcvr_);
1476 }
1477
1478 _Receiver& __rcvr_;
1479 };
1480 };
1481
1482 template <receiver _Receiver>
1483 using __promise_t = __t<__promise<__id<_Receiver>>>;
1484
1485 template <receiver _Receiver>
1486 using __operation_t = __t<__operation<__id<_Receiver>>>;
1487
1488 struct __connect_awaitable_t
1489 {
1490 private:
1491 template <class _Fun, class... _Ts>
__co_callstdexec::__connect_awaitable_::__connect_awaitable_t1492 static auto __co_call(_Fun __fun, _Ts&&... __as) noexcept
1493 {
1494 auto __fn = [&, __fun]() noexcept {
1495 __fun(static_cast<_Ts&&>(__as)...);
1496 };
1497
1498 struct __awaiter
1499 {
1500 decltype(__fn) __fn_;
1501
1502 static constexpr auto await_ready() noexcept -> bool
1503 {
1504 return false;
1505 }
1506
1507 void await_suspend(__coro::coroutine_handle<>) noexcept
1508 {
1509 __fn_();
1510 }
1511
1512 [[noreturn]] void await_resume() noexcept
1513 {
1514 std::terminate();
1515 }
1516 };
1517
1518 return __awaiter{__fn};
1519 }
1520
1521 template <class _Awaitable, class _Receiver>
1522 #if STDEXEC_GCC() && (__GNUC__ > 11)
1523 __attribute__((__used__))
1524 #endif
1525 static auto
__co_implstdexec::__connect_awaitable_::__connect_awaitable_t1526 __co_impl(_Awaitable __awaitable, _Receiver __rcvr)
1527 -> __operation_t<_Receiver>
1528 {
1529 using __result_t = __await_result_t<_Awaitable, __promise_t<_Receiver>>;
1530 std::exception_ptr __eptr;
1531 try
1532 {
1533 if constexpr (same_as<__result_t, void>)
1534 co_await (
1535 co_await static_cast<_Awaitable&&>(__awaitable),
1536 __co_call(set_value, static_cast<_Receiver&&>(__rcvr)));
1537 else
1538 co_await __co_call(
1539 set_value, static_cast<_Receiver&&>(__rcvr),
1540 co_await static_cast<_Awaitable&&>(__awaitable));
1541 }
1542 catch (...)
1543 {
1544 __eptr = std::current_exception();
1545 }
1546 co_await __co_call(set_error, static_cast<_Receiver&&>(__rcvr),
1547 static_cast<std::exception_ptr&&>(__eptr));
1548 }
1549
1550 template <receiver _Receiver, class _Awaitable>
1551 using __completions_t = //
1552 completion_signatures<
1553 __minvoke< // set_value_t() or set_value_t(T)
1554 __remove<void, __qf<set_value_t>>,
1555 __await_result_t<_Awaitable, __promise_t<_Receiver>>>,
1556 set_error_t(std::exception_ptr), set_stopped_t()>;
1557
1558 public:
1559 template <class _Receiver, __awaitable<__promise_t<_Receiver>> _Awaitable>
1560 requires receiver_of<_Receiver, __completions_t<_Receiver, _Awaitable>>
operator ()stdexec::__connect_awaitable_::__connect_awaitable_t1561 auto operator()(_Awaitable&& __awaitable, _Receiver __rcvr) const
1562 -> __operation_t<_Receiver>
1563 {
1564 return __co_impl(static_cast<_Awaitable&&>(__awaitable),
1565 static_cast<_Receiver&&>(__rcvr));
1566 }
1567 };
1568 } // namespace __connect_awaitable_
1569
1570 using __connect_awaitable_::__connect_awaitable_t;
1571 #else
1572 struct __connect_awaitable_t
1573 {};
1574 #endif
1575 inline constexpr __connect_awaitable_t __connect_awaitable{};
1576
1577 /////////////////////////////////////////////////////////////////////////////
1578 // [execution.senders.connect]
1579 namespace __connect
1580 {
1581 struct connect_t;
1582
1583 template <class _Sender, class _Receiver>
1584 using __tfx_sender = //
1585 transform_sender_result_t<__late_domain_of_t<_Sender, env_of_t<_Receiver&>>,
1586 _Sender, env_of_t<_Receiver&>>;
1587
1588 template <class _Sender, class _Receiver>
1589 concept __connectable_with_tag_invoke_ = //
1590 receiver<_Receiver> && //
1591 sender_in<_Sender, env_of_t<_Receiver>> && //
1592 __receiver_from<_Receiver, _Sender> && //
1593 tag_invocable<connect_t, _Sender, _Receiver>;
1594
1595 template <class _Sender, class _Receiver>
1596 concept __connectable_with_tag_invoke = //
1597 __connectable_with_tag_invoke_<__tfx_sender<_Sender, _Receiver>, _Receiver>;
1598
1599 template <class _Sender, class _Receiver>
1600 concept __connectable_with_co_await = //
1601 __callable<__connect_awaitable_t, __tfx_sender<_Sender, _Receiver>,
1602 _Receiver>;
1603
1604 struct connect_t
1605 {
1606 template <class _Sender, class _Env>
__check_signaturesstdexec::__connect::connect_t1607 static constexpr auto __check_signatures() -> bool
1608 {
1609 if constexpr (sender_in<_Sender, _Env>)
1610 {
1611 // Instantiate __debug_sender via completion_signatures_of_t
1612 // to check that the actual completions match the expected
1613 // completions.
1614 //
1615 // Instantiate completion_signatures_of_t only if sender_in
1616 // is true to workaround Clang not implementing CWG#2369 yet
1617 // (connect() does have a constraint for _Sender satisfying
1618 // sender_in).
1619 using __checked_signatures
1620 [[maybe_unused]] = completion_signatures_of_t<_Sender, _Env>;
1621 }
1622 return true;
1623 }
1624
1625 template <class _Sender, class _Receiver>
__select_implstdexec::__connect::connect_t1626 static constexpr auto __select_impl() noexcept
1627 {
1628 using _Domain = __late_domain_of_t<_Sender, env_of_t<_Receiver&>>;
1629 constexpr bool _NothrowTfxSender =
1630 __nothrow_callable<get_env_t, _Receiver&> &&
1631 __nothrow_callable<transform_sender_t, _Domain, _Sender,
1632 env_of_t<_Receiver&>>;
1633 using _TfxSender = __tfx_sender<_Sender, _Receiver&>;
1634
1635 #if STDEXEC_ENABLE_EXTRA_TYPE_CHECKING()
1636 static_assert(__check_signatures<_TfxSender, env_of_t<_Receiver>>());
1637 #endif
1638
1639 if constexpr (__connectable_with_tag_invoke<_Sender, _Receiver>)
1640 {
1641 using _Result =
1642 tag_invoke_result_t<connect_t, _TfxSender, _Receiver>;
1643 constexpr bool _Nothrow = //
1644 _NothrowTfxSender &&
1645 nothrow_tag_invocable<connect_t, _TfxSender, _Receiver>;
1646 return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
1647 }
1648 else if constexpr (__connectable_with_co_await<_Sender, _Receiver>)
1649 {
1650 using _Result =
1651 __call_result_t<__connect_awaitable_t, _TfxSender, _Receiver>;
1652 return static_cast<_Result (*)()>(nullptr);
1653 }
1654 else
1655 {
1656 using _Result = __debug::__debug_operation;
1657 return static_cast<_Result (*)() noexcept(_NothrowTfxSender)>(
1658 nullptr);
1659 }
1660 }
1661
1662 template <class _Sender, class _Receiver>
1663 using __select_impl_t = decltype(__select_impl<_Sender, _Receiver>());
1664
1665 template <sender _Sender, receiver _Receiver>
1666 requires __connectable_with_tag_invoke<_Sender, _Receiver> ||
1667 __connectable_with_co_await<_Sender, _Receiver> ||
1668 __is_debug_env<env_of_t<_Receiver>>
1669 auto operator()(_Sender&& __sndr, _Receiver&& __rcvr) const
1670 noexcept(__nothrow_callable<__select_impl_t<_Sender, _Receiver>>)
1671 -> __call_result_t<__select_impl_t<_Sender, _Receiver>>
1672 {
1673 using _TfxSender = __tfx_sender<_Sender, _Receiver&>;
1674 auto&& __env = get_env(__rcvr);
1675 auto __domain = __get_late_domain(__sndr, __env);
1676
1677 if constexpr (__connectable_with_tag_invoke<_Sender, _Receiver>)
1678 {
1679 static_assert(
1680 operation_state<
1681 tag_invoke_result_t<connect_t, _TfxSender, _Receiver>>,
1682 "stdexec::connect(sender, receiver) must return a type that "
1683 "satisfies the operation_state concept");
1684 return tag_invoke(connect_t{},
1685 transform_sender(__domain,
1686 static_cast<_Sender&&>(__sndr),
1687 __env),
1688 static_cast<_Receiver&&>(__rcvr));
1689 }
1690 else if constexpr (__connectable_with_co_await<_Sender, _Receiver>)
1691 {
1692 return __connect_awaitable( //
1693 transform_sender(__domain, static_cast<_Sender&&>(__sndr),
1694 __env),
1695 static_cast<_Receiver&&>(__rcvr));
1696 }
1697 else
1698 {
1699 // This should generate an instantiation backtrace that contains
1700 // useful debugging information.
1701 using __tag_invoke::tag_invoke;
1702 tag_invoke(*this,
1703 transform_sender(__domain,
1704 static_cast<_Sender&&>(__sndr), __env),
1705 static_cast<_Receiver&&>(__rcvr));
1706 }
1707 }
1708
tag_invoke(forwarding_query_t,connect_t)1709 friend constexpr auto tag_invoke(forwarding_query_t, connect_t) noexcept
1710 -> bool
1711 {
1712 return false;
1713 }
1714 };
1715 } // namespace __connect
1716
1717 using __connect::connect_t;
1718 inline constexpr __connect::connect_t connect{};
1719
1720 /////////////////////////////////////////////////////////////////////////////
1721 // [exec.snd]
1722 template <class _Sender, class _Receiver>
1723 concept sender_to = receiver<_Receiver> && //
1724 sender_in<_Sender, env_of_t<_Receiver>> && //
1725 __receiver_from<_Receiver, _Sender> && //
1726 requires(_Sender&& __sndr, _Receiver&& __rcvr) {
1727 connect(static_cast<_Sender&&>(__sndr),
1728 static_cast<_Receiver&&>(__rcvr));
1729 };
1730
1731 template <class _Tag, class... _Args>
1732 auto __tag_of_sig_(_Tag (*)(_Args...)) -> _Tag;
1733 template <class _Sig>
1734 using __tag_of_sig_t =
1735 decltype(stdexec::__tag_of_sig_(static_cast<_Sig*>(nullptr)));
1736
1737 template <class _Sender, class _SetSig, class _Env = empty_env>
1738 concept sender_of =
1739 sender_in<_Sender, _Env> &&
1740 same_as<__types<_SetSig>, __gather_completions_for<
1741 __tag_of_sig_t<_SetSig>, _Sender, _Env,
1742 __qf<__tag_of_sig_t<_SetSig>>, __q<__types>>>;
1743
1744 #if !STDEXEC_STD_NO_COROUTINES_
1745 /////////////////////////////////////////////////////////////////////////////
1746 // stdexec::as_awaitable [execution.coro_utils.as_awaitable]
1747 namespace __as_awaitable
1748 {
1749 struct __void
1750 {};
1751 template <class _Value>
1752 using __value_or_void_t = __if<std::is_same<_Value, void>, __void, _Value>;
1753 template <class _Value>
1754 using __expected_t =
1755 std::variant<std::monostate, __value_or_void_t<_Value>, std::exception_ptr>;
1756
1757 template <class _Value>
1758 struct __receiver_base
1759 {
1760 using receiver_concept = receiver_t;
1761
1762 template <same_as<set_value_t> _Tag, class... _Us>
1763 requires constructible_from<__value_or_void_t<_Value>, _Us...>
tag_invoke(_Tag,__receiver_base && __self,_Us &&...__us)1764 friend void tag_invoke(_Tag, __receiver_base&& __self,
1765 _Us&&... __us) noexcept
1766 {
1767 try
1768 {
1769 __self.__result_->template emplace<1>(static_cast<_Us&&>(__us)...);
1770 __self.__continuation_.resume();
1771 }
1772 catch (...)
1773 {
1774 set_error(static_cast<__receiver_base&&>(__self),
1775 std::current_exception());
1776 }
1777 }
1778
1779 template <same_as<set_error_t> _Tag, class _Error>
tag_invoke(_Tag,__receiver_base && __self,_Error && __err)1780 friend void tag_invoke(_Tag, __receiver_base&& __self,
1781 _Error&& __err) noexcept
1782 {
1783 if constexpr (__decays_to<_Error, std::exception_ptr>)
1784 __self.__result_->template emplace<2>(static_cast<_Error&&>(__err));
1785 else if constexpr (__decays_to<_Error, std::error_code>)
1786 __self.__result_->template emplace<2>(
1787 std::make_exception_ptr(std::system_error(__err)));
1788 else
1789 __self.__result_->template emplace<2>(
1790 std::make_exception_ptr(static_cast<_Error&&>(__err)));
1791 __self.__continuation_.resume();
1792 }
1793
1794 __expected_t<_Value>* __result_;
1795 __coro::coroutine_handle<> __continuation_;
1796 };
1797
1798 template <class _PromiseId, class _Value>
1799 struct __receiver
1800 {
1801 using _Promise = stdexec::__t<_PromiseId>;
1802
1803 struct __t : __receiver_base<_Value>
1804 {
1805 using __id = __receiver;
1806
1807 template <same_as<set_stopped_t> _Tag>
tag_invokestdexec::__as_awaitable::__receiver1808 friend void tag_invoke(_Tag, __t&& __self) noexcept
1809 {
1810 auto __continuation =
1811 __coro::coroutine_handle<_Promise>::from_address(
1812 __self.__continuation_.address());
1813 __coro::coroutine_handle<> __stopped_continuation =
1814 __continuation.promise().unhandled_stopped();
1815 __stopped_continuation.resume();
1816 }
1817
1818 // Forward get_env query to the coroutine promise
tag_invokestdexec::__as_awaitable::__receiver1819 friend auto tag_invoke(get_env_t, const __t& __self) noexcept
1820 -> env_of_t<_Promise&>
1821 {
1822 auto __continuation =
1823 __coro::coroutine_handle<_Promise>::from_address(
1824 __self.__continuation_.address());
1825 return get_env(__continuation.promise());
1826 }
1827 };
1828 };
1829
1830 // BUGBUG NOT TO SPEC: make senders of more-than-one-value awaitable
1831 // by packaging the values into a tuple.
1832 // See: https://github.com/cplusplus/sender-receiver/issues/182
1833 template <std::size_t _Count>
1834 extern const __q<__decayed_tuple> __as_single;
1835
1836 template <>
1837 inline const __q<__midentity> __as_single<1>;
1838
1839 template <>
1840 inline const __mconst<void> __as_single<0>;
1841
1842 template <class... _Values>
1843 using __single_value =
1844 __minvoke<decltype(__as_single<sizeof...(_Values)>), _Values...>;
1845
1846 template <class _Sender, class _Promise>
1847 using __value_t =
1848 __decay_t<__value_types_of_t<_Sender, env_of_t<_Promise&>,
1849 __q<__single_value>, __msingle_or<void>>>;
1850
1851 template <class _Sender, class _Promise>
1852 using __receiver_t =
1853 __t<__receiver<__id<_Promise>, __value_t<_Sender, _Promise>>>;
1854
1855 template <class _Value>
1856 struct __sender_awaitable_base
1857 {
await_readystdexec::__as_awaitable::__sender_awaitable_base1858 [[nodiscard]] auto await_ready() const noexcept -> bool
1859 {
1860 return false;
1861 }
1862
await_resumestdexec::__as_awaitable::__sender_awaitable_base1863 auto await_resume() -> _Value
1864 {
1865 switch (__result_.index())
1866 {
1867 case 0: // receiver contract not satisfied
1868 STDEXEC_ASSERT(!"_Should never get here");
1869 break;
1870 case 1: // set_value
1871 if constexpr (!std::is_void_v<_Value>)
1872 return static_cast<_Value&&>(std::get<1>(__result_));
1873 else
1874 return;
1875 case 2: // set_error
1876 std::rethrow_exception(std::get<2>(__result_));
1877 }
1878 std::terminate();
1879 }
1880
1881 protected:
1882 __expected_t<_Value> __result_;
1883 };
1884
1885 template <class _PromiseId, class _SenderId>
1886 struct __sender_awaitable
1887 {
1888 using _Promise = stdexec::__t<_PromiseId>;
1889 using _Sender = stdexec::__t<_SenderId>;
1890 using __value = __value_t<_Sender, _Promise>;
1891
1892 struct __t : __sender_awaitable_base<__value>
1893 {
__tstdexec::__as_awaitable::__sender_awaitable::__t1894 __t(_Sender&& sndr, __coro::coroutine_handle<_Promise> __hcoro) //
1895 noexcept(__nothrow_connectable<_Sender, __receiver>) :
1896 __op_state_(connect(static_cast<_Sender&&>(sndr),
1897 __receiver{{&this->__result_, __hcoro}}))
1898 {}
1899
await_suspendstdexec::__as_awaitable::__sender_awaitable::__t1900 void await_suspend(__coro::coroutine_handle<_Promise>) noexcept
1901 {
1902 start(__op_state_);
1903 }
1904
1905 private:
1906 using __receiver = __receiver_t<_Sender, _Promise>;
1907 connect_result_t<_Sender, __receiver> __op_state_;
1908 };
1909 };
1910
1911 template <class _Promise, class _Sender>
1912 using __sender_awaitable_t =
1913 __t<__sender_awaitable<__id<_Promise>, __id<_Sender>>>;
1914
1915 template <class _Sender, class _Promise>
1916 concept __awaitable_sender =
1917 sender_in<_Sender, env_of_t<_Promise&>> && //
1918 __mvalid<__value_t, _Sender, _Promise> && //
1919 sender_to<_Sender, __receiver_t<_Sender, _Promise>> && //
1920 requires(_Promise& __promise) {
1921 {
1922 __promise.unhandled_stopped()
1923 } -> convertible_to<__coro::coroutine_handle<>>;
1924 };
1925
1926 struct __unspecified
1927 {
1928 auto get_return_object() noexcept -> __unspecified;
1929 auto initial_suspend() noexcept -> __unspecified;
1930 auto final_suspend() noexcept -> __unspecified;
1931 void unhandled_exception() noexcept;
1932 void return_void() noexcept;
1933 auto unhandled_stopped() noexcept -> __coro::coroutine_handle<>;
1934 };
1935
1936 struct as_awaitable_t
1937 {
1938 template <class _Tp, class _Promise>
__select_impl_stdexec::__as_awaitable::as_awaitable_t1939 static constexpr auto __select_impl_() noexcept
1940 {
1941 if constexpr (tag_invocable<as_awaitable_t, _Tp, _Promise&>)
1942 {
1943 using _Result = tag_invoke_result_t<as_awaitable_t, _Tp, _Promise&>;
1944 constexpr bool _Nothrow =
1945 nothrow_tag_invocable<as_awaitable_t, _Tp, _Promise&>;
1946 return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
1947 // NOLINTNEXTLINE(bugprone-branch-clone)
1948 }
1949 else if constexpr (__awaitable<_Tp, __unspecified>)
1950 { // NOT __awaitable<_Tp, _Promise> !!
1951 using _Result = _Tp&&;
1952 return static_cast<_Result (*)() noexcept>(nullptr);
1953 }
1954 else if constexpr (__awaitable_sender<_Tp, _Promise>)
1955 {
1956 using _Result = __sender_awaitable_t<_Promise, _Tp>;
1957 constexpr bool _Nothrow = __nothrow_constructible_from<
1958 _Result, _Tp, __coro::coroutine_handle<_Promise>>;
1959 return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
1960 }
1961 else
1962 {
1963 using _Result = _Tp&&;
1964 return static_cast<_Result (*)() noexcept>(nullptr);
1965 }
1966 }
1967 template <class _Tp, class _Promise>
1968 using __select_impl_t = decltype(__select_impl_<_Tp, _Promise>());
1969
1970 template <class _Tp, class _Promise>
operator ()stdexec::__as_awaitable::as_awaitable_t1971 auto operator()(_Tp&& __t, _Promise& __promise) const
1972 noexcept(__nothrow_callable<__select_impl_t<_Tp, _Promise>>)
1973 -> __call_result_t<__select_impl_t<_Tp, _Promise>>
1974 {
1975 if constexpr (tag_invocable<as_awaitable_t, _Tp, _Promise&>)
1976 {
1977 using _Result = tag_invoke_result_t<as_awaitable_t, _Tp, _Promise&>;
1978 static_assert(__awaitable<_Result, _Promise>);
1979 return tag_invoke(*this, static_cast<_Tp&&>(__t), __promise);
1980 // NOLINTNEXTLINE(bugprone-branch-clone)
1981 }
1982 else if constexpr (__awaitable<_Tp, __unspecified>)
1983 { // NOT __awaitable<_Tp, _Promise> !!
1984 return static_cast<_Tp&&>(__t);
1985 }
1986 else if constexpr (__awaitable_sender<_Tp, _Promise>)
1987 {
1988 auto __hcoro =
1989 __coro::coroutine_handle<_Promise>::from_promise(__promise);
1990 return __sender_awaitable_t<_Promise, _Tp>{static_cast<_Tp&&>(__t),
1991 __hcoro};
1992 }
1993 else
1994 {
1995 return static_cast<_Tp&&>(__t);
1996 }
1997 }
1998 };
1999 } // namespace __as_awaitable
2000
2001 using __as_awaitable::as_awaitable_t;
2002 inline constexpr as_awaitable_t as_awaitable{};
2003
2004 namespace __with_awaitable_senders
2005 {
2006
2007 template <class _Promise = void>
2008 class __continuation_handle;
2009
2010 template <>
2011 class __continuation_handle<void>
2012 {
2013 public:
2014 __continuation_handle() = default;
2015
2016 template <class _Promise>
__continuation_handle(__coro::coroutine_handle<_Promise> __coro)2017 __continuation_handle(__coro::coroutine_handle<_Promise> __coro) noexcept :
2018 __coro_(__coro)
2019 {
2020 if constexpr (requires(_Promise& __promise) {
2021 __promise.unhandled_stopped();
2022 })
2023 {
2024 __stopped_callback_ =
2025 [](void* __address) noexcept -> __coro::coroutine_handle<> {
2026 // This causes the rest of the coroutine (the part after the
2027 // co_await of the sender) to be skipped and invokes the calling
2028 // coroutine's stopped handler.
2029 return __coro::coroutine_handle<_Promise>::from_address(
2030 __address)
2031 .promise()
2032 .unhandled_stopped();
2033 };
2034 }
2035 // If _Promise doesn't implement unhandled_stopped(), then if a
2036 // "stopped" unwind reaches this point, it's considered an unhandled
2037 // exception and terminate() is called.
2038 }
2039
handle() const2040 [[nodiscard]] auto handle() const noexcept -> __coro::coroutine_handle<>
2041 {
2042 return __coro_;
2043 }
2044
unhandled_stopped() const2045 [[nodiscard]] auto unhandled_stopped() const noexcept
2046 -> __coro::coroutine_handle<>
2047 {
2048 return __stopped_callback_(__coro_.address());
2049 }
2050
2051 private:
2052 __coro::coroutine_handle<> __coro_{};
2053 using __stopped_callback_t = __coro::coroutine_handle<> (*)(void*) noexcept;
2054 __stopped_callback_t __stopped_callback_ =
__anon996f5b230302(void*) 2055 [](void*) noexcept -> __coro::coroutine_handle<> { std::terminate(); };
2056 };
2057
2058 template <class _Promise>
2059 class __continuation_handle
2060 {
2061 public:
2062 __continuation_handle() = default;
2063
__continuation_handle(__coro::coroutine_handle<_Promise> __coro)2064 __continuation_handle(__coro::coroutine_handle<_Promise> __coro) noexcept :
2065 __continuation_{__coro}
2066 {}
2067
handle() const2068 auto handle() const noexcept -> __coro::coroutine_handle<_Promise>
2069 {
2070 return __coro::coroutine_handle<_Promise>::from_address(
2071 __continuation_.handle().address());
2072 }
2073
unhandled_stopped() const2074 [[nodiscard]] auto unhandled_stopped() const noexcept
2075 -> __coro::coroutine_handle<>
2076 {
2077 return __continuation_.unhandled_stopped();
2078 }
2079
2080 private:
2081 __continuation_handle<> __continuation_{};
2082 };
2083
2084 struct __with_awaitable_senders_base
2085 {
2086 template <class _OtherPromise>
set_continuationstdexec::__with_awaitable_senders::__with_awaitable_senders_base2087 void set_continuation(
2088 __coro::coroutine_handle<_OtherPromise> __hcoro) noexcept
2089 {
2090 static_assert(!std::is_void_v<_OtherPromise>);
2091 __continuation_ = __hcoro;
2092 }
2093
set_continuationstdexec::__with_awaitable_senders::__with_awaitable_senders_base2094 void set_continuation(__continuation_handle<> __continuation) noexcept
2095 {
2096 __continuation_ = __continuation;
2097 }
2098
continuationstdexec::__with_awaitable_senders::__with_awaitable_senders_base2099 [[nodiscard]] auto continuation() const noexcept -> __continuation_handle<>
2100 {
2101 return __continuation_;
2102 }
2103
unhandled_stoppedstdexec::__with_awaitable_senders::__with_awaitable_senders_base2104 auto unhandled_stopped() noexcept -> __coro::coroutine_handle<>
2105 {
2106 return __continuation_.unhandled_stopped();
2107 }
2108
2109 private:
2110 __continuation_handle<> __continuation_{};
2111 };
2112
2113 template <class _Promise>
2114 struct with_awaitable_senders : __with_awaitable_senders_base
2115 {
2116 template <class _Value>
await_transformstdexec::__with_awaitable_senders::with_awaitable_senders2117 auto await_transform(_Value&& __val)
2118 -> __call_result_t<as_awaitable_t, _Value, _Promise&>
2119 {
2120 static_assert(derived_from<_Promise, with_awaitable_senders>);
2121 return as_awaitable(static_cast<_Value&&>(__val),
2122 static_cast<_Promise&>(*this));
2123 }
2124 };
2125 } // namespace __with_awaitable_senders
2126
2127 using __with_awaitable_senders::__continuation_handle;
2128 using __with_awaitable_senders::with_awaitable_senders;
2129 #endif
2130
2131 namespace
2132 {
2133 inline constexpr auto __ref = []<class _Ty>(_Ty& __ty) noexcept {
2134 return [__ty = &__ty]() noexcept -> decltype(auto) { return (*__ty); };
2135 };
2136 } // namespace
2137
2138 template <class _Ty>
2139 using __ref_t = decltype(__ref(__declval<_Ty&>()));
2140
2141 /////////////////////////////////////////////////////////////////////////////
2142 // NOT TO SPEC: __submit
2143 namespace __submit_
2144 {
2145 template <class _OpRef>
2146 struct __receiver
2147 {
2148 using receiver_concept = receiver_t;
2149 using __t = __receiver;
2150 using __id = __receiver;
2151
2152 using _Operation = __decay_t<__call_result_t<_OpRef>>;
2153 using _Receiver = stdexec::__t<__mapply<__q<__msecond>, _Operation>>;
2154
2155 _OpRef __opref_;
2156
2157 // Forward all the receiver ops, and delete the operation state.
2158 template <__completion_tag _Tag, class... _As>
2159 requires __callable<_Tag, _Receiver, _As...>
tag_invoke(_Tag __tag,__receiver && __self,_As &&...__as)2160 friend void tag_invoke(_Tag __tag, __receiver&& __self,
2161 _As&&... __as) noexcept
2162 {
2163 __tag(static_cast<_Receiver&&>(__self.__opref_().__rcvr_),
2164 static_cast<_As&&>(__as)...);
2165 __self.__delete_op();
2166 }
2167
__delete_opstdexec::__submit_::__receiver2168 void __delete_op() noexcept
2169 {
2170 _Operation* __op = &__opref_();
2171 if constexpr (__callable<get_allocator_t, env_of_t<_Receiver>>)
2172 {
2173 auto&& __env = get_env(__op->__rcvr_);
2174 auto __alloc = get_allocator(__env);
2175 using _Alloc = decltype(__alloc);
2176 using _OpAlloc = typename std::allocator_traits<
2177 _Alloc>::template rebind_alloc<_Operation>;
2178 _OpAlloc __op_alloc{__alloc};
2179 std::allocator_traits<_OpAlloc>::destroy(__op_alloc, __op);
2180 std::allocator_traits<_OpAlloc>::deallocate(__op_alloc, __op, 1);
2181 }
2182 else
2183 {
2184 delete __op;
2185 }
2186 }
2187
2188 // Forward all receiver queries.
tag_invoke(get_env_t,const __receiver & __self)2189 friend auto tag_invoke(get_env_t, const __receiver& __self) noexcept
2190 -> env_of_t<_Receiver&>
2191 {
2192 return get_env(__self.__opref_().__rcvr_);
2193 }
2194 };
2195
2196 template <class _SenderId, class _ReceiverId>
2197 struct __operation
2198 {
2199 using _Sender = stdexec::__t<_SenderId>;
2200 using _Receiver = stdexec::__t<_ReceiverId>;
2201 using __receiver_t = __receiver<__ref_t<__operation>>;
2202
2203 STDEXEC_ATTRIBUTE((no_unique_address))
2204 _Receiver __rcvr_;
2205 connect_result_t<_Sender, __receiver_t> __op_state_;
2206
__operationstdexec::__submit_::__operation2207 __operation(_Sender&& __sndr, _Receiver __rcvr) :
2208 __rcvr_(static_cast<_Receiver&&>(__rcvr)),
2209 __op_state_(
2210 connect(static_cast<_Sender&&>(__sndr), __receiver_t{__ref(*this)}))
2211 {}
2212 };
2213
2214 struct __submit_t
2215 {
2216 template <receiver _Receiver, sender_to<_Receiver> _Sender>
operator ()stdexec::__submit_::__submit_t2217 void operator()(_Sender&& __sndr, _Receiver __rcvr) const noexcept(false)
2218 {
2219 if constexpr (__callable<get_allocator_t, env_of_t<_Receiver>>)
2220 {
2221 auto&& __env = get_env(__rcvr);
2222 auto __alloc = get_allocator(__env);
2223 using _Alloc = decltype(__alloc);
2224 using _Op = __operation<__id<_Sender>, __id<_Receiver>>;
2225 using _OpAlloc = typename std::allocator_traits<
2226 _Alloc>::template rebind_alloc<_Op>;
2227 _OpAlloc __op_alloc{__alloc};
2228 auto __op = std::allocator_traits<_OpAlloc>::allocate(__op_alloc,
2229 1);
2230 try
2231 {
2232 std::allocator_traits<_OpAlloc>::construct(
2233 __op_alloc, __op, static_cast<_Sender&&>(__sndr),
2234 static_cast<_Receiver&&>(__rcvr));
2235 start(__op->__op_state_);
2236 }
2237 catch (...)
2238 {
2239 std::allocator_traits<_OpAlloc>::deallocate(__op_alloc, __op,
2240 1);
2241 throw;
2242 }
2243 }
2244 else
2245 {
2246 start((new __operation<__id<_Sender>, __id<_Receiver>>{
2247 static_cast<_Sender&&>(__sndr),
2248 static_cast<_Receiver&&>(__rcvr)})
2249 ->__op_state_);
2250 }
2251 }
2252 };
2253 } // namespace __submit_
2254
2255 using __submit_::__submit_t;
2256 inline constexpr __submit_t __submit{};
2257
2258 namespace __inln
2259 {
2260 struct __schedule_t
2261 {};
2262
2263 struct __scheduler
2264 {
2265 using __t = __scheduler;
2266 using __id = __scheduler;
2267
2268 template <class _Tag = __schedule_t>
2269 STDEXEC_ATTRIBUTE((host, device))
tag_invoke(schedule_t,__scheduler)2270 friend auto tag_invoke(schedule_t, __scheduler)
2271 {
2272 return __make_sexpr<_Tag>();
2273 }
2274
tag_invoke(get_forward_progress_guarantee_t,__scheduler)2275 friend auto tag_invoke(get_forward_progress_guarantee_t,
2276 __scheduler) noexcept -> forward_progress_guarantee
2277 {
2278 return forward_progress_guarantee::weakly_parallel;
2279 }
2280
2281 auto operator==(const __scheduler&) const noexcept -> bool = default;
2282 };
2283 } // namespace __inln
2284
2285 template <>
2286 struct __sexpr_impl<__inln::__schedule_t> : __sexpr_defaults
2287 {
2288 static constexpr auto get_attrs = //
2289 [](__ignore) noexcept
2290 -> __env::__with<__inln::__scheduler,
__anon996f5b230502stdexec::__sexpr_impl2291 get_completion_scheduler_t<set_value_t>> {
2292 return __env::__with(__inln::__scheduler{},
2293 get_completion_scheduler<set_value_t>);
2294 };
2295
2296 static constexpr auto get_completion_signatures = //
2297 [](__ignore,
__anon996f5b230602stdexec::__sexpr_impl2298 __ignore) noexcept -> completion_signatures<set_value_t()> {
2299 return {};
2300 };
2301
2302 static constexpr auto start = //
2303 []<class _Receiver>(__ignore, _Receiver& __rcvr) noexcept -> void {
2304 set_value(static_cast<_Receiver&&>(__rcvr));
2305 };
2306 };
2307
2308 /////////////////////////////////////////////////////////////////////////////
2309 // [execution.senders.consumer.start_detached]
2310 namespace __start_detached
2311 {
2312 template <class _EnvId>
2313 struct __detached_receiver
2314 {
2315 using _Env = stdexec::__t<_EnvId>;
2316
2317 struct __t
2318 {
2319 using receiver_concept = receiver_t;
2320 using __id = __detached_receiver;
2321 STDEXEC_ATTRIBUTE((no_unique_address))
2322 _Env __env_;
2323
2324 template <same_as<set_value_t> _Tag, class... _As>
tag_invokestdexec::__start_detached::__detached_receiver2325 friend void tag_invoke(_Tag, __t&&, _As&&...) noexcept
2326 {}
2327
2328 template <same_as<set_error_t> _Tag, class _Error>
tag_invokestdexec::__start_detached::__detached_receiver2329 [[noreturn]] friend void tag_invoke(_Tag, __t&&, _Error&&) noexcept
2330 {
2331 std::terminate();
2332 }
2333
2334 template <same_as<set_stopped_t> _Tag>
tag_invokestdexec::__start_detached::__detached_receiver2335 friend void tag_invoke(_Tag, __t&&) noexcept
2336 {}
2337
tag_invokestdexec::__start_detached::__detached_receiver2338 friend auto tag_invoke(get_env_t, const __t& __self) noexcept
2339 -> const _Env&
2340 {
2341 // BUGBUG NOT TO SPEC
2342 return __self.__env_;
2343 }
2344 };
2345 };
2346 template <class _Env = empty_env>
2347 using __detached_receiver_t = __t<__detached_receiver<__id<__decay_t<_Env>>>>;
2348
2349 struct start_detached_t
2350 {
2351 template <sender_in<empty_env> _Sender>
2352 requires __callable<apply_sender_t, __early_domain_of_t<_Sender>,
2353 start_detached_t, _Sender>
operator ()stdexec::__start_detached::start_detached_t2354 void operator()(_Sender&& __sndr) const
2355 {
2356 auto __domain = __get_early_domain(__sndr);
2357 stdexec::apply_sender(__domain, *this, static_cast<_Sender&&>(__sndr));
2358 }
2359
2360 template <class _Env, sender_in<_Env> _Sender>
2361 requires __callable<apply_sender_t, __late_domain_of_t<_Sender, _Env>,
2362 start_detached_t, _Sender, _Env>
operator ()stdexec::__start_detached::start_detached_t2363 void operator()(_Sender&& __sndr, _Env&& __env) const
2364 {
2365 auto __domain = __get_late_domain(__sndr, __env);
2366 stdexec::apply_sender(__domain, *this, static_cast<_Sender&&>(__sndr),
2367 static_cast<_Env&&>(__env));
2368 }
2369
2370 using _Sender = __0;
2371 using __legacy_customizations_t =
2372 __types<tag_invoke_t(start_detached_t,
2373 get_completion_scheduler_t<set_value_t>(
2374 get_env_t(const _Sender&)),
2375 _Sender),
2376 tag_invoke_t(start_detached_t, _Sender)>;
2377
2378 template <class _Sender, class _Env = empty_env>
2379 requires sender_to<_Sender, __detached_receiver_t<_Env>>
apply_senderstdexec::__start_detached::start_detached_t2380 void apply_sender(_Sender&& __sndr, _Env&& __env = {}) const
2381 {
2382 __submit(static_cast<_Sender&&>(__sndr),
2383 __detached_receiver_t<_Env>{static_cast<_Env&&>(__env)});
2384 }
2385 };
2386 } // namespace __start_detached
2387
2388 using __start_detached::start_detached_t;
2389 inline constexpr start_detached_t start_detached{};
2390
2391 /////////////////////////////////////////////////////////////////////////////
2392 // [execution.senders.factories]
2393 namespace __just
2394 {
2395 template <class _JustTag>
2396 struct __impl : __sexpr_defaults
2397 {
2398 using __tag_t = typename _JustTag::__tag_t;
2399
2400 static constexpr auto get_completion_signatures =
2401 []<class _Sender>(_Sender&&, __ignore) noexcept {
2402 static_assert(sender_expr_for<_Sender, _JustTag>);
2403 return completion_signatures<
2404 __mapply<__qf<__tag_t>, __decay_t<__data_of<_Sender>>>>{};
2405 };
2406
2407 static constexpr auto start =
2408 []<class _State, class _Receiver>(_State& __state,
2409 _Receiver& __rcvr) noexcept -> void {
2410 __tup::__apply(
2411 [&]<class... _Ts>(_Ts&... __ts) noexcept {
2412 __tag_t()(std::move(__rcvr), std::move(__ts)...);
2413 },
2414 __state);
2415 };
2416 };
2417
2418 struct just_t
2419 {
2420 using __tag_t = set_value_t;
2421
2422 template <__movable_value... _Ts>
2423 STDEXEC_ATTRIBUTE((host, device))
operator ()stdexec::__just::just_t2424 auto operator()(_Ts&&... __ts) const
2425 noexcept((__nothrow_decay_copyable<_Ts> && ...))
2426 {
2427 return __make_sexpr<just_t>(__tuple{static_cast<_Ts&&>(__ts)...});
2428 }
2429 };
2430
2431 struct just_error_t
2432 {
2433 using __tag_t = set_error_t;
2434
2435 template <__movable_value _Error>
2436 STDEXEC_ATTRIBUTE((host, device))
operator ()stdexec::__just::just_error_t2437 auto operator()(_Error&& __err) const
2438 noexcept(__nothrow_decay_copyable<_Error>)
2439 {
2440 return __make_sexpr<just_error_t>(
2441 __tuple{static_cast<_Error&&>(__err)});
2442 }
2443 };
2444
2445 struct just_stopped_t
2446 {
2447 using __tag_t = set_stopped_t;
2448
2449 template <class _Tag = just_stopped_t>
2450 STDEXEC_ATTRIBUTE((host, device))
operator ()stdexec::__just::just_stopped_t2451 auto operator()() const noexcept
2452 {
2453 return __make_sexpr<_Tag>(__tuple{});
2454 }
2455 };
2456 } // namespace __just
2457
2458 using __just::just_error_t;
2459 using __just::just_stopped_t;
2460 using __just::just_t;
2461
2462 template <>
2463 struct __sexpr_impl<just_t> : __just::__impl<just_t>
2464 {};
2465
2466 template <>
2467 struct __sexpr_impl<just_error_t> : __just::__impl<just_error_t>
2468 {};
2469
2470 template <>
2471 struct __sexpr_impl<just_stopped_t> : __just::__impl<just_stopped_t>
2472 {};
2473
2474 inline constexpr just_t just{};
2475 inline constexpr just_error_t just_error{};
2476 inline constexpr just_stopped_t just_stopped{};
2477
2478 /////////////////////////////////////////////////////////////////////////////
2479 // [execution.execute]
2480 namespace __execute_
2481 {
2482 template <class _Fun>
2483 struct __as_receiver
2484 {
2485 using receiver_concept = receiver_t;
2486 _Fun __fun_;
2487
2488 template <same_as<set_value_t> _Tag>
tag_invoke(_Tag,__as_receiver && __rcvr)2489 friend void tag_invoke(_Tag, __as_receiver&& __rcvr) noexcept
2490 {
2491 try
2492 {
2493 __rcvr.__fun_();
2494 }
2495 catch (...)
2496 {
2497 set_error(static_cast<__as_receiver&&>(__rcvr),
2498 std::exception_ptr());
2499 }
2500 }
2501
2502 template <same_as<set_error_t> _Tag>
tag_invoke(_Tag,__as_receiver &&,std::exception_ptr)2503 [[noreturn]] friend void tag_invoke(_Tag, __as_receiver&&,
2504 std::exception_ptr) noexcept
2505 {
2506 std::terminate();
2507 }
2508
2509 template <same_as<set_stopped_t> _Tag>
tag_invoke(_Tag,__as_receiver &&)2510 friend void tag_invoke(_Tag, __as_receiver&&) noexcept
2511 {}
2512
tag_invoke(get_env_t,const __as_receiver &)2513 friend auto tag_invoke(get_env_t, const __as_receiver&) noexcept
2514 -> empty_env
2515 {
2516 return {};
2517 }
2518 };
2519
2520 struct execute_t
2521 {
2522 template <scheduler _Scheduler, class _Fun>
2523 requires __callable<_Fun&> && move_constructible<_Fun>
operator ()stdexec::__execute_::execute_t2524 void operator()(_Scheduler&& __sched, _Fun __fun) const noexcept(false)
2525 {
2526 // Look for a legacy customization
2527 if constexpr (tag_invocable<execute_t, _Scheduler, _Fun>)
2528 {
2529 tag_invoke(execute_t{}, static_cast<_Scheduler&&>(__sched),
2530 static_cast<_Fun&&>(__fun));
2531 }
2532 else
2533 {
2534 auto __domain = query_or(get_domain, __sched, default_domain());
2535 stdexec::apply_sender(__domain, *this,
2536 schedule(static_cast<_Scheduler&&>(__sched)),
2537 static_cast<_Fun&&>(__fun));
2538 }
2539 }
2540
2541 template <sender_of<set_value_t()> _Sender, class _Fun>
2542 requires __callable<_Fun&> && move_constructible<_Fun>
apply_senderstdexec::__execute_::execute_t2543 void apply_sender(_Sender&& __sndr, _Fun __fun) const noexcept(false)
2544 {
2545 __submit(static_cast<_Sender&&>(__sndr),
2546 __as_receiver<_Fun>{static_cast<_Fun&&>(__fun)});
2547 }
2548 };
2549 } // namespace __execute_
2550
2551 using __execute_::execute_t;
2552 inline constexpr execute_t execute{};
2553
2554 // NOT TO SPEC:
2555 namespace __closure
2556 {
2557 template <__class _Dp>
2558 struct sender_adaptor_closure;
2559 } // namespace __closure
2560
2561 using __closure::sender_adaptor_closure;
2562
2563 template <class _Tp>
2564 concept __sender_adaptor_closure =
2565 derived_from<__decay_t<_Tp>, sender_adaptor_closure<__decay_t<_Tp>>> &&
2566 move_constructible<__decay_t<_Tp>> &&
2567 constructible_from<__decay_t<_Tp>, _Tp>;
2568
2569 template <class _Tp, class _Sender>
2570 concept __sender_adaptor_closure_for =
2571 __sender_adaptor_closure<_Tp> && sender<__decay_t<_Sender>> &&
2572 __callable<_Tp, __decay_t<_Sender>> &&
2573 sender<__call_result_t<_Tp, __decay_t<_Sender>>>;
2574
2575 namespace __closure
2576 {
2577 template <class _T0, class _T1>
2578 struct __compose : sender_adaptor_closure<__compose<_T0, _T1>>
2579 {
2580 STDEXEC_ATTRIBUTE((no_unique_address))
2581 _T0 __t0_;
2582 STDEXEC_ATTRIBUTE((no_unique_address))
2583 _T1 __t1_;
2584
2585 template <sender _Sender>
2586 requires __callable<_T0, _Sender> &&
2587 __callable<_T1, __call_result_t<_T0, _Sender>>
2588 STDEXEC_ATTRIBUTE((always_inline))
2589 __call_result_t<_T1, __call_result_t<_T0, _Sender>>
operator ()stdexec::__closure::__compose2590 operator()(_Sender&& __sndr) &&
2591 {
2592 return static_cast<_T1&&>(__t1_)(
2593 static_cast<_T0&&>(__t0_)(static_cast<_Sender&&>(__sndr)));
2594 }
2595
2596 template <sender _Sender>
2597 requires __callable<const _T0&, _Sender> &&
2598 __callable<const _T1&, __call_result_t<const _T0&, _Sender>>
2599 STDEXEC_ATTRIBUTE((always_inline))
2600 __call_result_t<_T1, __call_result_t<_T0, _Sender>>
operator ()stdexec::__closure::__compose2601 operator()(_Sender&& __sndr) const&
2602 {
2603 return __t1_(__t0_(static_cast<_Sender&&>(__sndr)));
2604 }
2605 };
2606
2607 template <__class _Dp>
2608 struct sender_adaptor_closure
2609 {};
2610
2611 template <sender _Sender, __sender_adaptor_closure_for<_Sender> _Closure>
2612 STDEXEC_ATTRIBUTE((always_inline))
operator |(_Sender && __sndr,_Closure && __clsur)2613 __call_result_t<_Closure, _Sender> operator|(_Sender&& __sndr,
2614 _Closure&& __clsur)
2615 {
2616 return static_cast<_Closure&&>(__clsur)(static_cast<_Sender&&>(__sndr));
2617 }
2618
2619 template <__sender_adaptor_closure _T0, __sender_adaptor_closure _T1>
2620 STDEXEC_ATTRIBUTE((always_inline))
operator |(_T0 && __t0,_T1 && __t1)2621 __compose<__decay_t<_T0>, __decay_t<_T1>> operator|(_T0&& __t0, _T1&& __t1)
2622 {
2623 return {{}, static_cast<_T0&&>(__t0), static_cast<_T1&&>(__t1)};
2624 }
2625
2626 template <class _Fun, class... _As>
2627 struct __binder_back : sender_adaptor_closure<__binder_back<_Fun, _As...>>
2628 {
2629 STDEXEC_ATTRIBUTE((no_unique_address))
2630 _Fun __fun_;
2631 std::tuple<_As...> __as_;
2632
2633 template <sender _Sender>
2634 requires __callable<_Fun, _Sender, _As...>
2635 STDEXEC_ATTRIBUTE((host, device,
2636 always_inline)) __call_result_t<_Fun, _Sender, _As...>
operator ()stdexec::__closure::__binder_back2637 operator()(_Sender&& __sndr) && noexcept(
2638 __nothrow_callable<_Fun, _Sender, _As...>)
2639 {
2640 return __apply(
2641 [&__sndr,
2642 this](_As&... __as) -> __call_result_t<_Fun, _Sender, _As...> {
2643 return static_cast<_Fun&&>(__fun_)(static_cast<_Sender&&>(__sndr),
2644 static_cast<_As&&>(__as)...);
2645 },
2646 __as_);
2647 }
2648
2649 template <sender _Sender>
2650 requires __callable<const _Fun&, _Sender, const _As&...>
2651 STDEXEC_ATTRIBUTE((host, device)) auto
operator ()stdexec::__closure::__binder_back2652 operator()(_Sender&& __sndr) const& //
2653 noexcept(__nothrow_callable<const _Fun&, _Sender, const _As&...>)
2654 -> __call_result_t<const _Fun&, _Sender, const _As&...>
2655 {
2656 return __apply(
2657 [&__sndr, this](const _As&... __as)
2658 -> __call_result_t<const _Fun&, _Sender, const _As&...> {
2659 return __fun_(static_cast<_Sender&&>(__sndr), __as...);
2660 },
2661 __as_);
2662 }
2663 };
2664 } // namespace __closure
2665
2666 using __closure::__binder_back;
2667
2668 namespace __adaptors
2669 {
2670 STDEXEC_PRAGMA_PUSH()
2671 STDEXEC_PRAGMA_IGNORE_GNU("-Wold-style-cast")
2672
2673 // A derived-to-base cast that works even when the base is not
2674 // accessible from derived.
2675 template <class _Tp, class _Up>
2676 STDEXEC_ATTRIBUTE((host, device))
__c_cast(_Up && u)2677 auto __c_cast(_Up&& u) noexcept -> __copy_cvref_t<_Up&&, _Tp>
2678 requires __decays_to<_Tp, _Tp>
2679 {
2680 static_assert(std::is_reference_v<__copy_cvref_t<_Up&&, _Tp>>);
2681 static_assert(STDEXEC_IS_BASE_OF(_Tp, __decay_t<_Up>));
2682 return (__copy_cvref_t<_Up&&, _Tp>)static_cast<_Up&&>(u);
2683 }
2684 STDEXEC_PRAGMA_POP()
2685
2686 namespace __no
2687 {
2688 struct __nope
2689 {};
2690
2691 struct __receiver : __nope
2692 {
2693 using receiver_concept = receiver_t;
2694 };
2695
2696 template <same_as<set_error_t> _Tag>
2697 void tag_invoke(_Tag, __receiver, std::exception_ptr) noexcept;
2698 template <same_as<set_stopped_t> _Tag>
2699 void tag_invoke(_Tag, __receiver) noexcept;
2700 auto tag_invoke(get_env_t, __receiver) noexcept -> empty_env;
2701 } // namespace __no
2702
2703 using __not_a_receiver = __no::__receiver;
2704
2705 template <class _Base>
2706 struct __adaptor_base
2707 {
2708 template <class _T1>
2709 requires constructible_from<_Base, _T1>
__adaptor_basestdexec::__adaptors::__adaptor_base2710 explicit __adaptor_base(_T1&& __base) : __base_(static_cast<_T1&&>(__base))
2711 {}
2712
2713 private:
2714 STDEXEC_ATTRIBUTE((no_unique_address))
2715 _Base __base_;
2716
2717 protected:
2718 STDEXEC_ATTRIBUTE((host, device, always_inline))
2719
basestdexec::__adaptors::__adaptor_base2720 _Base& base() & noexcept
2721 {
2722 return __base_;
2723 }
2724
2725 STDEXEC_ATTRIBUTE((host, device, always_inline))
2726
basestdexec::__adaptors::__adaptor_base2727 const _Base& base() const& noexcept
2728 {
2729 return __base_;
2730 }
2731
2732 STDEXEC_ATTRIBUTE((host, device, always_inline))
2733
basestdexec::__adaptors::__adaptor_base2734 _Base&& base() && noexcept
2735 {
2736 return static_cast<_Base&&>(__base_);
2737 }
2738 };
2739
2740 template <derived_from<__no::__nope> _Base>
2741 struct __adaptor_base<_Base>
2742 {};
2743
2744 // BUGBUG Not to spec: on gcc and nvc++, member functions in derived classes
2745 // don't shadow type aliases of the same name in base classes. :-O
2746 // On mingw gcc, 'bool(type::existing_member_function)' evaluates to true,
2747 // but 'int(type::existing_member_function)' is an error (as desired).
2748 #define STDEXEC_DISPATCH_MEMBER(_TAG) \
2749 template <class _Self, class... _Ts> \
2750 STDEXEC_ATTRIBUTE((host, device, always_inline)) \
2751 static auto __call_##_TAG(_Self&& __self, _Ts&&... __ts) noexcept \
2752 -> decltype((static_cast<_Self&&>(__self)) \
2753 ._TAG(static_cast<_Ts&&>(__ts)...)) \
2754 { \
2755 static_assert(noexcept((static_cast<_Self&&>(__self)) \
2756 ._TAG(static_cast<_Ts&&>(__ts)...))); \
2757 return static_cast<_Self&&>(__self)._TAG(static_cast<_Ts&&>(__ts)...); \
2758 } /**/
2759 #define STDEXEC_CALL_MEMBER(_TAG, ...) __call_##_TAG(__VA_ARGS__)
2760
2761 #if STDEXEC_CLANG()
2762 // Only clang gets this right.
2763 #define STDEXEC_MISSING_MEMBER(_Dp, _TAG) requires { typename _Dp::_TAG; }
2764 #define STDEXEC_DEFINE_MEMBER(_TAG) \
2765 STDEXEC_DISPATCH_MEMBER(_TAG) using _TAG = void
2766 #else
2767 #define STDEXEC_MISSING_MEMBER(_Dp, _TAG) (__missing_##_TAG<_Dp>())
2768 #define STDEXEC_DEFINE_MEMBER(_TAG) \
2769 template <class _Dp> \
2770 static constexpr bool __missing_##_TAG() noexcept \
2771 { \
2772 return requires { requires bool(int(_Dp::_TAG)); }; \
2773 } \
2774 STDEXEC_DISPATCH_MEMBER(_TAG) \
2775 static constexpr int _TAG = 1 /**/
2776 #endif
2777
2778 template <__class _Derived, class _Base = __not_a_receiver>
2779 struct receiver_adaptor : __adaptor_base<_Base>, receiver_t
2780 {
2781 friend _Derived;
2782 STDEXEC_DEFINE_MEMBER(set_value);
2783 STDEXEC_DEFINE_MEMBER(set_error);
2784 STDEXEC_DEFINE_MEMBER(set_stopped);
2785 STDEXEC_DEFINE_MEMBER(get_env);
2786
2787 static constexpr bool __has_base = !derived_from<_Base, __no::__nope>;
2788
2789 template <class _Dp>
2790 using __base_from_derived_t = decltype(__declval<_Dp>().base());
2791
2792 using __get_base_t =
2793 __if_c<__has_base, __mbind_back_q<__copy_cvref_t, _Base>,
2794 __q<__base_from_derived_t>>;
2795
2796 template <class _Dp>
2797 using __base_t = __minvoke<__get_base_t, _Dp&&>;
2798
2799 template <class _Dp>
2800 STDEXEC_ATTRIBUTE((host, device))
__get_basestdexec::__adaptors::receiver_adaptor2801 static auto __get_base(_Dp&& __self) noexcept -> __base_t<_Dp>
2802 {
2803 if constexpr (__has_base)
2804 {
2805 return __c_cast<receiver_adaptor>(static_cast<_Dp&&>(__self))
2806 .base();
2807 }
2808 else
2809 {
2810 return static_cast<_Dp&&>(__self).base();
2811 }
2812 }
2813
2814 template <same_as<set_value_t> _SetValue, class... _As>
2815 STDEXEC_ATTRIBUTE((host, device, always_inline))
tag_invoke(_SetValue,_Derived && __self,_As &&...__as)2816 friend auto tag_invoke(_SetValue, _Derived&& __self,
2817 _As&&... __as) noexcept //
2818 -> __msecond< //
2819 __if_c<same_as<set_value_t, _SetValue>>,
2820 decltype(STDEXEC_CALL_MEMBER(set_value,
2821 static_cast<_Derived&&>(__self),
2822 static_cast<_As&&>(__as)...))>
2823 {
2824 static_assert(noexcept(
2825 STDEXEC_CALL_MEMBER(set_value, static_cast<_Derived&&>(__self),
2826 static_cast<_As&&>(__as)...)));
2827 STDEXEC_CALL_MEMBER(set_value, static_cast<_Derived&&>(__self),
2828 static_cast<_As&&>(__as)...);
2829 }
2830
2831 template <same_as<set_value_t> _SetValue, class _Dp = _Derived,
2832 class... _As>
STDEXEC_MISSING_MEMBER(_Dp,set_value)2833 requires STDEXEC_MISSING_MEMBER
2834 (_Dp, set_value) &&
2835 tag_invocable<_SetValue, __base_t<_Dp>, _As...> STDEXEC_ATTRIBUTE((
2836 host, device,
2837 always_inline)) friend void tag_invoke(_SetValue, _Derived&& __self,
2838 _As&&... __as) noexcept
2839 {
2840 stdexec::set_value(__get_base(static_cast<_Dp&&>(__self)),
2841 static_cast<_As&&>(__as)...);
2842 }
2843
2844 template <same_as<set_error_t> _SetError, class _Error>
2845 STDEXEC_ATTRIBUTE((host, device, always_inline))
tag_invoke(_SetError,_Derived && __self,_Error && __err)2846 friend auto tag_invoke(_SetError, _Derived&& __self,
2847 _Error&& __err) noexcept //
2848 -> __msecond< //
2849 __if_c<same_as<set_error_t, _SetError>>,
2850 decltype(STDEXEC_CALL_MEMBER(set_error,
2851 static_cast<_Derived&&>(__self),
2852 static_cast<_Error&&>(__err)))>
2853 {
2854 static_assert(noexcept(
2855 STDEXEC_CALL_MEMBER(set_error, static_cast<_Derived&&>(__self),
2856 static_cast<_Error&&>(__err))));
2857 STDEXEC_CALL_MEMBER(set_error, static_cast<_Derived&&>(__self),
2858 static_cast<_Error&&>(__err));
2859 }
2860
2861 template <same_as<set_error_t> _SetError, class _Error,
2862 class _Dp = _Derived>
STDEXEC_MISSING_MEMBER(_Dp,set_error)2863 requires STDEXEC_MISSING_MEMBER
2864 (_Dp, set_error) &&
2865 tag_invocable<_SetError, __base_t<_Dp>, _Error> STDEXEC_ATTRIBUTE((
2866 host, device,
2867 always_inline)) friend void tag_invoke(_SetError, _Derived&& __self,
2868 _Error&& __err) noexcept
2869 {
2870 stdexec::set_error(__get_base(static_cast<_Derived&&>(__self)),
2871 static_cast<_Error&&>(__err));
2872 }
2873
2874 template <same_as<set_stopped_t> _SetStopped, class _Dp = _Derived>
2875 STDEXEC_ATTRIBUTE((host, device, always_inline))
tag_invoke(_SetStopped,_Derived && __self)2876 friend auto tag_invoke(_SetStopped, _Derived&& __self) noexcept //
2877 -> __msecond< //
2878 __if_c<same_as<set_stopped_t, _SetStopped>>,
2879 decltype(STDEXEC_CALL_MEMBER(set_stopped,
2880 static_cast<_Dp&&>(__self)))>
2881 {
2882 static_assert(noexcept(
2883 STDEXEC_CALL_MEMBER(set_stopped, static_cast<_Derived&&>(__self))));
2884 STDEXEC_CALL_MEMBER(set_stopped, static_cast<_Derived&&>(__self));
2885 }
2886
2887 template <same_as<set_stopped_t> _SetStopped, class _Dp = _Derived>
STDEXEC_MISSING_MEMBER(_Dp,set_stopped)2888 requires STDEXEC_MISSING_MEMBER
2889 (_Dp, set_stopped) &&
2890 tag_invocable<_SetStopped, __base_t<_Dp>> STDEXEC_ATTRIBUTE(
2891 (host, device,
2892 always_inline)) friend void tag_invoke(_SetStopped,
2893 _Derived&& __self) noexcept
2894 {
2895 stdexec::set_stopped(__get_base(static_cast<_Derived&&>(__self)));
2896 }
2897
2898 // Pass through the get_env receiver query
2899 template <same_as<get_env_t> _GetEnv, class _Dp = _Derived>
2900 STDEXEC_ATTRIBUTE((host, device, always_inline))
tag_invoke(_GetEnv,const _Derived & __self)2901 friend auto tag_invoke(_GetEnv, const _Derived& __self) noexcept
2902 -> decltype(STDEXEC_CALL_MEMBER(get_env,
2903 static_cast<const _Dp&>(__self)))
2904 {
2905 static_assert(noexcept(STDEXEC_CALL_MEMBER(get_env, __self)));
2906 return STDEXEC_CALL_MEMBER(get_env, __self);
2907 }
2908
2909 template <same_as<get_env_t> _GetEnv, class _Dp = _Derived>
STDEXEC_MISSING_MEMBER(_Dp,get_env)2910 requires STDEXEC_MISSING_MEMBER
2911 (_Dp, get_env)
2912 STDEXEC_ATTRIBUTE((host, device, always_inline)) friend auto tag_invoke(
2913 _GetEnv, const _Derived& __self) noexcept
2914 -> env_of_t<__base_t<const _Dp&>>
2915 {
2916 return stdexec::get_env(__get_base(__self));
2917 }
2918
2919 public:
2920 receiver_adaptor() = default;
2921 using __adaptor_base<_Base>::__adaptor_base;
2922
2923 using receiver_concept = receiver_t;
2924 };
2925 } // namespace __adaptors
2926
2927 template <__class _Derived, receiver _Base = __adaptors::__not_a_receiver>
2928 using receiver_adaptor = __adaptors::receiver_adaptor<_Derived, _Base>;
2929
2930 template <class _Receiver, class _Fun, class... _As>
2931 concept __receiver_of_invoke_result = //
2932 receiver_of<_Receiver, completion_signatures<
2933 __minvoke<__remove<void, __qf<set_value_t>>,
2934 __invoke_result_t<_Fun, _As...>>>>;
2935
2936 template <bool _CanThrow = false, class _Receiver, class _Fun, class... _As>
__set_value_invoke(_Receiver && __rcvr,_Fun && __fun,_As &&...__as)2937 void __set_value_invoke(_Receiver&& __rcvr, _Fun&& __fun,
2938 _As&&... __as) noexcept(!_CanThrow)
2939 {
2940 if constexpr (_CanThrow || __nothrow_invocable<_Fun, _As...>)
2941 {
2942 if constexpr (same_as<void, __invoke_result_t<_Fun, _As...>>)
2943 {
2944 __invoke(static_cast<_Fun&&>(__fun), static_cast<_As&&>(__as)...);
2945 set_value(static_cast<_Receiver&&>(__rcvr));
2946 }
2947 else
2948 {
2949 set_value(static_cast<_Receiver&&>(__rcvr),
2950 __invoke(static_cast<_Fun&&>(__fun),
2951 static_cast<_As&&>(__as)...));
2952 }
2953 }
2954 else
2955 {
2956 try
2957 {
2958 stdexec::__set_value_invoke<true>(static_cast<_Receiver&&>(__rcvr),
2959 static_cast<_Fun&&>(__fun),
2960 static_cast<_As&&>(__as)...);
2961 }
2962 catch (...)
2963 {
2964 set_error(static_cast<_Receiver&&>(__rcvr),
2965 std::current_exception());
2966 }
2967 }
2968 }
2969
2970 template <class _Fun>
2971 struct _WITH_FUNCTION_
2972 {};
2973
2974 template <class... _Args>
2975 struct _WITH_ARGUMENTS_
2976 {};
2977
2978 inline constexpr __mstring __not_callable_diag =
2979 "The specified function is not callable with the arguments provided."_mstr;
2980
2981 template <__mstring _Context, __mstring _Diagnostic = __not_callable_diag>
2982 struct _NOT_CALLABLE_
2983 {};
2984
2985 template <__mstring _Context>
2986 struct __callable_error
2987 {
2988 template <class _Fun, class... _Args>
2989 using __f = //
2990 __mexception< //
2991 _NOT_CALLABLE_<_Context>, _WITH_FUNCTION_<_Fun>,
2992 _WITH_ARGUMENTS_<_Args...>>;
2993 };
2994
2995 template <class _Fun, class... _Args>
2996 requires __invocable<_Fun, _Args...>
2997 using __non_throwing_ = __mbool<__nothrow_invocable<_Fun, _Args...>>;
2998
2999 template <class _Tag, class _Fun, class _Sender, class _Env, class _Catch>
3000 using __with_error_invoke_t = //
3001 __if<__gather_completions_for<
3002 _Tag, _Sender, _Env,
3003 __mbind_front<__mtry_catch_q<__non_throwing_, _Catch>, _Fun>,
3004 __q<__mand>>,
3005 completion_signatures<>, __with_exception_ptr>;
3006
3007 template <class _Fun, class... _Args>
3008 requires __invocable<_Fun, _Args...>
3009 using __set_value_invoke_t = //
3010 completion_signatures<__minvoke<__remove<void, __qf<set_value_t>>,
3011 __invoke_result_t<_Fun, _Args...>>>;
3012
3013 /////////////////////////////////////////////////////////////////////////////
3014 // [execution.senders.adaptors.then]
3015 namespace __then
3016 {
3017 inline constexpr __mstring __then_context =
3018 "In stdexec::then(Sender, Function)..."_mstr;
3019 using __on_not_callable = __callable_error<__then_context>;
3020
3021 template <class _Fun, class _CvrefSender, class _Env>
3022 using __completion_signatures_t = //
3023 __try_make_completion_signatures<
3024 _CvrefSender, _Env,
3025 __with_error_invoke_t<set_value_t, _Fun, _CvrefSender, _Env,
3026 __on_not_callable>,
3027 __mbind_front<__mtry_catch_q<__set_value_invoke_t, __on_not_callable>,
3028 _Fun>>;
3029
3030 ////////////////////////////////////////////////////////////////////////////////////////////////
3031 struct then_t
3032 {
3033 template <sender _Sender, __movable_value _Fun>
operator ()stdexec::__then::then_t3034 auto operator()(_Sender&& __sndr, _Fun __fun) const -> __well_formed_sender
3035 auto
3036 {
3037 auto __domain = __get_early_domain(__sndr);
3038 return stdexec::transform_sender(
3039 __domain, __make_sexpr<then_t>(static_cast<_Fun&&>(__fun),
3040 static_cast<_Sender&&>(__sndr)));
3041 }
3042
3043 template <__movable_value _Fun>
3044 STDEXEC_ATTRIBUTE((always_inline))
operator ()stdexec::__then::then_t3045 __binder_back<then_t, _Fun> operator()(_Fun __fun) const
3046 {
3047 return {{}, {}, {static_cast<_Fun&&>(__fun)}};
3048 }
3049
3050 using _Sender = __1;
3051 using _Fun = __0;
3052 using __legacy_customizations_t =
3053 __types<tag_invoke_t(then_t,
3054 get_completion_scheduler_t<set_value_t>(
3055 get_env_t(_Sender&)),
3056 _Sender, _Fun),
3057 tag_invoke_t(then_t, _Sender, _Fun)>;
3058 };
3059
3060 struct __then_impl : __sexpr_defaults
3061 {
3062 static constexpr auto get_completion_signatures = //
3063 []<class _Sender, class _Env>(_Sender&&, _Env&&) noexcept
3064 -> __completion_signatures_t<__decay_t<__data_of<_Sender>>,
3065 __child_of<_Sender>, _Env> {
3066 static_assert(sender_expr_for<_Sender, then_t>);
3067 return {};
3068 };
3069
3070 static constexpr auto complete = //
3071 []<class _Tag, class... _Args>(__ignore, auto& __state, auto& __rcvr,
3072 _Tag,
3073 _Args&&... __args) noexcept -> void {
3074 if constexpr (std::same_as<_Tag, set_value_t>)
3075 {
3076 stdexec::__set_value_invoke(std::move(__rcvr), std::move(__state),
3077 static_cast<_Args&&>(__args)...);
3078 }
3079 else
3080 {
3081 _Tag()(std::move(__rcvr), static_cast<_Args&&>(__args)...);
3082 }
3083 };
3084 };
3085 } // namespace __then
3086
3087 using __then::then_t;
3088
3089 /// @brief The then sender adaptor, which invokes a function with the result of
3090 /// a sender, making the result available to the next receiver.
3091 /// @hideinitializer
3092 inline constexpr then_t then{};
3093
3094 template <>
3095 struct __sexpr_impl<then_t> : __then::__then_impl
3096 {};
3097
3098 /////////////////////////////////////////////////////////////////////////////
3099 // [execution.senders.adaptors.upon_error]
3100 namespace __upon_error
3101 {
3102 inline constexpr __mstring __upon_error_context =
3103 "In stdexec::upon_error(Sender, Function)..."_mstr;
3104 using __on_not_callable = __callable_error<__upon_error_context>;
3105
3106 template <class _Fun, class _CvrefSender, class _Env>
3107 using __completion_signatures_t = //
3108 __try_make_completion_signatures<
3109 _CvrefSender, _Env,
3110 __with_error_invoke_t<set_error_t, _Fun, _CvrefSender, _Env,
3111 __on_not_callable>,
3112 __q<__compl_sigs::__default_set_value>,
3113 __mbind_front<__mtry_catch_q<__set_value_invoke_t, __on_not_callable>,
3114 _Fun>>;
3115
3116 ////////////////////////////////////////////////////////////////////////////////////////////////
3117 struct upon_error_t
3118 {
3119 template <sender _Sender, __movable_value _Fun>
operator ()stdexec::__upon_error::upon_error_t3120 auto operator()(_Sender&& __sndr, _Fun __fun) const -> __well_formed_sender
3121 auto
3122 {
3123 auto __domain = __get_early_domain(__sndr);
3124 return stdexec::transform_sender(
3125 __domain,
3126 __make_sexpr<upon_error_t>(static_cast<_Fun&&>(__fun),
3127 static_cast<_Sender&&>(__sndr)));
3128 }
3129
3130 template <__movable_value _Fun>
3131 STDEXEC_ATTRIBUTE((always_inline))
operator ()stdexec::__upon_error::upon_error_t3132 __binder_back<upon_error_t, _Fun> operator()(_Fun __fun) const
3133 {
3134 return {{}, {}, {static_cast<_Fun&&>(__fun)}};
3135 }
3136
3137 using _Sender = __1;
3138 using _Fun = __0;
3139 using __legacy_customizations_t =
3140 __types<tag_invoke_t(upon_error_t,
3141 get_completion_scheduler_t<set_value_t>(
3142 get_env_t(_Sender&)),
3143 _Sender, _Fun),
3144 tag_invoke_t(upon_error_t, _Sender, _Fun)>;
3145 };
3146
3147 struct __upon_error_impl : __sexpr_defaults
3148 {
3149 static constexpr auto get_completion_signatures = //
3150 []<class _Sender, class _Env>(_Sender&&, _Env&&) noexcept
3151 -> __completion_signatures_t<__decay_t<__data_of<_Sender>>,
3152 __child_of<_Sender>, _Env> {
3153 static_assert(sender_expr_for<_Sender, upon_error_t>);
3154 return {};
3155 };
3156
3157 static constexpr auto complete = //
3158 []<class _Tag, class... _Args>(__ignore, auto& __state, auto& __rcvr,
3159 _Tag,
3160 _Args&&... __args) noexcept -> void {
3161 if constexpr (std::same_as<_Tag, set_error_t>)
3162 {
3163 stdexec::__set_value_invoke(std::move(__rcvr), std::move(__state),
3164 static_cast<_Args&&>(__args)...);
3165 }
3166 else
3167 {
3168 _Tag()(std::move(__rcvr), static_cast<_Args&&>(__args)...);
3169 }
3170 };
3171 };
3172 } // namespace __upon_error
3173
3174 using __upon_error::upon_error_t;
3175 inline constexpr upon_error_t upon_error{};
3176
3177 template <>
3178 struct __sexpr_impl<upon_error_t> : __upon_error::__upon_error_impl
3179 {};
3180
3181 /////////////////////////////////////////////////////////////////////////////
3182 // [execution.senders.adaptors.upon_stopped]
3183 namespace __upon_stopped
3184 {
3185 inline constexpr __mstring __upon_stopped_context =
3186 "In stdexec::upon_stopped(Sender, Function)..."_mstr;
3187 using __on_not_callable = __callable_error<__upon_stopped_context>;
3188
3189 template <class _Fun, class _CvrefSender, class _Env>
3190 using __completion_signatures_t = //
3191 __try_make_completion_signatures<
3192 _CvrefSender, _Env,
3193 __with_error_invoke_t<set_stopped_t, _Fun, _CvrefSender, _Env,
3194 __on_not_callable>,
3195 __q<__compl_sigs::__default_set_value>,
3196 __q<__compl_sigs::__default_set_error>, __set_value_invoke_t<_Fun>>;
3197
3198 ////////////////////////////////////////////////////////////////////////////////////////////////
3199 struct upon_stopped_t
3200 {
3201 template <sender _Sender, __movable_value _Fun>
3202 requires __callable<_Fun>
operator ()stdexec::__upon_stopped::upon_stopped_t3203 auto operator()(_Sender&& __sndr, _Fun __fun) const -> __well_formed_sender
3204 auto
3205 {
3206 auto __domain = __get_early_domain(__sndr);
3207 return stdexec::transform_sender(
3208 __domain,
3209 __make_sexpr<upon_stopped_t>(static_cast<_Fun&&>(__fun),
3210 static_cast<_Sender&&>(__sndr)));
3211 }
3212
3213 template <__movable_value _Fun>
3214 requires __callable<_Fun>
3215 STDEXEC_ATTRIBUTE((always_inline)) __binder_back<upon_stopped_t, _Fun>
operator ()stdexec::__upon_stopped::upon_stopped_t3216 operator()(_Fun __fun) const
3217 {
3218 return {{}, {}, {static_cast<_Fun&&>(__fun)}};
3219 }
3220
3221 using _Sender = __1;
3222 using _Fun = __0;
3223 using __legacy_customizations_t =
3224 __types<tag_invoke_t(upon_stopped_t,
3225 get_completion_scheduler_t<set_value_t>(
3226 get_env_t(_Sender&)),
3227 _Sender, _Fun),
3228 tag_invoke_t(upon_stopped_t, _Sender, _Fun)>;
3229 };
3230
3231 struct __upon_stopped_impl : __sexpr_defaults
3232 {
3233 static constexpr auto get_completion_signatures = //
3234 []<class _Sender, class _Env>(_Sender&&, _Env&&) noexcept
3235 -> __completion_signatures_t<__decay_t<__data_of<_Sender>>,
3236 __child_of<_Sender>, _Env> {
3237 static_assert(sender_expr_for<_Sender, upon_stopped_t>);
3238 return {};
3239 };
3240
3241 static constexpr auto complete = //
3242 []<class _Tag, class... _Args>(__ignore, auto& __state, auto& __rcvr,
3243 _Tag,
3244 _Args&&... __args) noexcept -> void {
3245 if constexpr (std::same_as<_Tag, set_stopped_t>)
3246 {
3247 stdexec::__set_value_invoke(std::move(__rcvr), std::move(__state),
3248 static_cast<_Args&&>(__args)...);
3249 }
3250 else
3251 {
3252 _Tag()(std::move(__rcvr), static_cast<_Args&&>(__args)...);
3253 }
3254 };
3255 };
3256 } // namespace __upon_stopped
3257
3258 using __upon_stopped::upon_stopped_t;
3259 inline constexpr upon_stopped_t upon_stopped{};
3260
3261 template <>
3262 struct __sexpr_impl<upon_stopped_t> : __upon_stopped::__upon_stopped_impl
3263 {};
3264
3265 /////////////////////////////////////////////////////////////////////////////
3266 // [execution.senders.adaptors.bulk]
3267 namespace __bulk
3268 {
3269 inline constexpr __mstring __bulk_context =
3270 "In stdexec::bulk(Sender, Shape, Function)..."_mstr;
3271 using __on_not_callable = __callable_error<__bulk_context>;
3272
3273 template <class _Shape, class _Fun>
3274 struct __data
3275 {
3276 _Shape __shape_;
3277 STDEXEC_ATTRIBUTE((no_unique_address))
3278 _Fun __fun_;
3279 static constexpr auto __mbrs_ =
3280 __mliterals<&__data::__shape_, &__data::__fun_>();
3281 };
3282 template <class _Shape, class _Fun>
3283 __data(_Shape, _Fun) -> __data<_Shape, _Fun>;
3284
3285 template <class _Ty>
3286 using __decay_ref = __decay_t<_Ty>&;
3287
3288 template <class _CvrefSender, class _Env, class _Shape, class _Fun,
3289 class _Catch>
3290 using __with_error_invoke_t = //
3291 __if<__try_value_types_of_t<
3292 _CvrefSender, _Env,
3293 __transform<__q<__decay_ref>,
3294 __mbind_front<__mtry_catch_q<__non_throwing_, _Catch>,
3295 _Fun, _Shape>>,
3296 __q<__mand>>,
3297 completion_signatures<>, __with_exception_ptr>;
3298
3299 template <class _CvrefSender, class _Env, class _Shape, class _Fun>
3300 using __completion_signatures = //
3301 __try_make_completion_signatures<
3302 _CvrefSender, _Env,
3303 __with_error_invoke_t<_CvrefSender, _Env, _Shape, _Fun,
3304 __on_not_callable>>;
3305
3306 struct bulk_t
3307 {
3308 template <sender _Sender, integral _Shape, __movable_value _Fun>
3309 STDEXEC_ATTRIBUTE((host, device))
operator ()stdexec::__bulk::bulk_t3310 auto operator()(_Sender&& __sndr, _Shape __shape, _Fun __fun) const
3311 -> __well_formed_sender auto
3312 {
3313 auto __domain = __get_early_domain(__sndr);
3314 return stdexec::transform_sender(
3315 __domain,
3316 __make_sexpr<bulk_t>(__data{__shape, static_cast<_Fun&&>(__fun)},
3317 static_cast<_Sender&&>(__sndr)));
3318 }
3319
3320 template <integral _Shape, class _Fun>
3321 STDEXEC_ATTRIBUTE((always_inline))
operator ()stdexec::__bulk::bulk_t3322 __binder_back<bulk_t, _Shape, _Fun> operator()(_Shape __shape,
3323 _Fun __fun) const
3324 {
3325 return {{},
3326 {},
3327 {static_cast<_Shape&&>(__shape), static_cast<_Fun&&>(__fun)}};
3328 }
3329
3330 // This describes how to use the pieces of a bulk sender to find
3331 // legacy customizations of the bulk algorithm.
3332 using _Sender = __1;
3333 using _Shape = __nth_member<0>(__0);
3334 using _Fun = __nth_member<1>(__0);
3335 using __legacy_customizations_t =
3336 __types<tag_invoke_t(bulk_t,
3337 get_completion_scheduler_t<set_value_t>(
3338 get_env_t(_Sender&)),
3339 _Sender, _Shape, _Fun),
3340 tag_invoke_t(bulk_t, _Sender, _Shape, _Fun)>;
3341 };
3342
3343 struct __bulk_impl : __sexpr_defaults
3344 {
3345 template <class _Sender>
3346 using __fun_t = decltype(__decay_t<__data_of<_Sender>>::__fun_);
3347
3348 template <class _Sender>
3349 using __shape_t = decltype(__decay_t<__data_of<_Sender>>::__shape_);
3350
3351 static constexpr auto get_completion_signatures = //
3352 []<class _Sender, class _Env>(_Sender&&, _Env&&) noexcept
3353 -> __completion_signatures<__child_of<_Sender>, _Env,
3354 __shape_t<_Sender>, __fun_t<_Sender>> {
3355 static_assert(sender_expr_for<_Sender, bulk_t>);
3356 return {};
3357 };
3358
3359 static constexpr auto complete = //
3360 []<class _Tag, class... _Args>(__ignore, auto& __state, auto& __rcvr,
3361 _Tag,
3362 _Args&&... __args) noexcept -> void {
3363 if constexpr (std::same_as<_Tag, set_value_t>)
3364 {
3365 using __shape_t = decltype(__state.__shape_);
3366 if constexpr (noexcept(__state.__fun_(__shape_t{}, __args...)))
3367 {
3368 for (__shape_t __i{}; __i != __state.__shape_; ++__i)
3369 {
3370 __state.__fun_(__i, __args...);
3371 }
3372 _Tag()(std::move(__rcvr), static_cast<_Args&&>(__args)...);
3373 }
3374 else
3375 {
3376 try
3377 {
3378 for (__shape_t __i{}; __i != __state.__shape_; ++__i)
3379 {
3380 __state.__fun_(__i, __args...);
3381 }
3382 _Tag()(std::move(__rcvr), static_cast<_Args&&>(__args)...);
3383 }
3384 catch (...)
3385 {
3386 set_error(std::move(__rcvr), std::current_exception());
3387 }
3388 }
3389 }
3390 else
3391 {
3392 _Tag()(std::move(__rcvr), static_cast<_Args&&>(__args)...);
3393 }
3394 };
3395 };
3396 } // namespace __bulk
3397
3398 using __bulk::bulk_t;
3399 inline constexpr bulk_t bulk{};
3400
3401 template <>
3402 struct __sexpr_impl<bulk_t> : __bulk::__bulk_impl
3403 {};
3404
3405 ////////////////////////////////////////////////////////////////////////////
3406 // shared components of split and ensure_started
3407 //
3408 // The split and ensure_started algorithms are very similar in implementation.
3409 // The salient differences are:
3410 //
3411 // split: the input async operation is always connected. It is only
3412 // started when one of the split senders is connected and started.
3413 // split senders are copyable, so there are multiple operation states
3414 // to be notified on completion. These are stored in an instrusive
3415 // linked list.
3416 //
3417 // ensure_started: the input async operation is always started, so
3418 // the internal receiver will always be completed. The ensure_started
3419 // sender is move-only and single-shot, so there will only ever be one
3420 // operation state to be notified on completion.
3421 //
3422 // The shared state should add-ref itself when the input async
3423 // operation is started and release itself when its completion
3424 // is notified.
3425 namespace __shared
3426 {
3427 template <class _BaseEnv>
3428 using __env_t = //
3429 __env::__join_t<__env::__with<in_place_stop_token, get_stop_token_t>,
3430 _BaseEnv>; // BUGBUG NOT TO SPEC
3431
3432 struct __on_stop_request
3433 {
3434 in_place_stop_source& __stop_source_;
3435
operator ()stdexec::__shared::__on_stop_request3436 void operator()() noexcept
3437 {
3438 __stop_source_.request_stop();
3439 }
3440 };
3441
3442 template <class _Receiver>
__notify_visitor(_Receiver & __rcvr)3443 auto __notify_visitor(_Receiver& __rcvr) noexcept
3444 {
3445 return [&]<class _Tuple>(_Tuple&& __tupl) noexcept -> void {
3446 __apply(
3447 [&](auto __tag, auto&&... __args) noexcept -> void {
3448 __tag(static_cast<_Receiver&&>(__rcvr),
3449 __forward_like<_Tuple>(__args)...);
3450 },
3451 __tupl);
3452 };
3453 }
3454
3455 enum class __action_kind : bool
3456 {
3457 __notify,
3458 __detach
3459 };
3460
3461 struct __local_state_base : __immovable
3462 {
3463 using __action_fn = void(__local_state_base*, __action_kind) noexcept;
3464
3465 __action_fn* __action_{};
3466 __local_state_base* __next_{};
3467 };
3468
3469 template <class _CvrefSender, class _Env>
3470 struct __shared_state;
3471
3472 // Each operation state of a split sender has one of these,
3473 // created when a split sender is connected. There are 0 or
3474 // more of them per input async operation. It is what
3475 // the split sender's `get_state` fn returns. It holds a
3476 // reference to the shared state of the input async operation.
3477 template <class _CvrefSender, class _Receiver>
3478 struct __local_state :
3479 __local_state_base,
3480 __enable_receiver_from_this<_CvrefSender, _Receiver>
3481 {
3482 using __data_t = __decay_t<__data_of<_CvrefSender>>;
3483 using __shared_state_t = __mapply<__q<__mfront>, __data_t>;
3484 using __on_stop_cb_t = //
3485 typename stop_token_of_t<env_of_t<_Receiver>&>::template callback_type<
3486 __on_stop_request>;
3487 using __tag_t = tag_of_t<_CvrefSender>;
3488 static_assert(__one_of<__tag_t, __split::__split_t,
3489 __ensure_started::__ensure_started_t>);
3490
__local_statestdexec::__shared::__local_state3491 explicit __local_state(_CvrefSender&& __sndr) noexcept :
3492 __local_state::__local_state_base{{},
3493 &__action<tag_of_t<_CvrefSender>>},
3494 __shared_state_(STDEXEC_CALL_EXPLICIT_THIS_MEMFN(
3495 static_cast<_CvrefSender&&>(__sndr),
3496 apply)(__detail::__get_data())
3497 .__shared_state)
3498 {}
3499
~__local_statestdexec::__shared::__local_state3500 ~__local_state()
3501 {
3502 __action_(this, __action_kind::__detach);
3503 }
3504
3505 // This is called when the input async operation completes; or,
3506 // if it has already completed when start is called, it is called
3507 // from start:
3508 template <class _Tag>
__actionstdexec::__shared::__local_state3509 static void __action(__local_state_base* __self,
3510 __action_kind __kind) noexcept
3511 {
3512 auto* const __op = static_cast<__local_state*>(__self);
3513 if (__kind == __action_kind::__notify)
3514 {
3515 __op->__on_stop_.reset();
3516
3517 // The split algorithm sends by T const&. ensure_started sends by
3518 // T&&.
3519 if constexpr (same_as<__split::__split_t, _Tag>)
3520 {
3521 std::visit(__notify_visitor(__op->__receiver()),
3522 std::as_const(__op->__shared_state_->__data_));
3523 }
3524 else
3525 {
3526 std::visit(__notify_visitor(__op->__receiver()),
3527 std::move(__op->__shared_state_->__data_));
3528 }
3529 }
3530 else
3531 {
3532 // This is a detach operation
3533 if constexpr (same_as<__split::__split_t, _Tag>)
3534 {
3535 // no-op
3536 }
3537 else
3538 {
3539 __op->__shared_state_->__detach();
3540 }
3541 }
3542 }
3543
3544 std::optional<__on_stop_cb_t> __on_stop_{};
3545 __intrusive_ptr<__shared_state_t> __shared_state_;
3546 };
3547
3548 template <class _CvrefSenderId, class _EnvId>
3549 struct __receiver
3550 {
3551 using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>;
3552 using _Env = stdexec::__t<_EnvId>;
3553
3554 struct __t
3555 {
3556 using receiver_concept = receiver_t;
3557 using __id = __receiver;
3558
__tstdexec::__shared::__receiver::__t3559 explicit __t(
3560 __shared_state<_CvrefSender, _Env>* __shared_state) noexcept :
3561 __shared_state_(__shared_state)
3562 {}
3563
3564 template <__completion_tag _Tag, class... _As>
tag_invokestdexec::__shared::__receiver3565 friend void tag_invoke(_Tag __tag, __t&& __self, _As&&... __as) noexcept
3566 {
3567 __shared_state<_CvrefSender, _Env>& __state =
3568 *__self.__shared_state_;
3569
3570 try
3571 {
3572 using __tuple_t = __decayed_tuple<_Tag, _As...>;
3573 __state.__data_.template emplace<__tuple_t>(
3574 __tag, static_cast<_As&&>(__as)...);
3575 }
3576 catch (...)
3577 {
3578 using __tuple_t =
3579 __decayed_tuple<set_error_t, std::exception_ptr>;
3580 __state.__data_.template emplace<__tuple_t>(
3581 set_error, std::current_exception());
3582 }
3583
3584 __state.__notify();
3585 }
3586
tag_invokestdexec::__shared::__receiver3587 friend auto tag_invoke(get_env_t, const __t& __self) noexcept
3588 -> const __env_t<_Env>&
3589 {
3590 return __self.__shared_state_->__env_;
3591 }
3592
3593 __shared_state<_CvrefSender, _Env>* __shared_state_;
3594 };
3595 };
3596
3597 template <class _CvrefSender, class _Env>
3598 struct __shared_state :
3599 __enable_intrusive_from_this<__shared_state<_CvrefSender, _Env>>
3600 {
3601 using __variant_t = __compl_sigs::__for_all_sigs<
3602 __completion_signatures_of_t<_CvrefSender, _Env>, __q<__decayed_tuple>,
3603 __mbind_front_q<__variant,
3604 std::tuple<set_stopped_t>, // Initial state of the
3605 // variant is set_stopped
3606 std::tuple<set_error_t, std::exception_ptr>>>;
3607
3608 using __receiver_t = __t<__receiver<__cvref_id<_CvrefSender>, __id<_Env>>>;
3609
3610 in_place_stop_source __stop_source_{};
3611 __variant_t __data_;
3612 std::atomic<void*> __head_{nullptr};
3613 __env_t<_Env> __env_;
3614 connect_result_t<_CvrefSender, __receiver_t> __op_state2_;
3615
__shared_statestdexec::__shared::__shared_state3616 explicit __shared_state(_CvrefSender&& __sndr, _Env __env) :
3617 __env_(__env::__join(
3618 __env::__with(__stop_source_.get_token(), get_stop_token),
3619 static_cast<_Env&&>(__env))),
3620 __op_state2_(
3621 connect(static_cast<_CvrefSender&&>(__sndr), __receiver_t{this}))
3622 {}
3623
__start_opstdexec::__shared::__shared_state3624 void __start_op() noexcept
3625 {
3626 // the inner sender isn't running. if we reach here, then
3627 // one way or the other, __shared_state::__notify() will be
3628 // called, which decrements the ref count of *this.
3629 // So we need to increment it here:
3630 this->__inc_ref();
3631
3632 if (__stop_source_.stop_requested())
3633 {
3634 // 1. resets __head to completion state
3635 // 2. notifies waiting threads
3636 // 3. propagates "stopped" signal to `out_r'`
3637 __notify();
3638 }
3639 else
3640 {
3641 stdexec::start(__op_state2_);
3642 }
3643 }
3644
3645 // This is called when the shared async operation completes:
__notifystdexec::__shared::__shared_state3646 void __notify() noexcept
3647 {
3648 void* const __completion_state = static_cast<void*>(this);
3649 void* const __old = __head_.exchange(__completion_state,
3650 std::memory_order_acq_rel);
3651 auto* __state = static_cast<__local_state_base*>(__old);
3652
3653 while (__state != nullptr)
3654 {
3655 __local_state_base* __next = __state->__next_;
3656 __state->__action_(__state, __action_kind::__notify);
3657 __state = __next;
3658 }
3659
3660 // The async operation has completed, so we can release our
3661 // ref-count on it:
3662 this->__dec_ref();
3663 }
3664
__detachstdexec::__shared::__shared_state3665 void __detach() noexcept
3666 {
3667 // Check to see if this operation was ever started. If not,
3668 // detach the (potentially still running) operation:
3669 if (nullptr == __head_.load(std::memory_order_acquire))
3670 {
3671 __stop_source_.request_stop();
3672 }
3673 }
3674 };
3675
3676 template <class _Cvref, class _CvrefSenderId, class _EnvId>
3677 using __completions_t = //
3678 __try_make_completion_signatures<
3679 // NOT TO SPEC:
3680 // See https://github.com/cplusplus/sender-receiver/issues/23
3681 __cvref_t<_CvrefSenderId>, __env_t<__t<_EnvId>>,
3682 completion_signatures<set_error_t(
3683 __minvoke<_Cvref, std::exception_ptr>),
3684 set_stopped_t()>, // NOT TO SPEC
3685 __transform<_Cvref,
3686 __mcompose<__q<completion_signatures>, __qf<set_value_t>>>,
3687 __transform<_Cvref,
3688 __mcompose<__q<completion_signatures>, __qf<set_error_t>>>>;
3689
3690 template <class _Ty>
3691 using __clref_t = const __decay_t<_Ty>&;
3692
3693 template <class _Ty>
3694 using __rref_t = __decay_t<_Ty>&&;
3695
3696 template <class _Tag>
3697 using __cvref_results_t = //
3698 __if_c<same_as<_Tag, __split::__split_t>, __q<__clref_t>, __q<__rref_t>>;
3699
3700 template <class _Tag>
__get_completion_signatures_fn()3701 inline auto __get_completion_signatures_fn() noexcept
3702 { //
3703 return
3704 []<template <class> class _Data, class _ShState>(
3705 auto, const _Data<_ShState>&) //
3706 -> __mapply<__mbind_front_q<__completions_t, __cvref_results_t<_Tag>>,
3707 _ShState> { return {}; };
3708 }
3709
3710 template <class _Tag>
3711 struct __shared_impl : __sexpr_defaults
3712 {
3713 static constexpr auto get_state = //
3714 []<class _Sender, class _Receiver>(
3715 _Sender&& __sndr,
3716 _Receiver&) noexcept -> __local_state<_Sender, _Receiver> {
3717 static_assert(sender_expr_for<_Sender, _Tag>);
3718 return __local_state<_Sender, _Receiver>{
3719 static_cast<_Sender&&>(__sndr)};
3720 };
3721
3722 static constexpr auto get_completion_signatures = //
3723 []<class _Self, class _OtherEnv>(_Self&&, _OtherEnv&&) noexcept
3724 -> __call_result_t<__sexpr_apply_t, _Self,
3725 __result_of<__get_completion_signatures_fn<_Tag>>> {
3726 static_assert(sender_expr_for<_Self, _Tag>);
3727 return {};
3728 };
3729
3730 static constexpr auto start = //
3731 []<class _Sender, class _Receiver>(
3732 __local_state<_Sender, _Receiver>& __state,
3733 _Receiver& __rcvr) noexcept -> void {
3734 auto* __shared_state = __state.__shared_state_.get();
3735 std::atomic<void*>& __head = __shared_state->__head_;
3736 void* const __completion_state = static_cast<void*>(__shared_state);
3737 void* __old = __head.load(std::memory_order_acquire);
3738
3739 if (__old != __completion_state)
3740 {
3741 __state.__on_stop_.emplace(
3742 get_stop_token(stdexec::get_env(__rcvr)),
3743 __on_stop_request{__shared_state->__stop_source_});
3744
3745 if constexpr (same_as<_Tag, __ensure_started::__ensure_started_t>)
3746 {
3747 // Check if the stop_source has requested cancellation
3748 if (__shared_state->__stop_source_.stop_requested())
3749 {
3750 // Stop has already been requested. Don't bother starting
3751 // the child operations.
3752 stdexec::set_stopped(static_cast<_Receiver&&>(__rcvr));
3753 return;
3754 }
3755 }
3756 }
3757
3758 // With the split algorithm, multiple split senders can be started
3759 // simultaneously, but only one should start the async operation. The
3760 // following loop atomically (re)tries to set the pointer to the head of
3761 // the list to __state. When it finally succeeds, the prior value is
3762 // checked. If it is nullptr, then this split sender has won the race
3763 // and has the honor of starting the async operation.
3764 do
3765 {
3766 if (__old == __completion_state)
3767 {
3768 __state.template __action<_Tag>(&__state,
3769 __action_kind::__notify);
3770 return;
3771 }
3772 __state.__next_ = static_cast<__local_state_base*>(__old);
3773 } while (!__head.compare_exchange_weak(
3774 __old, static_cast<void*>(&__state), std::memory_order_release,
3775 std::memory_order_acquire));
3776
3777 if constexpr (same_as<_Tag, __split::__split_t>)
3778 {
3779 if (__old == nullptr)
3780 {
3781 __shared_state->__start_op();
3782 }
3783 }
3784 };
3785 };
3786 } // namespace __shared
3787
3788 ////////////////////////////////////////////////////////////////////////////
3789 // [execution.senders.adaptors.split]
3790 namespace __split
3791 {
3792 using namespace __shared;
3793
3794 template <class _ShState>
3795 struct __data
3796 {
__datastdexec::__split::__data3797 explicit __data(__intrusive_ptr<_ShState> __shared_state) noexcept :
3798 __shared_state(std::move(__shared_state))
3799 {}
3800
3801 __intrusive_ptr<_ShState> __shared_state;
3802 };
3803
3804 struct __split_t
3805 {};
3806
3807 struct split_t
3808 {
3809 template <sender _Sender, class _Env = empty_env>
3810 requires sender_in<_Sender, _Env> && __decay_copyable<env_of_t<_Sender>>
operator ()stdexec::__split::split_t3811 auto operator()(_Sender&& __sndr, _Env&& __env = {}) const
3812 -> __well_formed_sender auto
3813 {
3814 auto __domain = __get_late_domain(__sndr, __env);
3815 return stdexec::transform_sender(
3816 __domain, __make_sexpr<split_t>(static_cast<_Env&&>(__env),
3817 static_cast<_Sender&&>(__sndr)));
3818 }
3819
3820 STDEXEC_ATTRIBUTE((always_inline))
3821
operator ()stdexec::__split::split_t3822 __binder_back<split_t> operator()() const
3823 {
3824 return {{}, {}, {}};
3825 }
3826
3827 using _Sender = __1;
3828 using __legacy_customizations_t = //
3829 __types<tag_invoke_t(split_t,
3830 get_completion_scheduler_t<set_value_t>(
3831 get_env_t(const _Sender&)),
3832 _Sender),
3833 tag_invoke_t(split_t, _Sender)>;
3834
3835 template <class _CvrefSender, class _Env>
3836 using __receiver_t =
3837 __t<__meval<__receiver, __cvref_id<_CvrefSender>, __id<_Env>>>;
3838
3839 template <class _Sender>
transform_senderstdexec::__split::split_t3840 static auto transform_sender(_Sender&& __sndr)
3841 {
3842 using _Receiver =
3843 __receiver_t<__child_of<_Sender>, __decay_t<__data_of<_Sender>>>;
3844 static_assert(sender_to<__child_of<_Sender>, _Receiver>);
3845 return __sexpr_apply(static_cast<_Sender&&>(__sndr),
3846 [&]<class _Env, class _Child>(
3847 __ignore, _Env&& __env, _Child&& __child) {
3848 auto __state =
3849 __make_intrusive<__shared_state<_Child, __decay_t<_Env>>>(
3850 static_cast<_Child&&>(__child), static_cast<_Env&&>(__env));
3851 return __make_sexpr<__split_t>(__data{std::move(__state)});
3852 });
3853 }
3854 };
3855 } // namespace __split
3856
3857 using __split::split_t;
3858 inline constexpr split_t split{};
3859
3860 template <>
3861 struct __sexpr_impl<__split::__split_t> :
3862 __shared::__shared_impl<__split::__split_t>
3863 {};
3864
3865 /////////////////////////////////////////////////////////////////////////////
3866 // [execution.senders.adaptors.ensure_started]
3867 namespace __ensure_started
3868 {
3869 using namespace __shared;
3870
3871 // Each ensure_started sender has one of these, created when
3872 // ensure_started() is called.
3873 template <class _ShState>
3874 struct __data
3875 {
__datastdexec::__ensure_started::__data3876 explicit __data(__intrusive_ptr<_ShState> __ptr) noexcept :
3877 __shared_state(std::move(__ptr))
3878 {
3879 // Eagerly launch the async operation.
3880 __shared_state->__start_op();
3881 }
3882
3883 __data(__data&&) noexcept = default;
3884 auto operator=(__data&&) noexcept -> __data& = default;
3885
~__datastdexec::__ensure_started::__data3886 ~__data()
3887 {
3888 if (__shared_state != nullptr)
3889 {
3890 // detach from the still-running operation.
3891 // NOT TO SPEC: This also requests cancellation.
3892 __shared_state->__detach();
3893 }
3894 }
3895
3896 __intrusive_ptr<_ShState> __shared_state;
3897 };
3898
3899 struct __ensure_started_t
3900 {};
3901
3902 struct ensure_started_t
3903 {
3904 template <sender _Sender, class _Env = empty_env>
3905 requires sender_in<_Sender, _Env> && __decay_copyable<env_of_t<_Sender>>
operator ()stdexec::__ensure_started::ensure_started_t3906 [[nodiscard]] auto operator()(_Sender&& __sndr, _Env&& __env = {}) const
3907 -> __well_formed_sender auto
3908 {
3909 if constexpr (sender_expr_for<_Sender, __ensure_started_t>)
3910 {
3911 return static_cast<_Sender&&>(__sndr);
3912 }
3913 else
3914 {
3915 auto __domain = __get_late_domain(__sndr, __env);
3916 return stdexec::transform_sender(
3917 __domain,
3918 __make_sexpr<ensure_started_t>(static_cast<_Env&&>(__env),
3919 static_cast<_Sender&&>(__sndr)));
3920 }
3921 }
3922
3923 STDEXEC_ATTRIBUTE((always_inline))
3924
operator ()stdexec::__ensure_started::ensure_started_t3925 __binder_back<ensure_started_t> operator()() const
3926 {
3927 return {{}, {}, {}};
3928 }
3929
3930 using _Sender = __1;
3931 using __legacy_customizations_t = //
3932 __types<tag_invoke_t(ensure_started_t,
3933 get_completion_scheduler_t<set_value_t>(
3934 get_env_t(const _Sender&)),
3935 _Sender),
3936 tag_invoke_t(ensure_started_t, _Sender)>;
3937
3938 template <class _CvrefSender, class _Env>
3939 using __receiver_t =
3940 __t<__meval<__receiver, __cvref_id<_CvrefSender>, __id<_Env>>>;
3941
3942 template <class _Sender>
transform_senderstdexec::__ensure_started::ensure_started_t3943 static auto transform_sender(_Sender&& __sndr)
3944 {
3945 using _Receiver =
3946 __receiver_t<__child_of<_Sender>, __decay_t<__data_of<_Sender>>>;
3947 static_assert(sender_to<__child_of<_Sender>, _Receiver>);
3948 return __sexpr_apply(static_cast<_Sender&&>(__sndr),
3949 [&]<class _Env, class _Child>(
3950 __ignore, _Env&& __env, _Child&& __child) {
3951 auto __state =
3952 __make_intrusive<__shared_state<_Child, __decay_t<_Env>>>(
3953 static_cast<_Child&&>(__child), static_cast<_Env&&>(__env));
3954 return __make_sexpr<__ensure_started_t>(__data{std::move(__state)});
3955 });
3956 }
3957 };
3958 } // namespace __ensure_started
3959
3960 using __ensure_started::ensure_started_t;
3961 inline constexpr ensure_started_t ensure_started{};
3962
3963 template <>
3964 struct __sexpr_impl<__ensure_started::__ensure_started_t> :
3965 __shared::__shared_impl<__ensure_started::__ensure_started_t>
3966 {};
3967
3968 STDEXEC_PRAGMA_PUSH()
3969 STDEXEC_PRAGMA_IGNORE_EDG(not_used_in_partial_spec_arg_list)
3970
3971 /////////////////////////////////////////////////////////////////////////////
3972 // a receiver adaptor that augments its environment
3973 namespace __detail
3974 {
3975 template <auto _ReceiverPtr, auto... _EnvFns>
3976 struct __receiver_with;
3977
3978 template <class _Operation, class _Receiver,
3979 _Receiver _Operation::*_ReceiverPtr, auto... _EnvFns>
3980 struct __receiver_with<_ReceiverPtr, _EnvFns...>
3981 {
3982 struct __t : receiver_adaptor<__t>
3983 {
3984 using __id = __receiver_with;
3985 using __env_t = __env::__join_t<__result_of<_EnvFns, _Operation*>...,
3986 env_of_t<_Receiver>>;
3987
3988 _Operation* __op_state_;
3989
basestdexec::__detail::__receiver_with::__t3990 auto base() && noexcept -> _Receiver&&
3991 {
3992 return static_cast<_Receiver&&>(__op_state_->*_ReceiverPtr);
3993 }
3994
get_envstdexec::__detail::__receiver_with::__t3995 auto get_env() const noexcept -> __env_t
3996 {
3997 return __env::__join(_EnvFns(__op_state_)...,
3998 stdexec::get_env(__op_state_->*_ReceiverPtr));
3999 }
4000 };
4001 };
4002 } // namespace __detail
4003
4004 STDEXEC_PRAGMA_POP()
4005
4006 //////////////////////////////////////////////////////////////////////////////
4007 // [exec.let]
4008 namespace __let
4009 {
4010 template <class _Set, class _Domain = dependent_domain>
4011 struct __let_t;
4012
4013 template <class _Set>
4014 inline constexpr __mstring __in_which_let_msg{
4015 "In stdexec::let_value(Sender, Function)..."};
4016
4017 template <>
4018 inline constexpr __mstring __in_which_let_msg<set_error_t>{
4019 "In stdexec::let_error(Sender, Function)..."};
4020
4021 template <>
4022 inline constexpr __mstring __in_which_let_msg<set_stopped_t>{
4023 "In stdexec::let_stopped(Sender, Function)..."};
4024
4025 template <class _Set>
4026 using __on_not_callable = __callable_error<__in_which_let_msg<_Set>>;
4027
4028 // FUTURE: when we have a scheduler query for "always completes inline",
4029 // then we can use that instead of hard-coding `__inln::__scheduler` here.
4030 template <class _Scheduler>
4031 concept __unknown_context =
4032 __one_of<_Scheduler, __none_such, __inln::__scheduler>;
4033
4034 template <class _Receiver, class _Scheduler>
4035 struct __receiver_with_sched
4036 {
4037 using receiver_concept = receiver_t;
4038 _Receiver __rcvr_;
4039 _Scheduler __sched_;
4040
4041 template <__completion_tag _Tag, same_as<__receiver_with_sched> _Self,
4042 class... _As>
tag_invoke(_Tag,_Self && __self,_As &&...__as)4043 friend void tag_invoke(_Tag, _Self&& __self, _As&&... __as) noexcept
4044 {
4045 _Tag()(static_cast<_Receiver&&>(__self.__rcvr_),
4046 static_cast<_As&&>(__as)...);
4047 }
4048
4049 template <same_as<get_env_t> _Tag>
tag_invoke(_Tag,const __receiver_with_sched & __self)4050 friend auto tag_invoke(_Tag, const __receiver_with_sched& __self) noexcept
4051 {
4052 return __env::__join(
4053 __env::__with(__self.__sched_, get_scheduler),
4054 __env::__without(get_env(__self.__rcvr_), get_domain));
4055 }
4056 };
4057
4058 template <class _Receiver, class _Scheduler>
4059 __receiver_with_sched(_Receiver, _Scheduler)
4060 -> __receiver_with_sched<_Receiver, _Scheduler>;
4061
4062 // If the input sender knows its completion scheduler, make it the current
4063 // scheduler in the environment seen by the result sender.
4064 template <class _Env, class _Scheduler>
4065 using __result_env_t =
4066 __if_c<__unknown_context<_Scheduler>, _Env,
4067 __env::__join_t<__env::__with<_Scheduler, get_scheduler_t>,
4068 __env::__without_t<_Env, get_domain_t>>>;
4069
4070 template <class _Tp>
4071 using __decay_ref = __decay_t<_Tp>&;
4072
4073 template <__mstring _Where, __mstring _What>
4074 struct _FUNCTION_MUST_RETURN_A_VALID_SENDER_IN_THE_CURRENT_ENVIRONMENT_
4075 {};
4076
4077 #if STDEXEC_NVHPC()
4078 template <class _Sender, class _Env, class _Set>
4079 struct __bad_result_sender_
4080 {
4081 using __t = __mexception<
4082 _FUNCTION_MUST_RETURN_A_VALID_SENDER_IN_THE_CURRENT_ENVIRONMENT_<
4083 __in_which_let_msg<_Set>,
4084 "The function must return a valid sender for the current environment"_mstr>,
4085 _WITH_SENDER_<_Sender>, _WITH_ENVIRONMENT_<_Env>>;
4086 };
4087 template <class _Sender, class _Env, class _Set>
4088 using __bad_result_sender = __t<__bad_result_sender_<_Sender, _Env, _Set>>;
4089 #else
4090 template <class _Sender, class _Env, class _Set>
4091 using __bad_result_sender = __mexception<
4092 _FUNCTION_MUST_RETURN_A_VALID_SENDER_IN_THE_CURRENT_ENVIRONMENT_<
4093 __in_which_let_msg<_Set>,
4094 "The function must return a valid sender for the current environment"_mstr>,
4095 _WITH_SENDER_<_Sender>, _WITH_ENVIRONMENT_<_Env>>;
4096 #endif
4097
4098 template <class _Sender, class _Env, class _Set>
4099 using __ensure_sender = //
4100 __minvoke_if_c<sender_in<_Sender, _Env>, __q<__midentity>,
4101 __mbind_back_q<__bad_result_sender, _Env, _Set>, _Sender>;
4102
4103 // A metafunction that computes the result sender type for a given set of
4104 // argument types
4105 template <class _Fun, class _Set, class _Env, class _Sched>
4106 using __result_sender_fn = //
4107 __mcompose<
4108 __mbind_back_q<__ensure_sender, __result_env_t<_Env, _Sched>, _Set>,
4109 __transform<__q<__decay_ref>,
4110 __mbind_front<__mtry_catch_q<__call_result_t,
4111 __on_not_callable<_Set>>,
4112 _Fun>>>;
4113
4114 // The receiver that gets connected to the result sender is the input receiver,
4115 // possibly augmented with the input sender's completion scheduler (which is
4116 // where the result sender will be started).
4117 template <class _Receiver, class _Scheduler>
4118 using __result_receiver_t =
4119 __if_c<__unknown_context<_Scheduler>, _Receiver,
4120 __receiver_with_sched<_Receiver, _Scheduler>>;
4121
4122 template <class _Receiver, class _Fun, class _Set, class _Sched>
4123 using __op_state_for = //
4124 __mcompose<__mbind_back_q<connect_result_t,
4125 __result_receiver_t<_Receiver, _Sched>>,
4126 __result_sender_fn<_Fun, _Set, env_of_t<_Receiver>, _Sched>>;
4127
4128 template <class _Set, class _Sig>
4129 struct __tfx_signal_fn
4130 {
4131 template <class, class, class>
4132 using __f = completion_signatures<_Sig>;
4133 };
4134
4135 template <class _Set, class... _Args>
4136 struct __tfx_signal_fn<_Set, _Set(_Args...)>
4137 {
4138 template <class _Env, class _Fun, class _Sched>
4139 using __f = //
4140 __try_make_completion_signatures<
4141 __minvoke<__result_sender_fn<_Fun, _Set, _Env, _Sched>, _Args...>,
4142 __result_env_t<_Env, _Sched>,
4143 // because we don't know if connect-ing the result sender will
4144 // throw:
4145 completion_signatures<set_error_t(std::exception_ptr)>>;
4146 };
4147
4148 // `_Sched` is the input sender's completion scheduler, or __none_such if it
4149 // doesn't have one.
4150 template <class _Env, class _Fun, class _Set, class _Sched, class _Sig>
4151 using __tfx_signal_t =
4152 __minvoke<__tfx_signal_fn<_Set, _Sig>, _Env, _Fun, _Sched>;
4153
4154 template <class _Sender, class _Set>
4155 using __completion_sched = __query_result_or_t<get_completion_scheduler_t<_Set>,
4156 env_of_t<_Sender>, __none_such>;
4157
4158 template <class _CvrefSender, class _Env, class _LetTag, class _Fun>
4159 using __completions = //
4160 __mapply<__transform<__mbind_front_q<
4161 __tfx_signal_t, _Env, _Fun, __t<_LetTag>,
4162 __completion_sched<_CvrefSender, __t<_LetTag>>>,
4163 __q<__concat_completion_signatures_t>>,
4164 __completion_signatures_of_t<_CvrefSender, _Env>>;
4165
4166 template <__mstring _Where, __mstring _What>
4167 struct _NO_COMMON_DOMAIN_
4168 {};
4169
4170 template <class _Set>
4171 using __no_common_domain_t = //
4172 _NO_COMMON_DOMAIN_<
4173 __in_which_let_msg<_Set>,
4174 "The senders returned by Function do not all share a common domain"_mstr>;
4175
4176 template <class _Set>
4177 using __try_common_domain_fn = //
4178 __mtry_catch_q<
4179 __domain::__common_domain_t,
4180 __mcompose<__mbind_front_q<__mexception, __no_common_domain_t<_Set>>,
4181 __q<_WITH_SENDERS_>>>;
4182
4183 // Compute all the domains of all the result senders and make sure they're all
4184 // the same
4185 template <class _Set, class _Child, class _Fun, class _Env, class _Sched>
4186 using __result_domain_t = //
4187 __gather_completions_for<_Set, _Child, _Env,
4188 __result_sender_fn<_Fun, _Set, _Env, _Sched>,
4189 __try_common_domain_fn<_Set>>;
4190
4191 template <class _LetTag, class _Env>
__mk_transform_env_fn(const _Env & __env)4192 auto __mk_transform_env_fn(const _Env& __env) noexcept
4193 {
4194 using _Set = __t<_LetTag>;
4195 return [&]<class _Fun, class _Child>(__ignore, _Fun&&,
4196 _Child&& __child) -> decltype(auto) {
4197 using __completions_t = __completion_signatures_of_t<_Child, _Env>;
4198 if constexpr (__merror<__completions_t>)
4199 {
4200 return __completions_t();
4201 }
4202 else
4203 {
4204 using _Scheduler = __completion_sched<_Child, _Set>;
4205 if constexpr (__unknown_context<_Scheduler>)
4206 {
4207 return (__env);
4208 }
4209 else
4210 {
4211 return __env::__join(
4212 __env::__with(get_completion_scheduler<_Set>(
4213 stdexec::get_env(__child)),
4214 get_scheduler),
4215 __env::__without(__env, get_domain));
4216 }
4217 }
4218 };
4219 }
4220
4221 template <class _LetTag, class _Env>
__mk_transform_sender_fn(const _Env &)4222 auto __mk_transform_sender_fn(const _Env&) noexcept
4223 {
4224 using _Set = __t<_LetTag>;
4225 return
4226 []<class _Fun, class _Child>(__ignore, _Fun&& __fun, _Child&& __child) {
4227 using __completions_t = __completion_signatures_of_t<_Child, _Env>;
4228 if constexpr (__merror<__completions_t>)
4229 {
4230 return __completions_t();
4231 }
4232 else
4233 {
4234 using _Sched = __completion_sched<_Child, _Set>;
4235 using _Domain = __result_domain_t<_Set, _Child, _Fun, _Env, _Sched>;
4236 if constexpr (__merror<_Domain>)
4237 {
4238 return _Domain();
4239 }
4240 else if constexpr (same_as<_Domain, dependent_domain>)
4241 {
4242 using _Domain2 = __late_domain_of_t<_Child, _Env>;
4243 return __make_sexpr<__let_t<_Set, _Domain2>>(
4244 static_cast<_Fun&&>(__fun), static_cast<_Child&&>(__child));
4245 }
4246 else
4247 {
4248 static_assert(!same_as<_Domain, __none_such>);
4249 return __make_sexpr<__let_t<_Set, _Domain>>(
4250 static_cast<_Fun&&>(__fun), static_cast<_Child&&>(__child));
4251 }
4252 }
4253 };
4254 }
4255
4256 template <class _Receiver, class _Fun, class _Set, class _Sched,
4257 class... _Tuples>
4258 struct __let_state
4259 {
4260 using __fun_t = _Fun;
4261 using __sched_t = _Sched;
4262
4263 using __result_variant = std::variant<std::monostate, _Tuples...>;
4264
4265 using __op_state_variant = //
4266 __minvoke<__transform<
4267 __uncurry<__op_state_for<_Receiver, _Fun, _Set, _Sched>>,
4268 __nullable_variant_t>,
4269 _Tuples...>;
4270
__get_result_receiverstdexec::__let::__let_state4271 auto __get_result_receiver(_Receiver&& __rcvr) -> decltype(auto)
4272 {
4273 if constexpr (__unknown_context<_Sched>)
4274 {
4275 return static_cast<_Receiver&&>(__rcvr);
4276 }
4277 else
4278 {
4279 return __receiver_with_sched{static_cast<_Receiver&&>(__rcvr),
4280 this->__sched_};
4281 }
4282 }
4283
4284 STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS _Fun __fun_;
4285 STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS _Sched __sched_;
4286 __result_variant __args_;
4287 __op_state_variant __op_state3_;
4288 };
4289
4290 template <class _Set, class _Domain>
4291 struct __let_t
4292 {
4293 using __domain_t = _Domain;
4294 using __t = _Set;
4295
4296 template <sender _Sender, __movable_value _Fun>
operator ()stdexec::__let::__let_t4297 auto operator()(_Sender&& __sndr, _Fun __fun) const -> __well_formed_sender
4298 auto
4299 {
4300 auto __domain = __get_early_domain(__sndr);
4301 return stdexec::transform_sender(
4302 __domain,
4303 __make_sexpr<__let_t<_Set>>(static_cast<_Fun&&>(__fun),
4304 static_cast<_Sender&&>(__sndr)));
4305 }
4306
4307 template <class _Fun>
4308 STDEXEC_ATTRIBUTE((always_inline))
operator ()stdexec::__let::__let_t4309 __binder_back<__let_t, _Fun> operator()(_Fun __fun) const
4310 {
4311 return {{}, {}, {static_cast<_Fun&&>(__fun)}};
4312 }
4313
4314 using _Sender = __1;
4315 using _Function = __0;
4316 using __legacy_customizations_t =
4317 __types<tag_invoke_t(__let_t,
4318 get_completion_scheduler_t<set_value_t>(
4319 get_env_t(const _Sender&)),
4320 _Sender, _Function),
4321 tag_invoke_t(__let_t, _Sender, _Function)>;
4322
4323 template <sender_expr_for<__let_t<_Set>> _Sender, class _Env>
transform_envstdexec::__let::__let_t4324 static auto transform_env(_Sender&& __sndr, const _Env& __env)
4325 -> decltype(auto)
4326 {
4327 return __sexpr_apply(static_cast<_Sender&&>(__sndr),
4328 __mk_transform_env_fn<__let_t<_Set>>(__env));
4329 }
4330
4331 template <sender_expr_for<__let_t<_Set>> _Sender, class _Env>
4332 requires same_as<__early_domain_of_t<_Sender>, dependent_domain>
transform_senderstdexec::__let::__let_t4333 static auto transform_sender(_Sender&& __sndr, const _Env& __env)
4334 -> decltype(auto)
4335 {
4336 return __sexpr_apply(static_cast<_Sender&&>(__sndr),
4337 __mk_transform_sender_fn<__let_t<_Set>>(__env));
4338 }
4339 };
4340
4341 template <class _Set, class _Domain>
4342 struct __let_impl : __sexpr_defaults
4343 {
4344 static constexpr auto get_attrs = //
4345 []<class _Child>(__ignore, const _Child& __child) noexcept {
4346 return __env::__join(__env::__with(_Domain(), get_domain),
4347 stdexec::get_env(__child));
4348 };
4349
4350 static constexpr auto get_completion_signatures = //
4351 []<class _Self, class _Env>(_Self&&, _Env&&) noexcept
4352 -> __completions<__child_of<_Self>, _Env, __let_t<_Set, _Domain>,
4353 __data_of<_Self>> {
4354 static_assert(sender_expr_for<_Self, __let_t<_Set, _Domain>>);
4355 return {};
4356 };
4357
4358 static constexpr auto get_state = //
4359 []<class _Sender, class _Receiver>(_Sender&& __sndr, _Receiver&) {
4360 static_assert(sender_expr_for<_Sender, __let_t<_Set, _Domain>>);
4361 using _Fun = __data_of<_Sender>;
4362 using _Sched = __completion_sched<_Sender, _Set>;
4363 using __mk_let_state =
4364 __mbind_front_q<__let_state, _Receiver, _Fun, _Set, _Sched>;
4365
4366 using __let_state_t =
4367 __gather_completions_for<_Set, __child_of<_Sender>,
4368 env_of_t<_Receiver>, __q<__decayed_tuple>,
4369 __mk_let_state>;
4370
4371 _Sched __sched = query_or(get_completion_scheduler<_Set>,
4372 stdexec::get_env(__sndr), __none_such());
4373 return __let_state_t{
4374 STDEXEC_CALL_EXPLICIT_THIS_MEMFN(static_cast<_Sender&&>(__sndr),
4375 apply)(__detail::__get_data()),
4376 __sched};
4377 };
4378
4379 template <class _State, class _Receiver, class... _As>
__bindstdexec::__let::__let_impl4380 static void __bind(_State& __state, _Receiver& __rcvr,
4381 _As&&... __as) noexcept
4382 {
4383 try
4384 {
4385 auto& __args =
4386 __state.__args_.template emplace<__decayed_tuple<_As...>>(
4387 static_cast<_As&&>(__as)...);
4388 auto __sndr2 = __apply(std::move(__state.__fun_), __args);
4389 auto __rcvr2 =
4390 __state.__get_result_receiver(static_cast<_Receiver&&>(__rcvr));
4391 auto __mkop = [&] {
4392 return stdexec::connect(std::move(__sndr2), std::move(__rcvr2));
4393 };
4394 auto& __op2 =
4395 __state.__op_state3_.template emplace<decltype(__mkop())>(
4396 __conv{__mkop});
4397 stdexec::start(__op2);
4398 }
4399 catch (...)
4400 {
4401 set_error(std::move(__rcvr), std::current_exception());
4402 }
4403 }
4404
4405 static constexpr auto complete = //
4406 []<class _State, class _Receiver, class _Tag, class... _As>(
4407 __ignore, _State& __state, _Receiver& __rcvr, _Tag,
4408 _As&&... __as) noexcept -> void {
4409 if constexpr (std::same_as<_Tag, _Set>)
4410 {
4411 __bind(__state, __rcvr, static_cast<_As&&>(__as)...);
4412 }
4413 else
4414 {
4415 _Tag()(static_cast<_Receiver&&>(__rcvr),
4416 static_cast<_As&&>(__as)...);
4417 }
4418 };
4419 };
4420 } // namespace __let
4421
4422 using let_value_t = __let::__let_t<set_value_t>;
4423 inline constexpr let_value_t let_value{};
4424
4425 using let_error_t = __let::__let_t<set_error_t>;
4426 inline constexpr let_error_t let_error{};
4427
4428 using let_stopped_t = __let::__let_t<set_stopped_t>;
4429 inline constexpr let_stopped_t let_stopped{};
4430
4431 template <class _Set, class _Domain>
4432 struct __sexpr_impl<__let::__let_t<_Set, _Domain>> :
4433 __let::__let_impl<_Set, _Domain>
4434 {};
4435
4436 /////////////////////////////////////////////////////////////////////////////
4437 // [execution.senders.adaptors.stopped_as_optional]
4438 // [execution.senders.adaptors.stopped_as_error]
4439 namespace __stopped_as_xxx
4440 {
4441 struct stopped_as_optional_t
4442 {
4443 template <sender _Sender>
operator ()stdexec::__stopped_as_xxx::stopped_as_optional_t4444 auto operator()(_Sender&& __sndr) const
4445 {
4446 return __make_sexpr<stopped_as_optional_t>(
4447 __(), static_cast<_Sender&&>(__sndr));
4448 }
4449
4450 STDEXEC_ATTRIBUTE((always_inline))
4451
operator ()stdexec::__stopped_as_xxx::stopped_as_optional_t4452 __binder_back<stopped_as_optional_t> operator()() const noexcept
4453 {
4454 return {};
4455 }
4456 };
4457
4458 struct __stopped_as_optional_impl : __sexpr_defaults
4459 {
4460 template <class... _Tys>
4461 requires(sizeof...(_Tys) == 1)
4462 using __set_value_t =
4463 completion_signatures<set_value_t(std::optional<__decay_t<_Tys>>...)>;
4464
4465 template <class _Ty>
4466 using __set_error_t = completion_signatures<set_error_t(_Ty)>;
4467
4468 static constexpr auto get_completion_signatures = //
4469 []<class _Self, class _Env>(_Self&&, _Env&&) noexcept //
4470 -> make_completion_signatures<
4471 __child_of<_Self>, _Env,
4472 completion_signatures<set_error_t(std::exception_ptr)>,
4473 __set_value_t, __set_error_t, completion_signatures<>> {
4474 static_assert(sender_expr_for<_Self, stopped_as_optional_t>);
4475 return {};
4476 };
4477
4478 static constexpr auto get_state = //
4479 []<class _Self, class _Receiver>(_Self&&, _Receiver&) noexcept
4480 requires __single_typed_sender<__child_of<_Self>, env_of_t<_Receiver>>
4481 {
4482 static_assert(sender_expr_for<_Self, stopped_as_optional_t>);
4483 using _Value = __decay_t<
4484 __single_sender_value_t<__child_of<_Self>, env_of_t<_Receiver>>>;
4485 return __mtype<_Value>();
4486 };
4487
4488 static constexpr auto complete = //
4489 []<class _State, class _Receiver, __completion_tag _Tag,
4490 class... _Args>(__ignore, _State&, _Receiver& __rcvr, _Tag,
4491 _Args&&... __args) noexcept -> void {
4492 if constexpr (same_as<_Tag, set_value_t>)
4493 {
4494 try
4495 {
4496 static_assert(constructible_from<__t<_State>, _Args...>);
4497 stdexec::set_value(static_cast<_Receiver&&>(__rcvr),
4498 std::optional<__t<_State>>{
4499 static_cast<_Args&&>(__args)...});
4500 }
4501 catch (...)
4502 {
4503 stdexec::set_error(static_cast<_Receiver&&>(__rcvr),
4504 std::current_exception());
4505 }
4506 }
4507 else if constexpr (same_as<_Tag, set_error_t>)
4508 {
4509 stdexec::set_error(static_cast<_Receiver&&>(__rcvr),
4510 static_cast<_Args&&>(__args)...);
4511 }
4512 else
4513 {
4514 stdexec::set_value(static_cast<_Receiver&&>(__rcvr),
4515 std::optional<__t<_State>>{std::nullopt});
4516 }
4517 };
4518 };
4519
4520 struct stopped_as_error_t
4521 {
4522 template <sender _Sender, __movable_value _Error>
operator ()stdexec::__stopped_as_xxx::stopped_as_error_t4523 auto operator()(_Sender&& __sndr, _Error __err) const
4524 {
4525 return static_cast<_Sender&&>(__sndr) |
4526 let_stopped(
4527 [__err2 = static_cast<_Error&&>(__err)]() mutable //
4528 noexcept(std::is_nothrow_move_constructible_v<_Error>) {
4529 return just_error(static_cast<_Error&&>(__err2));
4530 });
4531 }
4532
4533 template <__movable_value _Error>
4534 STDEXEC_ATTRIBUTE((always_inline))
operator ()stdexec::__stopped_as_xxx::stopped_as_error_t4535 auto operator()(_Error __err) const
4536 -> __binder_back<stopped_as_error_t, _Error>
4537 {
4538 return {{}, {}, {static_cast<_Error&&>(__err)}};
4539 }
4540 };
4541 } // namespace __stopped_as_xxx
4542
4543 using __stopped_as_xxx::stopped_as_optional_t;
4544 inline constexpr stopped_as_optional_t stopped_as_optional{};
4545 using __stopped_as_xxx::stopped_as_error_t;
4546 inline constexpr stopped_as_error_t stopped_as_error{};
4547
4548 template <>
4549 struct __sexpr_impl<stopped_as_optional_t> :
4550 __stopped_as_xxx::__stopped_as_optional_impl
4551 {};
4552
4553 /////////////////////////////////////////////////////////////////////////////
4554 // run_loop
4555 namespace __loop
4556 {
4557 class run_loop;
4558
4559 struct __task : __immovable
4560 {
4561 __task* __next_ = this;
4562
4563 union
4564 {
4565 void (*__execute_)(__task*) noexcept;
4566 __task* __tail_;
4567 };
4568
__executestdexec::__loop::__task4569 void __execute() noexcept
4570 {
4571 (*__execute_)(this);
4572 }
4573 };
4574
4575 template <class _ReceiverId>
4576 struct __operation
4577 {
4578 using _Receiver = stdexec::__t<_ReceiverId>;
4579
4580 struct __t : __task
4581 {
4582 using __id = __operation;
4583
4584 run_loop* __loop_;
4585 STDEXEC_ATTRIBUTE((no_unique_address))
4586 _Receiver __rcvr_;
4587
__execute_implstdexec::__loop::__operation::__t4588 static void __execute_impl(__task* __p) noexcept
4589 {
4590 auto& __rcvr = static_cast<__t*>(__p)->__rcvr_;
4591 try
4592 {
4593 if (get_stop_token(get_env(__rcvr)).stop_requested())
4594 {
4595 set_stopped(static_cast<_Receiver&&>(__rcvr));
4596 }
4597 else
4598 {
4599 set_value(static_cast<_Receiver&&>(__rcvr));
4600 }
4601 }
4602 catch (...)
4603 {
4604 set_error(static_cast<_Receiver&&>(__rcvr),
4605 std::current_exception());
4606 }
4607 }
4608
__tstdexec::__loop::__operation::__t4609 explicit __t(__task* __tail) noexcept : __task{.__tail_ = __tail} {}
4610
__tstdexec::__loop::__operation::__t4611 __t(__task* __next, run_loop* __loop, _Receiver __rcvr) :
4612 __task{{}, __next, {&__execute_impl}}, __loop_{__loop},
4613 __rcvr_{static_cast<_Receiver&&>(__rcvr)}
4614 {}
4615
tag_invokestdexec::__loop::__operation4616 friend void tag_invoke(start_t, __t& __self) noexcept
4617 {
4618 __self.__start_();
4619 }
4620
4621 void __start_() noexcept;
4622 };
4623 };
4624
4625 class run_loop
4626 {
4627 template <class... Ts>
4628 using __completion_signatures_ = completion_signatures<Ts...>;
4629
4630 template <class>
4631 friend struct __operation;
4632
4633 public:
4634 struct __scheduler
4635 {
4636 using __t = __scheduler;
4637 using __id = __scheduler;
4638 auto operator==(const __scheduler&) const noexcept -> bool = default;
4639
4640 private:
4641 struct __schedule_task
4642 {
4643 using __t = __schedule_task;
4644 using __id = __schedule_task;
4645 using sender_concept = sender_t;
4646 using completion_signatures = //
4647 __completion_signatures_<set_value_t(),
4648 set_error_t(std::exception_ptr),
4649 set_stopped_t()>;
4650
4651 private:
4652 friend __scheduler;
4653
4654 template <class _Receiver>
4655 using __operation =
4656 stdexec::__t<__operation<stdexec::__id<_Receiver>>>;
4657
4658 template <class _Receiver>
tag_invokestdexec::__loop::run_loop::__scheduler4659 friend auto tag_invoke(connect_t, const __schedule_task& __self,
4660 _Receiver __rcvr) -> __operation<_Receiver>
4661 {
4662 return __self.__connect_(static_cast<_Receiver&&>(__rcvr));
4663 }
4664
4665 template <class _Receiver>
__connect_stdexec::__loop::run_loop::__scheduler::__schedule_task4666 auto __connect_(_Receiver&& __rcvr) const -> __operation<_Receiver>
4667 {
4668 return {&__loop_->__head_, __loop_,
4669 static_cast<_Receiver&&>(__rcvr)};
4670 }
4671
4672 struct __env
4673 {
4674 run_loop* __loop_;
4675
4676 template <class _CPO>
tag_invokestdexec::__loop::run_loop::__scheduler::__schedule_task4677 friend auto tag_invoke(get_completion_scheduler_t<_CPO>,
4678 const __env& __self) noexcept
4679 -> __scheduler
4680 {
4681 return __self.__loop_->get_scheduler();
4682 }
4683 };
4684
tag_invokestdexec::__loop::run_loop::__scheduler4685 friend auto tag_invoke(get_env_t,
4686 const __schedule_task& __self) noexcept
4687 -> __env
4688 {
4689 return __env{__self.__loop_};
4690 }
4691
__schedule_taskstdexec::__loop::run_loop::__scheduler::__schedule_task4692 explicit __schedule_task(run_loop* __loop) noexcept :
4693 __loop_(__loop)
4694 {}
4695
4696 run_loop* const __loop_;
4697 };
4698
4699 friend run_loop;
4700
__schedulerstdexec::__loop::run_loop::__scheduler4701 explicit __scheduler(run_loop* __loop) noexcept : __loop_(__loop) {}
4702
tag_invoke(schedule_t,const __scheduler & __self)4703 friend auto tag_invoke(schedule_t, const __scheduler& __self) noexcept
4704 -> __schedule_task
4705 {
4706 return __self.__schedule();
4707 }
4708
tag_invoke(get_forward_progress_guarantee_t,const __scheduler &)4709 friend auto tag_invoke(get_forward_progress_guarantee_t,
4710 const __scheduler&) noexcept
4711 -> stdexec::forward_progress_guarantee
4712 {
4713 return stdexec::forward_progress_guarantee::parallel;
4714 }
4715
4716 // BUGBUG NOT TO SPEC
tag_invoke(execute_may_block_caller_t,const __scheduler &)4717 friend auto tag_invoke(execute_may_block_caller_t,
4718 const __scheduler&) noexcept -> bool
4719 {
4720 return false;
4721 }
4722
__schedulestdexec::__loop::run_loop::__scheduler4723 [[nodiscard]] auto __schedule() const noexcept -> __schedule_task
4724 {
4725 return __schedule_task{__loop_};
4726 }
4727
4728 run_loop* __loop_;
4729 };
4730
get_scheduler()4731 auto get_scheduler() noexcept -> __scheduler
4732 {
4733 return __scheduler{this};
4734 }
4735
4736 void run();
4737
4738 void finish();
4739
4740 private:
4741 void __push_back_(__task* __task);
4742 auto __pop_front_() -> __task*;
4743
4744 std::mutex __mutex_;
4745 std::condition_variable __cv_;
4746 __task __head_{.__tail_ = &__head_};
4747 bool __stop_ = false;
4748 };
4749
4750 template <class _ReceiverId>
__start_()4751 inline void __operation<_ReceiverId>::__t::__start_() noexcept
4752 {
4753 try
4754 {
4755 __loop_->__push_back_(this);
4756 }
4757 catch (...)
4758 {
4759 set_error(static_cast<_Receiver&&>(__rcvr_), std::current_exception());
4760 }
4761 }
4762
run()4763 inline void run_loop::run()
4764 {
4765 for (__task* __task; (__task = __pop_front_()) != &__head_;)
4766 {
4767 __task->__execute();
4768 }
4769 }
4770
finish()4771 inline void run_loop::finish()
4772 {
4773 std::unique_lock __lock{__mutex_};
4774 __stop_ = true;
4775 __cv_.notify_all();
4776 }
4777
__push_back_(__task * __task)4778 inline void run_loop::__push_back_(__task* __task)
4779 {
4780 std::unique_lock __lock{__mutex_};
4781 __task->__next_ = &__head_;
4782 __head_.__tail_ = __head_.__tail_->__next_ = __task;
4783 __cv_.notify_one();
4784 }
4785
__pop_front_()4786 inline auto run_loop::__pop_front_() -> __task*
4787 {
4788 std::unique_lock __lock{__mutex_};
4789 __cv_.wait(__lock,
4790 [this] { return __head_.__next_ != &__head_ || __stop_; });
4791 if (__head_.__tail_ == __head_.__next_)
4792 __head_.__tail_ = &__head_;
4793 return std::exchange(__head_.__next_, __head_.__next_->__next_);
4794 }
4795 } // namespace __loop
4796
4797 // NOT TO SPEC
4798 using run_loop = __loop::run_loop;
4799
4800 /////////////////////////////////////////////////////////////////////////////
4801 // [execution.senders.adaptors.schedule_from]
4802 namespace __schedule_from
4803 {
4804 // Compute a variant type that is capable of storing the results of the
4805 // input sender when it completes. The variant has type:
4806 // variant<
4807 // monostate,
4808 // tuple<set_stopped_t>,
4809 // tuple<set_value_t, __decay_t<_Values1>...>,
4810 // tuple<set_value_t, __decay_t<_Values2>...>,
4811 // ...
4812 // tuple<set_error_t, __decay_t<_Error1>>,
4813 // tuple<set_error_t, __decay_t<_Error2>>,
4814 // ...
4815 // >
4816 template <class _CvrefSender, class _Env>
4817 using __variant_for_t = __compl_sigs::__maybe_for_all_sigs<
4818 __completion_signatures_of_t<_CvrefSender, _Env>, __q<__decayed_tuple>,
4819 __nullable_variant_t>;
4820
4821 template <class _Tp>
4822 using __decay_rvalue_ref = __decay_t<_Tp>&&;
4823
4824 template <class _Tag>
4825 using __decay_signature =
4826 __transform<__q<__decay_rvalue_ref>,
4827 __mcompose<__q<completion_signatures>, __qf<_Tag>>>;
4828
4829 template <class... _Ts>
4830 using __all_nothrow_decay_copyable_ =
4831 __mbool<(__nothrow_decay_copyable<_Ts> && ...)>;
4832
4833 template <class _CvrefSender, class _Env>
4834 using __all_values_and_errors_nothrow_decay_copyable = //
4835 __compl_sigs::__maybe_for_all_sigs<
4836 __completion_signatures_of_t<_CvrefSender, _Env>,
4837 __q<__all_nothrow_decay_copyable_>, __q<__mand>>;
4838
4839 template <class _CvrefSender, class _Env>
4840 using __with_error_t = //
4841 __if<__all_values_and_errors_nothrow_decay_copyable<_CvrefSender, _Env>,
4842 completion_signatures<>, __with_exception_ptr>;
4843
4844 template <class _Scheduler, class _CvrefSender, class _Env>
4845 using __completions_t = //
4846 __try_make_completion_signatures<
4847 _CvrefSender, _Env,
4848 __try_make_completion_signatures<schedule_result_t<_Scheduler>, _Env,
4849 __with_error_t<_CvrefSender, _Env>,
4850 __mconst<completion_signatures<>>>,
4851 __decay_signature<set_value_t>, __decay_signature<set_error_t>>;
4852
4853 template <class _SchedulerId>
4854 struct __environ
4855 {
4856 using _Scheduler = stdexec::__t<_SchedulerId>;
4857
4858 struct __t :
4859 __env::__with<stdexec::__t<_SchedulerId>,
4860 get_completion_scheduler_t<set_value_t>,
4861 get_completion_scheduler_t<set_stopped_t>>
4862 {
4863 using __id = __environ;
4864
__tstdexec::__schedule_from::__environ::__t4865 explicit __t(_Scheduler __sched) noexcept :
4866 __t::__with{std::move(__sched)}
4867 {}
4868
4869 template <same_as<get_domain_t> _Key>
tag_invokestdexec::__schedule_from::__environ4870 friend auto tag_invoke(_Key, const __t& __self) noexcept
4871 {
4872 return query_or(get_domain, __self.__value_, default_domain());
4873 }
4874 };
4875 };
4876
4877 template <class _Scheduler, class _Sexpr, class _Receiver>
4878 struct __state;
4879
4880 // This receiver is to be completed on the execution context
4881 // associated with the scheduler. When the source sender
4882 // completes, the completion information is saved off in the
4883 // operation state so that when this receiver completes, it can
4884 // read the completion out of the operation state and forward it
4885 // to the output receiver after transitioning to the scheduler's
4886 // context.
4887 template <class _Scheduler, class _Sexpr, class _Receiver>
4888 struct __receiver2 :
4889 receiver_adaptor<__receiver2<_Scheduler, _Sexpr, _Receiver>>
4890 {
__receiver2stdexec::__schedule_from::__receiver24891 explicit __receiver2(
4892 __state<_Scheduler, _Sexpr, _Receiver>* __state) noexcept :
4893 __state_{__state}
4894 {}
4895
basestdexec::__schedule_from::__receiver24896 auto base() && noexcept -> _Receiver&&
4897 {
4898 return std::move(__state_->__receiver());
4899 }
4900
basestdexec::__schedule_from::__receiver24901 auto base() const& noexcept -> const _Receiver&
4902 {
4903 return __state_->__receiver();
4904 }
4905
set_valuestdexec::__schedule_from::__receiver24906 void set_value() && noexcept
4907 {
4908 STDEXEC_ASSERT(!__state_->__data_.valueless_by_exception());
4909 std::visit(
4910 [__state = __state_]<class _Tup>(_Tup& __tupl) noexcept -> void {
4911 if constexpr (same_as<_Tup, std::monostate>)
4912 {
4913 std::terminate(); // reaching this indicates a bug in
4914 // schedule_from
4915 }
4916 else
4917 {
4918 __apply(
4919 [&]<class... _Args>(auto __tag,
4920 _Args&... __args) noexcept -> void {
4921 __tag(std::move(__state->__receiver()),
4922 static_cast<_Args&&>(__args)...);
4923 },
4924 __tupl);
4925 }
4926 },
4927 __state_->__data_);
4928 }
4929
4930 __state<_Scheduler, _Sexpr, _Receiver>* __state_;
4931 };
4932
4933 template <class _Scheduler, class _Sexpr, class _Receiver>
4934 struct __state : __enable_receiver_from_this<_Sexpr, _Receiver>
4935 {
4936 using __variant_t =
4937 __variant_for_t<__child_of<_Sexpr>, env_of_t<_Receiver>>;
4938 using __receiver2_t = __receiver2<_Scheduler, _Sexpr, _Receiver>;
4939
4940 __variant_t __data_;
4941 connect_result_t<schedule_result_t<_Scheduler>, __receiver2_t> __state2_;
4942
__statestdexec::__schedule_from::__state4943 explicit __state(_Scheduler __sched) :
4944 __data_(), __state2_(connect(schedule(__sched), __receiver2_t{this}))
4945 {}
4946 };
4947
4948 struct schedule_from_t
4949 {
4950 template <scheduler _Scheduler, sender _Sender>
operator ()stdexec::__schedule_from::schedule_from_t4951 auto operator()(_Scheduler&& __sched, _Sender&& __sndr) const
4952 -> __well_formed_sender auto
4953 {
4954 using _Env = __t<__environ<__id<__decay_t<_Scheduler>>>>;
4955 auto __env = _Env{{static_cast<_Scheduler&&>(__sched)}};
4956 auto __domain = query_or(get_domain, __sched, default_domain());
4957 return stdexec::transform_sender(
4958 __domain, __make_sexpr<schedule_from_t>(
4959 std::move(__env), static_cast<_Sender&&>(__sndr)));
4960 }
4961
4962 using _Sender = __1;
4963 using _Env = __0;
4964 using __legacy_customizations_t = __types<tag_invoke_t(
4965 schedule_from_t, get_completion_scheduler_t<set_value_t>(_Env&),
4966 _Sender)>;
4967 };
4968
4969 struct __schedule_from_impl : __sexpr_defaults
4970 {
4971 template <class _Sender>
4972 using __scheduler_t =
4973 __decay_t<__call_result_t<get_completion_scheduler_t<set_value_t>,
4974 env_of_t<_Sender>>>;
4975
4976 static constexpr auto get_attrs = //
4977 []<class _Data, class _Child>(const _Data& __data,
4978 const _Child& __child) noexcept {
4979 return __env::__join(__data, stdexec::get_env(__child));
4980 };
4981
4982 static constexpr auto get_completion_signatures = //
4983 []<class _Sender, class _Env>(_Sender&&, const _Env&) noexcept
4984 -> __completions_t<__scheduler_t<_Sender>, __child_of<_Sender>, _Env> {
4985 static_assert(sender_expr_for<_Sender, schedule_from_t>);
4986 return {};
4987 };
4988
4989 static constexpr auto get_state =
4990 []<class _Sender, class _Receiver>(_Sender&& __sndr, _Receiver&) {
4991 static_assert(sender_expr_for<_Sender, schedule_from_t>);
4992 auto __sched =
4993 get_completion_scheduler<set_value_t>(stdexec::get_env(__sndr));
4994 using _Scheduler = decltype(__sched);
4995 return __state<_Scheduler, _Sender, _Receiver>{__sched};
4996 };
4997
4998 static constexpr auto complete =
4999 []<class _Tag, class... _Args>(__ignore, auto& __state, auto& __rcvr,
5000 _Tag,
5001 _Args&&... __args) noexcept -> void {
5002 // Write the tag and the args into the operation state so that
5003 // we can forward the completion from within the scheduler's
5004 // execution context.
5005 using __async_result = __decayed_tuple<_Tag, _Args...>;
5006 if constexpr (__nothrow_constructible_from<__async_result, _Tag,
5007 _Args...>)
5008 {
5009 __state.__data_.template emplace<__async_result>(
5010 _Tag(), static_cast<_Args&&>(__args)...);
5011 }
5012 else
5013 {
5014 try
5015 {
5016 __state.__data_.template emplace<__async_result>(
5017 _Tag(), static_cast<_Args&&>(__args)...);
5018 }
5019 catch (...)
5020 {
5021 set_error(std::move(__rcvr), std::current_exception());
5022 return;
5023 }
5024 }
5025 // Enqueue the schedule operation so the completion happens
5026 // on the scheduler's execution context.
5027 stdexec::start(__state.__state2_);
5028 };
5029 };
5030 } // namespace __schedule_from
5031
5032 using __schedule_from::schedule_from_t;
5033 inline constexpr schedule_from_t schedule_from{};
5034
5035 template <>
5036 struct __sexpr_impl<schedule_from_t> : __schedule_from::__schedule_from_impl
5037 {};
5038
5039 /////////////////////////////////////////////////////////////////////////////
5040 // [execution.senders.adaptors.transfer]
5041 namespace __transfer
5042 {
5043 using __schedule_from::__environ;
5044
5045 template <class _Env>
5046 using __scheduler_t = __result_of<get_completion_scheduler<set_value_t>, _Env>;
5047
5048 template <class _Sender>
5049 using __lowered_t = //
5050 __result_of<schedule_from, __scheduler_t<__data_of<_Sender>>,
5051 __child_of<_Sender>>;
5052
5053 struct transfer_t
5054 {
5055 template <sender _Sender, scheduler _Scheduler>
operator ()stdexec::__transfer::transfer_t5056 auto operator()(_Sender&& __sndr, _Scheduler&& __sched) const
5057 -> __well_formed_sender auto
5058 {
5059 auto __domain = __get_early_domain(__sndr);
5060 using _Env = __t<__environ<__id<__decay_t<_Scheduler>>>>;
5061 return stdexec::transform_sender(
5062 __domain,
5063 __make_sexpr<transfer_t>(_Env{{static_cast<_Scheduler&&>(__sched)}},
5064 static_cast<_Sender&&>(__sndr)));
5065 }
5066
5067 template <scheduler _Scheduler>
5068 STDEXEC_ATTRIBUTE((always_inline))
5069 __binder_back<transfer_t, __decay_t<_Scheduler>>
operator ()stdexec::__transfer::transfer_t5070 operator()(_Scheduler&& __sched) const
5071 {
5072 return {{}, {}, {static_cast<_Scheduler&&>(__sched)}};
5073 }
5074
5075 //////////////////////////////////////////////////////////////////////////////////////////////
5076 using _Env = __0;
5077 using _Sender = __1;
5078 using __legacy_customizations_t = //
5079 __types<tag_invoke_t(transfer_t,
5080 get_completion_scheduler_t<set_value_t>(
5081 get_env_t(const _Sender&)),
5082 _Sender,
5083 get_completion_scheduler_t<set_value_t>(_Env)),
5084 tag_invoke_t(transfer_t, _Sender,
5085 get_completion_scheduler_t<set_value_t>(_Env))>;
5086
5087 template <class _Env>
__transform_sender_fnstdexec::__transfer::transfer_t5088 static auto __transform_sender_fn(const _Env&)
5089 {
5090 return [&]<class _Data, class _Child>(__ignore, _Data&& __data,
5091 _Child&& __child) {
5092 auto __sched = get_completion_scheduler<set_value_t>(__data);
5093 return schedule_from(std::move(__sched),
5094 static_cast<_Child&&>(__child));
5095 };
5096 }
5097
5098 template <class _Sender, class _Env>
transform_senderstdexec::__transfer::transfer_t5099 static auto transform_sender(_Sender&& __sndr, const _Env& __env)
5100 {
5101 return __sexpr_apply(static_cast<_Sender&&>(__sndr),
5102 __transform_sender_fn(__env));
5103 }
5104 };
5105
5106 struct __transfer_impl : __sexpr_defaults
5107 {
5108 static constexpr auto get_attrs = //
5109 []<class _Data, class _Child>(
5110 const _Data& __data,
5111 const _Child& __child) noexcept -> decltype(auto) {
5112 return __env::__join(__data, stdexec::get_env(__child));
5113 };
5114 };
5115 } // namespace __transfer
5116
5117 using __transfer::transfer_t;
5118 inline constexpr transfer_t transfer{};
5119
5120 template <>
5121 struct __sexpr_impl<transfer_t> : __transfer::__transfer_impl
5122 {};
5123
5124 /////////////////////////////////////////////////////////////////////////////
5125 // [execution.senders.transfer_just]
5126 namespace __transfer_just
5127 {
5128 // This is a helper for finding legacy cusutomizations of transfer_just.
__transfer_just_tag_invoke()5129 inline auto __transfer_just_tag_invoke()
5130 {
5131 return []<class... _Ts>(
5132 _Ts&&... __ts) -> tag_invoke_result_t<transfer_just_t, _Ts...> {
5133 return tag_invoke(transfer_just, static_cast<_Ts&&>(__ts)...);
5134 };
5135 }
5136
5137 template <class _Env>
__make_transform_fn(const _Env & __env)5138 auto __make_transform_fn(const _Env& __env)
5139 {
5140 return [&]<class _Scheduler, class... _Values>(_Scheduler&& __sched,
5141 _Values&&... __vals) {
5142 return transfer(just(static_cast<_Values&&>(__vals)...),
5143 static_cast<_Scheduler&&>(__sched));
5144 };
5145 }
5146
5147 template <class _Env>
__transform_sender_fn(const _Env & __env)5148 auto __transform_sender_fn(const _Env& __env)
5149 {
5150 return [&]<class _Data>(__ignore, _Data&& __data) {
5151 return __apply(__make_transform_fn(__env),
5152 static_cast<_Data&&>(__data));
5153 };
5154 }
5155
5156 struct transfer_just_t
5157 {
5158 using _Data = __0;
5159 using __legacy_customizations_t = //
5160 __types<__apply_t(decltype(__transfer_just_tag_invoke()), _Data)>;
5161
5162 template <scheduler _Scheduler, __movable_value... _Values>
operator ()stdexec::__transfer_just::transfer_just_t5163 auto operator()(_Scheduler&& __sched, _Values&&... __vals) const
5164 -> __well_formed_sender auto
5165 {
5166 auto __domain = query_or(get_domain, __sched, default_domain());
5167 return stdexec::transform_sender(
5168 __domain, __make_sexpr<transfer_just_t>(
5169 std::tuple{static_cast<_Scheduler&&>(__sched),
5170 static_cast<_Values&&>(__vals)...}));
5171 }
5172
5173 template <class _Sender, class _Env>
transform_senderstdexec::__transfer_just::transfer_just_t5174 static auto transform_sender(_Sender&& __sndr, const _Env& __env)
5175 {
5176 return __sexpr_apply(static_cast<_Sender&&>(__sndr),
5177 __transform_sender_fn(__env));
5178 }
5179 };
5180
__make_env_fn()5181 inline auto __make_env_fn() noexcept
5182 {
5183 return []<class _Scheduler>(const _Scheduler& __sched,
5184 const auto&...) noexcept {
5185 using _Env = __t<__schedule_from::__environ<__id<_Scheduler>>>;
5186 return _Env{__sched};
5187 };
5188 }
5189
5190 struct __transfer_just_impl : __sexpr_defaults
5191 {
5192 static constexpr auto get_attrs = //
5193 []<class _Data>(const _Data& __data) noexcept {
5194 return __apply(__make_env_fn(), __data);
5195 };
5196 };
5197 } // namespace __transfer_just
5198
5199 using __transfer_just::transfer_just_t;
5200 inline constexpr transfer_just_t transfer_just{};
5201
5202 template <>
5203 struct __sexpr_impl<transfer_just_t> : __transfer_just::__transfer_just_impl
5204 {};
5205
5206 //////////////////////////////////////////////////////////////////////////////////////////////////
5207 // __write adaptor
5208 namespace __write_
5209 {
5210 struct __write_t
5211 {
5212 template <sender _Sender, class... _Envs>
operator ()stdexec::__write_::__write_t5213 auto operator()(_Sender&& __sndr, _Envs... __envs) const
5214 {
5215 return __make_sexpr<__write_t>(__env::__join(std::move(__envs)...),
5216 static_cast<_Sender&&>(__sndr));
5217 }
5218
5219 template <class... _Envs>
5220 STDEXEC_ATTRIBUTE((always_inline))
operator ()stdexec::__write_::__write_t5221 auto operator()(_Envs... __envs) const -> __binder_back<__write_t, _Envs...>
5222 {
5223 return {{}, {}, {std::move(__envs)...}};
5224 }
5225
5226 template <class _Env>
5227 STDEXEC_ATTRIBUTE((always_inline))
__transform_env_fnstdexec::__write_::__write_t5228 static auto __transform_env_fn(_Env&& __env) noexcept
5229 {
5230 return [&](__ignore, const auto& __state, __ignore) noexcept {
5231 return __env::__join(__state, static_cast<_Env&&>(__env));
5232 };
5233 }
5234
5235 template <sender_expr_for<__write_t> _Self, class _Env>
transform_envstdexec::__write_::__write_t5236 static auto transform_env(const _Self& __self, _Env&& __env) noexcept
5237 {
5238 return __sexpr_apply(__self,
5239 __transform_env_fn(static_cast<_Env&&>(__env)));
5240 }
5241 };
5242
5243 struct __write_impl : __sexpr_defaults
5244 {
5245 static constexpr auto get_env = //
__anon996f5b230e02stdexec::__write_::__write_impl5246 [](__ignore, const auto& __state, const auto& __rcvr) noexcept {
5247 return __env::__join(__state, stdexec::get_env(__rcvr));
5248 };
5249
5250 static constexpr auto get_completion_signatures = //
5251 []<class _Self, class _Env>(_Self&&, _Env&&) noexcept
5252 -> stdexec::__completion_signatures_of_t<
5253 __child_of<_Self>,
5254 __env::__join_t<const __decay_t<__data_of<_Self>>&, _Env>> {
5255 static_assert(sender_expr_for<_Self, __write_t>);
5256 return {};
5257 };
5258 };
5259 } // namespace __write_
5260
5261 using __write_::__write_t;
5262 inline constexpr __write_t __write{};
5263
5264 template <>
5265 struct __sexpr_impl<__write_t> : __write_::__write_impl
5266 {};
5267
5268 namespace __detail
5269 {
5270 template <class _Env, class _Scheduler>
5271 STDEXEC_ATTRIBUTE((always_inline))
__mkenv_sched(_Env && __env,_Scheduler __sched)5272 auto __mkenv_sched(_Env&& __env, _Scheduler __sched)
5273 {
5274 auto __env2 =
5275 __env::__join(__env::__with(__sched, get_scheduler),
5276 __env::__without(static_cast<_Env&&>(__env), get_domain));
5277 using _Env2 = decltype(__env2);
5278
5279 struct __env_t : _Env2
5280 {};
5281
5282 return __env_t{static_cast<_Env2&&>(__env2)};
5283 }
5284
5285 template <class _Ty, class = __name_of<__decay_t<_Ty>>>
5286 struct __always
5287 {
5288 _Ty __val_;
5289
operator ()stdexec::__detail::__always5290 auto operator()() noexcept -> _Ty
5291 {
5292 return static_cast<_Ty&&>(__val_);
5293 }
5294 };
5295
5296 template <class _Ty>
5297 __always(_Ty) -> __always<_Ty>;
5298 } // namespace __detail
5299
5300 /////////////////////////////////////////////////////////////////////////////
5301 // [execution.senders.adaptors.on]
5302 namespace __on
5303 {
5304 struct on_t
5305 {
5306 using _Sender = __1;
5307 using _Scheduler = __0;
5308 using __legacy_customizations_t =
5309 __types<tag_invoke_t(on_t, _Scheduler, _Sender)>;
5310
5311 template <scheduler _Scheduler, sender _Sender>
operator ()stdexec::__on::on_t5312 auto operator()(_Scheduler&& __sched, _Sender&& __sndr) const
5313 -> __well_formed_sender auto
5314 {
5315 auto __domain = query_or(get_domain, __sched, default_domain());
5316 return stdexec::transform_sender(
5317 __domain, __make_sexpr<on_t>(static_cast<_Scheduler&&>(__sched),
5318 static_cast<_Sender&&>(__sndr)));
5319 }
5320
5321 template <class _Env>
5322 STDEXEC_ATTRIBUTE((always_inline))
__transform_env_fnstdexec::__on::on_t5323 static auto __transform_env_fn(_Env&& __env) noexcept
5324 {
5325 return [&](__ignore, auto __sched, __ignore) noexcept {
5326 return __detail::__mkenv_sched(static_cast<_Env&&>(__env), __sched);
5327 };
5328 }
5329
5330 template <class _Sender, class _Env>
transform_envstdexec::__on::on_t5331 static auto transform_env(const _Sender& __sndr, _Env&& __env) noexcept
5332 {
5333 return __sexpr_apply(__sndr,
5334 __transform_env_fn(static_cast<_Env&&>(__env)));
5335 }
5336
5337 template <class _Sender, class _Env>
transform_senderstdexec::__on::on_t5338 static auto transform_sender(_Sender&& __sndr, const _Env&)
5339 {
5340 return __sexpr_apply(static_cast<_Sender&&>(__sndr),
5341 []<class _Data, class _Child>(
5342 __ignore, _Data&& __data, _Child&& __child) {
5343 return let_value(
5344 schedule(__data),
5345 __detail::__always{static_cast<_Child&&>(__child)});
5346 });
5347 }
5348 };
5349 } // namespace __on
5350
5351 using __on::on_t;
5352 inline constexpr on_t on{};
5353
5354 /////////////////////////////////////////////////////////////////////////////
5355 // [execution.senders.adaptors.into_variant]
5356 namespace __into_variant
5357 {
5358 template <class _Sender, class _Env>
5359 requires sender_in<_Sender, _Env>
5360 using __into_variant_result_t = value_types_of_t<_Sender, _Env>;
5361
5362 template <class _Sender, class _Env>
5363 using __variant_t = __try_value_types_of_t<_Sender, _Env>;
5364
5365 template <class _Variant>
5366 using __variant_completions =
5367 completion_signatures<set_value_t(_Variant),
5368 set_error_t(std::exception_ptr)>;
5369
5370 template <class _Sender, class _Env>
5371 using __compl_sigs = //
5372 __try_make_completion_signatures<
5373 _Sender, _Env,
5374 __meval<__variant_completions, __variant_t<_Sender, _Env>>,
5375 __mconst<completion_signatures<>>>;
5376
5377 struct into_variant_t
5378 {
5379 template <sender _Sender>
operator ()stdexec::__into_variant::into_variant_t5380 auto operator()(_Sender&& __sndr) const -> __well_formed_sender auto
5381 {
5382 auto __domain = __get_early_domain(__sndr);
5383 return stdexec::transform_sender(
5384 __domain,
5385 __make_sexpr<into_variant_t>(__(), std::forward<_Sender>(__sndr)));
5386 }
5387
5388 STDEXEC_ATTRIBUTE((always_inline))
5389
operator ()stdexec::__into_variant::into_variant_t5390 auto operator()() const noexcept
5391 {
5392 return __binder_back<into_variant_t>{};
5393 }
5394 };
5395
5396 struct __into_variant_impl : __sexpr_defaults
5397 {
5398 static constexpr auto get_state = //
5399 []<class _Self, class _Receiver>(_Self&&, _Receiver&) noexcept {
5400 using __variant_t =
5401 value_types_of_t<__child_of<_Self>, env_of_t<_Receiver>>;
5402 return __mtype<__variant_t>();
5403 };
5404
5405 static constexpr auto complete = //
5406 []<class _State, class _Receiver, class _Tag, class... _Args>(
5407 __ignore, _State, _Receiver& __rcvr, _Tag,
5408 _Args&&... __args) noexcept -> void {
5409 if constexpr (same_as<_Tag, set_value_t>)
5410 {
5411 using __variant_t = __t<_State>;
5412 try
5413 {
5414 set_value(static_cast<_Receiver&&>(__rcvr),
5415 __variant_t{std::tuple<_Args&&...>{
5416 static_cast<_Args&&>(__args)...}});
5417 }
5418 catch (...)
5419 {
5420 set_error(static_cast<_Receiver&&>(__rcvr),
5421 std::current_exception());
5422 }
5423 }
5424 else
5425 {
5426 _Tag()(static_cast<_Receiver&&>(__rcvr),
5427 static_cast<_Args&&>(__args)...);
5428 }
5429 };
5430
5431 static constexpr auto get_completion_signatures = //
5432 []<class _Self, class _Env>(_Self&&, _Env&&) noexcept //
5433 -> __compl_sigs<__child_of<_Self>, _Env> {
5434 static_assert(sender_expr_for<_Self, into_variant_t>);
5435 return {};
5436 };
5437 };
5438 } // namespace __into_variant
5439
5440 using __into_variant::into_variant_t;
5441 inline constexpr into_variant_t into_variant{};
5442
5443 template <>
5444 struct __sexpr_impl<into_variant_t> : __into_variant::__into_variant_impl
5445 {};
5446
5447 /////////////////////////////////////////////////////////////////////////////
5448 // [execution.senders.adaptors.when_all]
5449 // [execution.senders.adaptors.when_all_with_variant]
5450 namespace __when_all
5451 {
5452 enum __state_t
5453 {
5454 __started,
5455 __error,
5456 __stopped
5457 };
5458
5459 struct __on_stop_request
5460 {
5461 in_place_stop_source& __stop_source_;
5462
operator ()stdexec::__when_all::__on_stop_request5463 void operator()() noexcept
5464 {
5465 __stop_source_.request_stop();
5466 }
5467 };
5468
5469 template <class _Env>
__mkenv(_Env && __env,in_place_stop_source & __stop_source)5470 auto __mkenv(_Env&& __env, in_place_stop_source& __stop_source) noexcept
5471 {
5472 return __env::__join(
5473 __env::__with(__stop_source.get_token(), get_stop_token),
5474 static_cast<_Env&&>(__env));
5475 }
5476
5477 template <class _Env>
5478 using __env_t = //
5479 decltype(__mkenv(__declval<_Env>(), __declval<in_place_stop_source&>()));
5480
5481 template <class _Tp>
5482 using __decay_rvalue_ref = __decay_t<_Tp>&&;
5483
5484 template <class _Sender, class _Env>
5485 concept __max1_sender = sender_in<_Sender, _Env> &&
5486 __mvalid<__value_types_of_t, _Sender, _Env,
5487 __mconst<int>, __msingle_or<void>>;
5488
5489 template <
5490 __mstring _Context = "In stdexec::when_all()..."_mstr,
5491 __mstring _Diagnostic =
5492 "The given sender can complete successfully in more that one way. "
5493 "Use stdexec::when_all_with_variant() instead."_mstr>
5494 struct _INVALID_WHEN_ALL_ARGUMENT_;
5495
5496 template <class _Sender, class _Env>
5497 using __too_many_value_completions_error =
5498 __mexception<_INVALID_WHEN_ALL_ARGUMENT_<>, _WITH_SENDER_<_Sender>,
5499 _WITH_ENVIRONMENT_<_Env>>;
5500
5501 template <class _Sender, class _Env, class _ValueTuple, class... _Rest>
5502 using __value_tuple_t =
5503 __minvoke<__if_c<(0 == sizeof...(_Rest)), __mconst<_ValueTuple>,
5504 __q<__too_many_value_completions_error>>,
5505 _Sender, _Env>;
5506
5507 template <class _Env, class _Sender>
5508 using __single_values_of_t = //
5509 __try_value_types_of_t<_Sender, _Env,
5510 __transform<__q<__decay_rvalue_ref>, __q<__types>>,
5511 __mbind_front_q<__value_tuple_t, _Sender, _Env>>;
5512
5513 template <class _Env, class... _Senders>
5514 using __set_values_sig_t = //
5515 __meval<completion_signatures,
5516 __minvoke<__mconcat<__qf<set_value_t>>,
5517 __single_values_of_t<_Env, _Senders>...>>;
5518
5519 template <class... _Args>
5520 using __all_nothrow_decay_copyable_ =
5521 __mbool<(__nothrow_decay_copyable<_Args> && ...)>;
5522
5523 template <class _Env, class... _Senders>
5524 using __all_nothrow_decay_copyable = //
5525 __mand<__compl_sigs::__maybe_for_all_sigs<
5526 __completion_signatures_of_t<_Senders, _Env>,
5527 __q<__all_nothrow_decay_copyable_>, __q<__mand>>...>;
5528
5529 template <class _Env, class... _Senders>
5530 using __completions_t = //
5531 __concat_completion_signatures_t<
5532 __if<__all_nothrow_decay_copyable<_Env, _Senders...>,
5533 completion_signatures<set_stopped_t()>,
5534 completion_signatures<set_stopped_t(),
5535 set_error_t(std::exception_ptr&&)>>,
5536 __minvoke<__with_default<__mbind_front_q<__set_values_sig_t, _Env>,
5537 completion_signatures<>>,
5538 _Senders...>,
5539 __try_make_completion_signatures<
5540 _Senders, _Env, completion_signatures<>,
5541 __mconst<completion_signatures<>>,
5542 __mcompose<__q<completion_signatures>, __qf<set_error_t>,
5543 __q<__decay_rvalue_ref>>>...>;
5544
5545 struct __not_an_error
5546 {};
5547
5548 struct __tie_fn
5549 {
5550 template <class... _Ty>
operator ()stdexec::__when_all::__tie_fn5551 auto operator()(_Ty&... __vals) noexcept -> std::tuple<_Ty&...>
5552 {
5553 return std::tuple<_Ty&...>{__vals...};
5554 }
5555 };
5556
5557 template <class _Tag, class _Receiver>
__complete_fn(_Tag,_Receiver & __rcvr)5558 auto __complete_fn(_Tag, _Receiver& __rcvr) noexcept
5559 {
5560 return [&]<class... _Ts>(_Ts&... __ts) noexcept {
5561 if constexpr (!same_as<__types<_Ts...>, __types<__not_an_error>>)
5562 {
5563 _Tag()(static_cast<_Receiver&&>(__rcvr),
5564 static_cast<_Ts&&>(__ts)...);
5565 }
5566 };
5567 }
5568
5569 template <class _Receiver, class _ValuesTuple>
__set_values(_Receiver & __rcvr,_ValuesTuple & __values)5570 void __set_values(_Receiver& __rcvr, _ValuesTuple& __values) noexcept
5571 {
5572 __tup::__apply(
5573 [&](auto&... __opt_vals) noexcept -> void {
5574 __apply(__complete_fn(set_value, __rcvr), //
5575 std::tuple_cat(__tup::__apply(__tie_fn{}, *__opt_vals)...));
5576 },
5577 __values);
5578 }
5579
5580 template <class... Ts>
5581 using __decayed_custom_tuple = __tup::__tuple_for<__decay_t<Ts>...>;
5582
5583 template <class _Env, class _Sender>
5584 using __values_opt_tuple_t = //
5585 value_types_of_t<_Sender, __env_t<_Env>, __decayed_custom_tuple,
5586 std::optional>;
5587
5588 template <class _Env, __max1_sender<__env_t<_Env>>... _Senders>
5589 struct __traits
5590 {
5591 // tuple<optional<tuple<Vs1...>>, optional<tuple<Vs2...>>, ...>
5592 using __values_tuple = //
5593 __minvoke<__with_default<
5594 __transform<__mbind_front_q<__values_opt_tuple_t, _Env>,
5595 __q<__tup::__tuple_for>>,
5596 __ignore>,
5597 _Senders...>;
5598
5599 using __nullable_variant_t_ =
5600 __munique<__mbind_front_q<std::variant, __not_an_error>>;
5601
5602 using __error_types = //
5603 __minvoke<__mconcat<__transform<__q<__decay_t>, __nullable_variant_t_>>,
5604 error_types_of_t<_Senders, __env_t<_Env>, __types>...>;
5605
5606 using __errors_variant = //
5607 __if<__all_nothrow_decay_copyable<_Env, _Senders...>, __error_types,
5608 __minvoke<__push_back_unique<__q<std::variant>>, __error_types,
5609 std::exception_ptr>>;
5610 };
5611
5612 struct _INVALID_ARGUMENTS_TO_WHEN_ALL_
5613 {};
5614
5615 template <class _ErrorsVariant, class _ValuesTuple, class _StopToken>
5616 struct __when_all_state
5617 {
5618 using __stop_callback_t =
5619 typename _StopToken::template callback_type<__on_stop_request>;
5620
5621 template <class _Receiver>
__arrivestdexec::__when_all::__when_all_state5622 void __arrive(_Receiver& __rcvr) noexcept
5623 {
5624 if (0 == --__count_)
5625 {
5626 __complete(__rcvr);
5627 }
5628 }
5629
5630 template <class _Receiver>
__completestdexec::__when_all::__when_all_state5631 void __complete(_Receiver& __rcvr) noexcept
5632 {
5633 // Stop callback is no longer needed. Destroy it.
5634 __on_stop_.reset();
5635 // All child operations have completed and arrived at the barrier.
5636 switch (__state_.load(std::memory_order_relaxed))
5637 {
5638 case __started:
5639 if constexpr (!same_as<_ValuesTuple, __ignore>)
5640 {
5641 // All child operations completed successfully:
5642 __when_all::__set_values(__rcvr, __values_);
5643 }
5644 break;
5645 case __error:
5646 if constexpr (!same_as<_ErrorsVariant,
5647 std::variant<std::monostate>>)
5648 {
5649 // One or more child operations completed with an error:
5650 std::visit(__complete_fn(set_error, __rcvr), __errors_);
5651 }
5652 break;
5653 case __stopped:
5654 stdexec::set_stopped(static_cast<_Receiver&&>(__rcvr));
5655 break;
5656 default:;
5657 }
5658 }
5659
5660 std::atomic<std::size_t> __count_;
5661 in_place_stop_source __stop_source_{};
5662 // Could be non-atomic here and atomic_ref everywhere except __completion_fn
5663 std::atomic<__state_t> __state_{__started};
5664 _ErrorsVariant __errors_{};
5665 STDEXEC_ATTRIBUTE((no_unique_address))
5666 _ValuesTuple __values_{};
5667 std::optional<__stop_callback_t> __on_stop_{};
5668 };
5669
5670 template <class _Env>
__mk_state_fn(const _Env & __env)5671 static auto __mk_state_fn(const _Env& __env) noexcept
5672 {
5673 return [&]<__max1_sender<__env_t<_Env>>... _Child>(__ignore, __ignore,
5674 _Child&&...) {
5675 using _Traits = __traits<_Env, _Child...>;
5676 using _ErrorsVariant = typename _Traits::__errors_variant;
5677 using _ValuesTuple = typename _Traits::__values_tuple;
5678 using _State = __when_all_state<_ErrorsVariant, _ValuesTuple,
5679 stop_token_of_t<_Env>>;
5680 return _State{sizeof...(_Child), in_place_stop_source{}, __started,
5681 _ErrorsVariant{}, _ValuesTuple{}, std::nullopt};
5682 };
5683 }
5684
5685 template <class _Env>
5686 using __mk_state_fn_t = decltype(__when_all::__mk_state_fn(__declval<_Env>()));
5687
5688 struct when_all_t
5689 {
5690 // Used by the default_domain to find legacy customizations:
5691 using _Sender = __1;
5692 using __legacy_customizations_t = //
5693 __types<tag_invoke_t(when_all_t, _Sender...)>;
5694
5695 template <sender... _Senders>
5696 requires __domain::__has_common_domain<_Senders...>
operator ()stdexec::__when_all::when_all_t5697 auto operator()(_Senders&&... __sndrs) const -> __well_formed_sender auto
5698 {
5699 auto __domain = __domain::__common_domain_t<_Senders...>();
5700 return stdexec::transform_sender(
5701 __domain, __make_sexpr<when_all_t>(
5702 __(), static_cast<_Senders&&>(__sndrs)...));
5703 }
5704 };
5705
5706 struct __when_all_impl : __sexpr_defaults
5707 {
5708 template <class _Self, class _Env>
5709 using __error_t = __mexception<_INVALID_ARGUMENTS_TO_WHEN_ALL_,
5710 __children_of<_Self, __q<_WITH_SENDERS_>>,
5711 _WITH_ENVIRONMENT_<_Env>>;
5712
5713 template <class _Self, class _Env>
5714 using __completions = //
5715 __children_of<_Self, __mbind_front_q<__completions_t, __env_t<_Env>>>;
5716
5717 static constexpr auto get_attrs = //
5718 []<class... _Child>(__ignore, const _Child&...) noexcept {
5719 using _Domain = __domain::__common_domain_t<_Child...>;
5720 if constexpr (same_as<_Domain, default_domain>)
5721 {
5722 return empty_env();
5723 }
5724 else
5725 {
5726 return __env::__with(_Domain(), get_domain);
5727 }
5728 };
5729
5730 static constexpr auto get_completion_signatures = //
5731 []<class _Self, class _Env>(_Self&&, _Env&&) noexcept {
5732 static_assert(sender_expr_for<_Self, when_all_t>);
5733 return __minvoke<__mtry_catch<__q<__completions>, __q<__error_t>>,
5734 _Self, _Env>();
5735 };
5736
5737 static constexpr auto get_env = //
5738 []<class _State, class _Receiver>(__ignore, _State& __state,
5739 const _Receiver& __rcvr) noexcept //
5740 -> __env_t<env_of_t<const _Receiver&>> {
5741 return __mkenv(stdexec::get_env(__rcvr), __state.__stop_source_);
5742 };
5743
5744 static constexpr auto get_state = //
5745 []<class _Self, class _Receiver>(_Self&& __self, _Receiver& __rcvr)
5746 -> __sexpr_apply_result_t<_Self, __mk_state_fn_t<env_of_t<_Receiver>>> {
5747 return __sexpr_apply(
5748 static_cast<_Self&&>(__self),
5749 __when_all::__mk_state_fn(stdexec::get_env(__rcvr)));
5750 };
5751
5752 static constexpr auto start = //
5753 []<class _State, class _Receiver, class... _Operations>(
5754 _State& __state, _Receiver& __rcvr,
5755 _Operations&... __child_ops) noexcept -> void {
5756 // register stop callback:
5757 __state.__on_stop_.emplace(get_stop_token(stdexec::get_env(__rcvr)),
5758 __on_stop_request{__state.__stop_source_});
5759 if (__state.__stop_source_.stop_requested())
5760 {
5761 // Stop has already been requested. Don't bother starting
5762 // the child operations.
5763 stdexec::set_stopped(std::move(__rcvr));
5764 }
5765 else
5766 {
5767 (stdexec::start(__child_ops), ...);
5768 if constexpr (sizeof...(__child_ops) == 0)
5769 {
5770 __state.__complete(__rcvr);
5771 }
5772 }
5773 };
5774
5775 template <class _State, class _Receiver, class _Error>
__set_errorstdexec::__when_all::__when_all_impl5776 static void __set_error(_State& __state, _Receiver& __rcvr,
5777 _Error&& __err) noexcept
5778 {
5779 // TODO: What memory orderings are actually needed here?
5780 if (__error != __state.__state_.exchange(__error))
5781 {
5782 __state.__stop_source_.request_stop();
5783 // We won the race, free to write the error into the operation
5784 // state without worry.
5785 if constexpr (__nothrow_decay_copyable<_Error>)
5786 {
5787 __state.__errors_.template emplace<__decay_t<_Error>>(
5788 static_cast<_Error&&>(__err));
5789 }
5790 else
5791 {
5792 try
5793 {
5794 __state.__errors_.template emplace<__decay_t<_Error>>(
5795 static_cast<_Error&&>(__err));
5796 }
5797 catch (...)
5798 {
5799 __state.__errors_.template emplace<std::exception_ptr>(
5800 std::current_exception());
5801 }
5802 }
5803 }
5804 }
5805
5806 static constexpr auto complete = //
5807 []<class _Index, class _State, class _Receiver, class _Set,
5808 class... _Args>(_Index, _State& __state, _Receiver& __rcvr, _Set,
5809 _Args&&... __args) noexcept -> void {
5810 if constexpr (same_as<_Set, set_error_t>)
5811 {
5812 __set_error(__state, __rcvr, static_cast<_Args&&>(__args)...);
5813 }
5814 else if constexpr (same_as<_Set, set_stopped_t>)
5815 {
5816 __state_t __expected = __started;
5817 // Transition to the "stopped" state if and only if we're in the
5818 // "started" state. (If this fails, it's because we're in an
5819 // error state, which trumps cancellation.)
5820 if (__state.__state_.compare_exchange_strong(__expected, __stopped))
5821 {
5822 __state.__stop_source_.request_stop();
5823 }
5824 }
5825 else if constexpr (!same_as<decltype(_State::__values_), __ignore>)
5826 {
5827 // We only need to bother recording the completion values
5828 // if we're not already in the "error" or "stopped" state.
5829 if (__state.__state_ == __started)
5830 {
5831 auto& __opt_values =
5832 __tup::__get<__v<_Index>>(__state.__values_);
5833 using _Tuple = __decayed_custom_tuple<_Args...>;
5834 static_assert(
5835 same_as<decltype(*__opt_values), _Tuple&>,
5836 "One of the senders in this when_all() is fibbing about what types it sends");
5837 if constexpr ((__nothrow_decay_copyable<_Args> && ...))
5838 {
5839 __opt_values.emplace(
5840 _Tuple{{static_cast<_Args&&>(__args)}...});
5841 }
5842 else
5843 {
5844 try
5845 {
5846 __opt_values.emplace(
5847 _Tuple{{static_cast<_Args&&>(__args)}...});
5848 }
5849 catch (...)
5850 {
5851 __set_error(__state, __rcvr, std::current_exception());
5852 }
5853 }
5854 }
5855 }
5856
5857 __state.__arrive(__rcvr);
5858 };
5859 };
5860
5861 struct when_all_with_variant_t
5862 {
5863 using _Sender = __1;
5864 using __legacy_customizations_t = //
5865 __types<tag_invoke_t(when_all_with_variant_t, _Sender...)>;
5866
5867 template <sender... _Senders>
5868 requires __domain::__has_common_domain<_Senders...>
operator ()stdexec::__when_all::when_all_with_variant_t5869 auto operator()(_Senders&&... __sndrs) const -> __well_formed_sender auto
5870 {
5871 auto __domain = __domain::__common_domain_t<_Senders...>();
5872 return stdexec::transform_sender(
5873 __domain, __make_sexpr<when_all_with_variant_t>(
5874 __(), static_cast<_Senders&&>(__sndrs)...));
5875 }
5876
5877 template <class _Sender, class _Env>
transform_senderstdexec::__when_all::when_all_with_variant_t5878 static auto transform_sender(_Sender&& __sndr, const _Env& __env)
5879 {
5880 // transform the when_all_with_variant into a regular when_all (looking
5881 // for early when_all customizations), then transform it again to look
5882 // for late customizations.
5883 return __sexpr_apply(
5884 static_cast<_Sender&&>(__sndr),
5885 [&]<class... _Child>(__ignore, __ignore, _Child&&... __child) {
5886 return when_all_t()(
5887 into_variant(static_cast<_Child&&>(__child))...);
5888 });
5889 }
5890 };
5891
5892 struct __when_all_with_variant_impl : __sexpr_defaults
5893 {
5894 static constexpr auto get_attrs = //
5895 []<class... _Child>(__ignore, const _Child&...) noexcept {
5896 using _Domain = __domain::__common_domain_t<_Child...>;
5897 if constexpr (same_as<_Domain, default_domain>)
5898 {
5899 return empty_env();
5900 }
5901 else
5902 {
5903 return __env::__with(_Domain(), get_domain);
5904 }
5905 };
5906 };
5907
5908 struct transfer_when_all_t
5909 {
5910 using _Env = __0;
5911 using _Sender = __1;
5912 using __legacy_customizations_t = //
5913 __types<tag_invoke_t(
5914 transfer_when_all_t,
5915 get_completion_scheduler_t<set_value_t>(const _Env&), _Sender...)>;
5916
5917 template <scheduler _Scheduler, sender... _Senders>
5918 requires __domain::__has_common_domain<_Senders...>
operator ()stdexec::__when_all::transfer_when_all_t5919 auto operator()(_Scheduler&& __sched, _Senders&&... __sndrs) const
5920 -> __well_formed_sender auto
5921 {
5922 using _Env =
5923 __t<__schedule_from::__environ<__id<__decay_t<_Scheduler>>>>;
5924 auto __domain = query_or(get_domain, __sched, default_domain());
5925 return stdexec::transform_sender(
5926 __domain, __make_sexpr<transfer_when_all_t>(
5927 _Env{static_cast<_Scheduler&&>(__sched)},
5928 static_cast<_Senders&&>(__sndrs)...));
5929 }
5930
5931 template <class _Sender, class _Env>
transform_senderstdexec::__when_all::transfer_when_all_t5932 static auto transform_sender(_Sender&& __sndr, const _Env& __env)
5933 {
5934 // transform the transfer_when_all into a regular transform | when_all
5935 // (looking for early customizations), then transform it again to look
5936 // for late customizations.
5937 return __sexpr_apply(
5938 static_cast<_Sender&&>(__sndr),
5939 [&]<class _Data, class... _Child>(__ignore, _Data&& __data,
5940 _Child&&... __child) {
5941 return transfer(when_all_t()(static_cast<_Child&&>(__child)...),
5942 get_completion_scheduler<set_value_t>(__data));
5943 });
5944 }
5945 };
5946
5947 struct __transfer_when_all_impl : __sexpr_defaults
5948 {
5949 static constexpr auto get_attrs = //
5950 []<class _Data>(const _Data& __data,
5951 const auto&...) noexcept -> const _Data& {
5952 return __data;
5953 };
5954 };
5955
5956 struct transfer_when_all_with_variant_t
5957 {
5958 using _Env = __0;
5959 using _Sender = __1;
5960 using __legacy_customizations_t = //
5961 __types<tag_invoke_t(
5962 transfer_when_all_with_variant_t,
5963 get_completion_scheduler_t<set_value_t>(const _Env&), _Sender...)>;
5964
5965 template <scheduler _Scheduler, sender... _Senders>
5966 requires __domain::__has_common_domain<_Senders...>
operator ()stdexec::__when_all::transfer_when_all_with_variant_t5967 auto operator()(_Scheduler&& __sched, _Senders&&... __sndrs) const
5968 -> __well_formed_sender auto
5969 {
5970 using _Env =
5971 __t<__schedule_from::__environ<__id<__decay_t<_Scheduler>>>>;
5972 auto __domain = query_or(get_domain, __sched, default_domain());
5973 return stdexec::transform_sender(
5974 __domain, __make_sexpr<transfer_when_all_with_variant_t>(
5975 _Env{{static_cast<_Scheduler&&>(__sched)}},
5976 static_cast<_Senders&&>(__sndrs)...));
5977 }
5978
5979 template <class _Sender, class _Env>
transform_senderstdexec::__when_all::transfer_when_all_with_variant_t5980 static auto transform_sender(_Sender&& __sndr, const _Env& __env)
5981 {
5982 // transform the transfer_when_all_with_variant into regular
5983 // transform_when_all and into_variant calls/ (looking for early
5984 // customizations), then transform it again to look for late
5985 // customizations.
5986 return __sexpr_apply(
5987 static_cast<_Sender&&>(__sndr),
5988 [&]<class _Data, class... _Child>(__ignore, _Data&& __data,
5989 _Child&&... __child) {
5990 return transfer_when_all_t()(
5991 get_completion_scheduler<set_value_t>(
5992 static_cast<_Data&&>(__data)),
5993 into_variant(static_cast<_Child&&>(__child))...);
5994 });
5995 }
5996 };
5997
5998 struct __transfer_when_all_with_variant_impl : __sexpr_defaults
5999 {
6000 static constexpr auto get_attrs = //
6001 []<class _Data>(const _Data& __data,
6002 const auto&...) noexcept -> const _Data& {
6003 return __data;
6004 };
6005 };
6006 } // namespace __when_all
6007
6008 using __when_all::when_all_t;
6009 inline constexpr when_all_t when_all{};
6010
6011 using __when_all::when_all_with_variant_t;
6012 inline constexpr when_all_with_variant_t when_all_with_variant{};
6013
6014 using __when_all::transfer_when_all_t;
6015 inline constexpr transfer_when_all_t transfer_when_all{};
6016
6017 using __when_all::transfer_when_all_with_variant_t;
6018 inline constexpr transfer_when_all_with_variant_t
6019 transfer_when_all_with_variant{};
6020
6021 template <>
6022 struct __sexpr_impl<when_all_t> : __when_all::__when_all_impl
6023 {};
6024
6025 template <>
6026 struct __sexpr_impl<when_all_with_variant_t> :
6027 __when_all::__when_all_with_variant_impl
6028 {};
6029
6030 template <>
6031 struct __sexpr_impl<transfer_when_all_t> : __when_all::__transfer_when_all_impl
6032 {};
6033
6034 template <>
6035 struct __sexpr_impl<transfer_when_all_with_variant_t> :
6036 __when_all::__transfer_when_all_with_variant_impl
6037 {};
6038
6039 namespace __read
6040 {
6041 template <class _Tag, class _ReceiverId>
6042 using __result_t = __call_result_t<_Tag, env_of_t<stdexec::__t<_ReceiverId>>>;
6043
6044 template <class _Tag, class _ReceiverId>
6045 concept __nothrow_t =
6046 __nothrow_callable<_Tag, env_of_t<stdexec::__t<_ReceiverId>>>;
6047
6048 inline constexpr __mstring __query_failed_diag =
6049 "The current execution environment doesn't have a value for the given query."_mstr;
6050
6051 template <class _Tag>
6052 struct _WITH_QUERY_;
6053
6054 template <class _Tag, class _Env>
6055 using __query_failed_error = //
6056 __mexception< //
6057 _NOT_CALLABLE_<"In stdexec::read()..."_mstr, __query_failed_diag>,
6058 _WITH_QUERY_<_Tag>, _WITH_ENVIRONMENT_<_Env>>;
6059
6060 template <class _Tag, class _Env>
6061 requires __callable<_Tag, _Env>
6062 using __completions_t = //
6063 __if_c<__nothrow_callable<_Tag, _Env>,
6064 completion_signatures<set_value_t(__call_result_t<_Tag, _Env>)>,
6065 completion_signatures<set_value_t(__call_result_t<_Tag, _Env>),
6066 set_error_t(std::exception_ptr)>>;
6067
6068 template <class _Tag, class _Ty>
6069 struct __state
6070 {
6071 using __query = _Tag;
6072 using __result = _Ty;
6073 std::optional<_Ty> __result_;
6074 };
6075
6076 template <class _Tag, class _Ty>
6077 requires same_as<_Ty, _Ty&&>
6078 struct __state<_Tag, _Ty>
6079 {
6080 using __query = _Tag;
6081 using __result = _Ty;
6082 };
6083
6084 struct __read_t
6085 {
6086 template <class _Tag>
operator ()stdexec::__read::__read_t6087 constexpr auto operator()(_Tag) const noexcept
6088 {
6089 return __make_sexpr<__read_t>(_Tag());
6090 }
6091 };
6092
6093 struct __read_impl : __sexpr_defaults
6094 {
6095 using is_dependent = void;
6096
6097 template <class _Tag, class _Env>
6098 using __completions_t = __minvoke<
6099 __mtry_catch_q<__read::__completions_t, __q<__query_failed_error>>,
6100 _Tag, _Env>;
6101
6102 static constexpr auto get_completion_signatures = //
6103 []<class _Self, class _Env>(const _Self&, _Env&&) noexcept //
6104 -> __completions_t<__data_of<_Self>, _Env> {
6105 static_assert(sender_expr_for<_Self, __read_t>);
6106 return {};
6107 };
6108
6109 static constexpr auto get_state = //
6110 []<class _Self, class _Receiver>(const _Self&,
6111 _Receiver& __rcvr) noexcept {
6112 using __query = __data_of<_Self>;
6113 using __result = __call_result_t<__query, env_of_t<_Receiver>>;
6114 return __state<__query, __result>();
6115 };
6116
6117 static constexpr auto start = //
6118 []<class _State, class _Receiver>(_State& __state,
6119 _Receiver& __rcvr) noexcept -> void {
6120 using __query = typename _State::__query;
6121 using __result = typename _State::__result;
6122 if constexpr (same_as<__result, __result&&>)
6123 {
6124 // The query returns a reference type; pass it straight through to
6125 // the receiver.
6126 stdexec::__set_value_invoke(std::move(__rcvr), __query(),
6127 stdexec::get_env(__rcvr));
6128 }
6129 else
6130 {
6131 constexpr bool _Nothrow =
6132 __nothrow_callable<__query, env_of_t<_Receiver>>;
__anon996f5b231102stdexec::__read::__read_impl6133 auto __query_fn = [&]() noexcept(_Nothrow) -> __result&& {
6134 __state.__result_.emplace(__conv{[&]() noexcept(_Nothrow) {
6135 return __query()(stdexec::get_env(__rcvr));
6136 }});
6137 return std::move(*__state.__result_);
6138 };
6139 stdexec::__set_value_invoke(std::move(__rcvr), __query_fn);
6140 }
6141 };
6142 };
6143 } // namespace __read
6144
6145 inline constexpr __read::__read_t read{};
6146
6147 template <>
6148 struct __sexpr_impl<__read::__read_t> : __read::__read_impl
6149 {};
6150
6151 namespace __queries
6152 {
6153 template <class _Tag>
operator ()() const6154 inline auto get_scheduler_t::operator()() const noexcept
6155 {
6156 return read(get_scheduler);
6157 }
6158
6159 template <class _Env>
6160 requires tag_invocable<get_scheduler_t, const _Env&>
operator ()(const _Env & __env) const6161 inline auto get_scheduler_t::operator()(const _Env& __env) const noexcept
6162 -> tag_invoke_result_t<get_scheduler_t, const _Env&>
6163 {
6164 static_assert(nothrow_tag_invocable<get_scheduler_t, const _Env&>);
6165 static_assert(scheduler<tag_invoke_result_t<get_scheduler_t, const _Env&>>);
6166 return tag_invoke(get_scheduler_t{}, __env);
6167 }
6168
6169 template <class _Tag>
operator ()() const6170 inline auto get_delegatee_scheduler_t::operator()() const noexcept
6171 {
6172 return read(get_delegatee_scheduler);
6173 }
6174
6175 template <class _Env>
6176 requires tag_invocable<get_delegatee_scheduler_t, const _Env&>
6177 inline auto
operator ()(const _Env & __t) const6178 get_delegatee_scheduler_t::operator()(const _Env& __t) const noexcept
6179 -> tag_invoke_result_t<get_delegatee_scheduler_t, const _Env&>
6180 {
6181 static_assert(
6182 nothrow_tag_invocable<get_delegatee_scheduler_t, const _Env&>);
6183 static_assert(
6184 scheduler<tag_invoke_result_t<get_delegatee_scheduler_t, const _Env&>>);
6185 return tag_invoke(get_delegatee_scheduler_t{}, std::as_const(__t));
6186 }
6187
6188 template <class _Tag>
operator ()() const6189 inline auto get_allocator_t::operator()() const noexcept
6190 {
6191 return read(get_allocator);
6192 }
6193
6194 template <class _Tag>
operator ()() const6195 inline auto get_stop_token_t::operator()() const noexcept
6196 {
6197 return read(get_stop_token);
6198 }
6199
6200 template <__completion_tag _CPO>
6201 template <__has_completion_scheduler_for<_CPO> _Queryable>
operator ()(const _Queryable & __queryable) const6202 auto get_completion_scheduler_t<_CPO>::operator()(
6203 const _Queryable& __queryable) const noexcept
6204 -> tag_invoke_result_t<get_completion_scheduler_t<_CPO>, const _Queryable&>
6205 {
6206 static_assert(nothrow_tag_invocable<get_completion_scheduler_t<_CPO>,
6207 const _Queryable&>,
6208 "get_completion_scheduler<_CPO> should be noexcept");
6209 static_assert(
6210 scheduler<tag_invoke_result_t<get_completion_scheduler_t<_CPO>,
6211 const _Queryable&>>);
6212 return tag_invoke(*this, __queryable);
6213 }
6214 } // namespace __queries
6215
6216 /////////////////////////////////////////////////////////////////////////////
6217 // [execution.senders.adaptors.on]
6218 namespace __on_v2
6219 {
6220 inline constexpr __mstring __on_context =
6221 "In stdexec::on(Scheduler, Sender)..."_mstr;
6222 inline constexpr __mstring __no_scheduler_diag =
6223 "stdexec::on() requires a scheduler to transition back to."_mstr;
6224 inline constexpr __mstring __no_scheduler_details =
6225 "The provided environment lacks a value for the get_scheduler() query."_mstr;
6226
6227 template <__mstring _Context = __on_context,
6228 __mstring _Diagnostic = __no_scheduler_diag,
6229 __mstring _Details = __no_scheduler_details>
6230 struct _CANNOT_RESTORE_EXECUTION_CONTEXT_AFTER_ON_
6231 {};
6232
6233 struct on_t;
6234
6235 STDEXEC_PRAGMA_PUSH()
6236 STDEXEC_PRAGMA_IGNORE_GNU("-Wunused-local-typedefs")
6237
6238 struct __no_scheduler_in_environment
6239 {
6240 // Issue a custom diagnostic if the environment doesn't provide a scheduler.
6241 template <class _Sender, class _Env>
transform_senderstdexec::__on_v2::__no_scheduler_in_environment6242 static auto transform_sender(_Sender&&, const _Env&)
6243 {
6244 struct __no_scheduler_in_environment
6245 {
6246 using sender_concept = sender_t;
6247 using completion_signatures = //
6248 __mexception<_CANNOT_RESTORE_EXECUTION_CONTEXT_AFTER_ON_<>,
6249 _WITH_SENDER_<_Sender>, _WITH_ENVIRONMENT_<_Env>>;
6250 };
6251
6252 return __no_scheduler_in_environment{};
6253 }
6254 };
6255
6256 STDEXEC_PRAGMA_POP()
6257
6258 struct on_t : __no_scheduler_in_environment
6259 {
6260 template <scheduler _Scheduler, sender _Sender>
operator ()stdexec::__on_v2::on_t6261 auto operator()(_Scheduler&& __sched, _Sender&& __sndr) const
6262 -> __well_formed_sender auto
6263 {
6264 // BUGBUG __get_early_domain, or get_domain(__sched), or ...?
6265 auto __domain = __get_early_domain(__sndr);
6266 return stdexec::transform_sender(
6267 __domain, __make_sexpr<on_t>(static_cast<_Scheduler&&>(__sched),
6268 static_cast<_Sender&&>(__sndr)));
6269 }
6270
6271 template <class _Env>
6272 STDEXEC_ATTRIBUTE((always_inline))
__transform_env_fnstdexec::__on_v2::on_t6273 static auto __transform_env_fn(_Env&& __env) noexcept
6274 {
6275 return [&](__ignore, auto __sched, __ignore) noexcept {
6276 return __detail::__mkenv_sched(static_cast<_Env&&>(__env), __sched);
6277 };
6278 }
6279
6280 template <class _Sender, class _Env>
transform_envstdexec::__on_v2::on_t6281 static auto transform_env(const _Sender& __sndr, _Env&& __env) noexcept
6282 {
6283 return __sexpr_apply(__sndr,
6284 __transform_env_fn(static_cast<_Env&&>(__env)));
6285 }
6286
6287 using __no_scheduler_in_environment::transform_sender;
6288
6289 template <class _Sender, class _Env>
6290 requires __callable<get_scheduler_t, const _Env&>
transform_senderstdexec::__on_v2::on_t6291 static auto transform_sender(_Sender&& __sndr, const _Env& __env)
6292 {
6293 return __sexpr_apply(
6294 static_cast<_Sender&&>(__sndr),
6295 [&]<class _Scheduler, class _Child>(__ignore, _Scheduler __sched,
6296 _Child&& __child) {
6297 auto __old = get_scheduler(__env);
6298 return transfer(
6299 let_value(transfer_just(std::move(__sched)),
6300 __detail::__always{static_cast<_Child&&>(__child)}),
6301 std::move(__old));
6302 });
6303 }
6304 };
6305
6306 template <class _Scheduler, class _Closure>
6307 struct __continue_on_data
6308 {
6309 _Scheduler __sched_;
6310 _Closure __clsur_;
6311 };
6312 template <class _Scheduler, class _Closure>
6313 __continue_on_data(_Scheduler, _Closure)
6314 -> __continue_on_data<_Scheduler, _Closure>;
6315
6316 template <class _Scheduler>
6317 struct __with_sched
6318 {
6319 _Scheduler __sched_;
6320
tag_invoke(get_scheduler_t,const __with_sched & __self)6321 friend auto tag_invoke(get_scheduler_t, const __with_sched& __self) noexcept
6322 -> _Scheduler
6323 {
6324 return __self.__sched_;
6325 }
6326
tag_invoke(get_domain_t,const __with_sched & __self)6327 friend auto tag_invoke(get_domain_t, const __with_sched& __self) noexcept
6328 {
6329 return query_or(get_domain, __self.__sched_, default_domain());
6330 }
6331 };
6332
6333 template <class _Scheduler>
6334 __with_sched(_Scheduler) -> __with_sched<_Scheduler>;
6335
6336 struct continue_on_t : __no_scheduler_in_environment
6337 {
6338 template <sender _Sender, scheduler _Scheduler,
6339 __sender_adaptor_closure_for<_Sender> _Closure>
operator ()stdexec::__on_v2::continue_on_t6340 auto operator()(_Sender&& __sndr, _Scheduler&& __sched,
6341 _Closure&& __clsur) const -> __well_formed_sender auto
6342 {
6343 auto __domain = __get_early_domain(__sndr);
6344 return stdexec::transform_sender(
6345 __domain, __make_sexpr<continue_on_t>(
6346 __continue_on_data{static_cast<_Scheduler&&>(__sched),
6347 static_cast<_Closure&&>(__clsur)},
6348 static_cast<_Sender&&>(__sndr)));
6349 }
6350
6351 template <scheduler _Scheduler, __sender_adaptor_closure _Closure>
6352 STDEXEC_ATTRIBUTE((always_inline))
operator ()stdexec::__on_v2::continue_on_t6353 auto operator()(_Scheduler&& __sched, _Closure&& __clsur) const
6354 -> __binder_back<continue_on_t, __decay_t<_Scheduler>,
6355 __decay_t<_Closure>>
6356 {
6357 return {{},
6358 {},
6359 {static_cast<_Scheduler&&>(__sched),
6360 static_cast<_Closure&&>(__clsur)}};
6361 }
6362
6363 using __no_scheduler_in_environment::transform_sender;
6364
6365 template <class _Sender, class _Env>
6366 requires __callable<get_scheduler_t, const _Env&>
transform_senderstdexec::__on_v2::continue_on_t6367 static auto transform_sender(_Sender&& __sndr, const _Env& __env)
6368 {
6369 auto __old = get_scheduler(__env);
6370 return __sexpr_apply(static_cast<_Sender&&>(__sndr),
6371 [&]<class _Data, class _Child>(
6372 __ignore, _Data&& __data, _Child&& __child) {
6373 auto&& [__sched, __clsur] = static_cast<_Data&&>(__data);
6374 using _Closure = decltype(__clsur);
6375 return __write(transfer(static_cast<_Closure&&>(__clsur)(transfer(
6376 __write(static_cast<_Child&&>(__child),
6377 __with_sched{__old}),
6378 __sched)),
6379 __old),
6380 __with_sched{__sched});
6381 });
6382 }
6383 };
6384 } // namespace __on_v2
6385
6386 namespace v2
6387 {
6388 using __on_v2::on_t;
6389 inline constexpr on_t on{};
6390
6391 using __on_v2::continue_on_t;
6392 inline constexpr continue_on_t continue_on{};
6393 } // namespace v2
6394
6395 template <>
6396 struct __sexpr_impl<v2::on_t> : __sexpr_defaults
6397 {
6398 using is_dependent = void;
6399 };
6400
6401 template <>
6402 struct __sexpr_impl<v2::continue_on_t> : __sexpr_defaults
6403 {
6404 using is_dependent = void;
6405 };
6406
6407 /////////////////////////////////////////////////////////////////////////////
6408 // [execution.senders.consumers.sync_wait]
6409 // [execution.senders.consumers.sync_wait_with_variant]
6410 namespace __sync_wait
6411 {
__make_env(run_loop & __loop)6412 inline auto __make_env(run_loop& __loop) noexcept
6413 {
6414 return __env::__with(__loop.get_scheduler(), get_scheduler,
6415 get_delegatee_scheduler);
6416 }
6417
6418 struct __env : __result_of<__make_env, run_loop&>
6419 {
6420 __env();
6421
__envstdexec::__sync_wait::__env6422 explicit __env(run_loop& __loop) noexcept :
6423 __result_of<__make_env, run_loop&>{__sync_wait::__make_env(__loop)}
6424 {}
6425 };
6426
6427 // What should sync_wait(just_stopped()) return?
6428 template <class _Sender, class _Continuation>
6429 using __sync_wait_result_impl = //
6430 __try_value_types_of_t<_Sender, __env,
6431 __transform<__q<__decay_t>, _Continuation>,
6432 __q<__msingle>>;
6433
6434 template <class _Sender>
6435 using __sync_wait_result_t =
6436 __mtry_eval<__sync_wait_result_impl, _Sender, __q<std::tuple>>;
6437
6438 template <class _Sender>
6439 using __sync_wait_with_variant_result_t =
6440 __mtry_eval<__sync_wait_result_impl, __result_of<into_variant, _Sender>,
6441 __q<__midentity>>;
6442
6443 template <class... _Values>
6444 struct __state
6445 {
6446 using _Tuple = std::tuple<_Values...>;
6447 std::variant<std::monostate, _Tuple, std::exception_ptr, set_stopped_t>
6448 __data_{};
6449 };
6450
6451 template <class... _Values>
6452 struct __receiver
6453 {
6454 struct __t
6455 {
6456 using receiver_concept = receiver_t;
6457 using __id = __receiver;
6458 __state<_Values...>* __state_;
6459 run_loop* __loop_;
6460
6461 template <class _Error>
__set_errorstdexec::__sync_wait::__receiver::__t6462 void __set_error(_Error __err) noexcept
6463 {
6464 if constexpr (__decays_to<_Error, std::exception_ptr>)
6465 __state_->__data_.template emplace<2>(
6466 static_cast<_Error&&>(__err));
6467 else if constexpr (__decays_to<_Error, std::error_code>)
6468 __state_->__data_.template emplace<2>(
6469 std::make_exception_ptr(std::system_error(__err)));
6470 else
6471 __state_->__data_.template emplace<2>(
6472 std::make_exception_ptr(static_cast<_Error&&>(__err)));
6473 __loop_->finish();
6474 }
6475
6476 template <same_as<set_value_t> _Tag, class... _As>
6477 requires constructible_from<std::tuple<_Values...>, _As...>
tag_invokestdexec::__sync_wait::__receiver6478 friend void tag_invoke(_Tag, __t&& __rcvr, _As&&... __as) noexcept
6479 {
6480 try
6481 {
6482 __rcvr.__state_->__data_.template emplace<1>(
6483 static_cast<_As&&>(__as)...);
6484 __rcvr.__loop_->finish();
6485 }
6486 catch (...)
6487 {
6488 __rcvr.__set_error(std::current_exception());
6489 }
6490 }
6491
6492 template <same_as<set_error_t> _Tag, class _Error>
tag_invokestdexec::__sync_wait::__receiver6493 friend void tag_invoke(_Tag, __t&& __rcvr, _Error __err) noexcept
6494 {
6495 __rcvr.__set_error(static_cast<_Error&&>(__err));
6496 }
6497
tag_invokestdexec::__sync_wait::__receiver6498 friend void tag_invoke(set_stopped_t __d, __t&& __rcvr) noexcept
6499 {
6500 __rcvr.__state_->__data_.template emplace<3>(__d);
6501 __rcvr.__loop_->finish();
6502 }
6503
tag_invokestdexec::__sync_wait::__receiver6504 friend auto tag_invoke(get_env_t, const __t& __rcvr) noexcept -> __env
6505 {
6506 return __env(*__rcvr.__loop_);
6507 }
6508 };
6509 };
6510
6511 template <class _Sender>
6512 using __receiver_t = __t<__sync_wait_result_impl<_Sender, __q<__receiver>>>;
6513
6514 // These are for hiding the metaprogramming in diagnostics
6515 template <class _Sender>
6516 struct __sync_receiver_for
6517 {
6518 using __t = __receiver_t<_Sender>;
6519 };
6520 template <class _Sender>
6521 using __sync_receiver_for_t = __t<__sync_receiver_for<_Sender>>;
6522
6523 template <class _Sender>
6524 struct __value_tuple_for
6525 {
6526 using __t = __sync_wait_result_t<_Sender>;
6527 };
6528 template <class _Sender>
6529 using __value_tuple_for_t = __t<__value_tuple_for<_Sender>>;
6530
6531 template <class _Sender>
6532 struct __variant_for
6533 {
6534 using __t = __sync_wait_with_variant_result_t<_Sender>;
6535 };
6536 template <class _Sender>
6537 using __variant_for_t = __t<__variant_for<_Sender>>;
6538
6539 inline constexpr __mstring __sync_wait_context_diag = //
6540 "In stdexec::sync_wait()..."_mstr;
6541 inline constexpr __mstring __too_many_successful_completions_diag =
6542 "The argument to stdexec::sync_wait() is a sender that can complete successfully in more "
6543 "than one way. Use stdexec::sync_wait_with_variant() instead."_mstr;
6544
6545 template <__mstring _Context, __mstring _Diagnostic>
6546 struct _INVALID_ARGUMENT_TO_SYNC_WAIT_;
6547
6548 template <__mstring _Diagnostic>
6549 using __invalid_argument_to_sync_wait =
6550 _INVALID_ARGUMENT_TO_SYNC_WAIT_<__sync_wait_context_diag, _Diagnostic>;
6551
6552 template <__mstring _Diagnostic, class _Sender, class _Env = __env>
6553 using __sync_wait_error =
6554 __mexception<__invalid_argument_to_sync_wait<_Diagnostic>,
6555 _WITH_SENDER_<_Sender>, _WITH_ENVIRONMENT_<_Env>>;
6556
6557 template <class _Sender, class>
6558 using __too_many_successful_completions_error =
6559 __sync_wait_error<__too_many_successful_completions_diag, _Sender>;
6560
6561 template <class _Sender>
6562 concept __valid_sync_wait_argument =
6563 __ok<__minvoke<__mtry_catch_q<__single_value_variant_sender_t,
6564 __q<__too_many_successful_completions_error>>,
6565 _Sender, __env>>;
6566
6567 #if STDEXEC_NVHPC()
6568 // It requires some hoop-jumping to get the NVHPC compiler to report a
6569 // meaningful diagnostic for SFINAE failures.
6570 template <class _Sender>
__diagnose_error()6571 auto __diagnose_error()
6572 {
6573 if constexpr (!sender_in<_Sender, __env>)
6574 {
6575 using _Completions = __completion_signatures_of_t<_Sender, __env>;
6576 if constexpr (__merror<_Completions>)
6577 {
6578 return _Completions();
6579 }
6580 else
6581 {
6582 constexpr __mstring __diag =
6583 "The stdexec::sender_in<Sender, Environment> concept check has failed."_mstr;
6584 return __sync_wait_error<__diag, _Sender>();
6585 }
6586 }
6587 else if constexpr (!__valid_sync_wait_argument<_Sender>)
6588 {
6589 return __sync_wait_error<__too_many_successful_completions_diag,
6590 _Sender>();
6591 }
6592 else if constexpr (!sender_to<_Sender, __sync_receiver_for_t<_Sender>>)
6593 {
6594 constexpr __mstring __diag =
6595 "Failed to connect the given sender to sync_wait's internal receiver. "
6596 "The stdexec::connect(Sender, Receiver) expression is ill-formed."_mstr;
6597 return __sync_wait_error<__diag, _Sender>();
6598 }
6599 else
6600 {
6601 constexpr __mstring __diag = "Unknown concept check failure."_mstr;
6602 return __sync_wait_error<__diag, _Sender>();
6603 }
6604 }
6605
6606 template <class _Sender>
6607 using __error_description_t =
6608 decltype(__sync_wait::__diagnose_error<_Sender>());
6609 #endif
6610
6611 ////////////////////////////////////////////////////////////////////////////
6612 // [execution.senders.consumers.sync_wait]
6613 struct sync_wait_t
6614 {
6615 template <sender_in<__env> _Sender>
6616 requires __valid_sync_wait_argument<_Sender> &&
6617 __has_implementation_for<sync_wait_t,
6618 __early_domain_of_t<_Sender>, _Sender>
operator ()stdexec::__sync_wait::sync_wait_t6619 auto operator()(_Sender&& __sndr) const
6620 -> std::optional<__value_tuple_for_t<_Sender>>
6621 {
6622 auto __domain = __get_early_domain(__sndr);
6623 return stdexec::apply_sender(__domain, *this,
6624 static_cast<_Sender&&>(__sndr));
6625 }
6626
6627 #if STDEXEC_NVHPC()
6628 // This is needed to get sensible diagnostics from nvc++
6629 template <class _Sender, class _Error = __error_description_t<_Sender>>
6630 auto operator()(_Sender&&, [[maybe_unused]] _Error __diagnostic = {}) const
6631 -> std::optional<std::tuple<int>> = delete;
6632 #endif
6633
6634 using _Sender = __0;
6635 using __legacy_customizations_t = __types<
6636 // For legacy reasons:
6637 tag_invoke_t(
6638 sync_wait_t,
6639 get_completion_scheduler_t<set_value_t>(get_env_t(const _Sender&)),
6640 _Sender),
6641 tag_invoke_t(sync_wait_t, _Sender)>;
6642
6643 // The default implementation goes here:
6644 template <class _Sender>
6645 requires sender_to<_Sender, __sync_receiver_for_t<_Sender>>
apply_senderstdexec::__sync_wait::sync_wait_t6646 auto apply_sender(_Sender&& __sndr) const
6647 -> std::optional<__sync_wait_result_t<_Sender>>
6648 {
6649 using state_t = __sync_wait_result_impl<_Sender, __q<__state>>;
6650 state_t __state{};
6651 run_loop __loop;
6652
6653 // Launch the sender with a continuation that will fill in a variant
6654 // and notify a condition variable.
6655 auto __op_state = connect(static_cast<_Sender&&>(__sndr),
6656 __receiver_t<_Sender>{&__state, &__loop});
6657 start(__op_state);
6658
6659 // Wait for the variant to be filled in.
6660 __loop.run();
6661
6662 if (__state.__data_.index() == 2)
6663 std::rethrow_exception(std::get<2>(__state.__data_));
6664
6665 if (__state.__data_.index() == 3)
6666 return std::nullopt;
6667
6668 return std::move(std::get<1>(__state.__data_));
6669 }
6670 };
6671
6672 ////////////////////////////////////////////////////////////////////////////
6673 // [execution.senders.consumers.sync_wait_with_variant]
6674 struct sync_wait_with_variant_t
6675 {
6676 struct __impl;
6677
6678 template <sender_in<__env> _Sender>
6679 requires __callable<apply_sender_t, __early_domain_of_t<_Sender>,
6680 sync_wait_with_variant_t, _Sender>
operator ()stdexec::__sync_wait::sync_wait_with_variant_t6681 auto operator()(_Sender&& __sndr) const
6682 -> std::optional<__variant_for_t<_Sender>>
6683 {
6684 auto __domain = __get_early_domain(__sndr);
6685 return stdexec::apply_sender(__domain, *this,
6686 static_cast<_Sender&&>(__sndr));
6687 }
6688
6689 #if STDEXEC_NVHPC()
6690 template <class _Sender, class _Error = __error_description_t<
6691 __result_of<into_variant, _Sender>>>
6692 auto operator()(_Sender&&, [[maybe_unused]] _Error __diagnostic = {}) const
6693 -> std::optional<std::tuple<std::variant<std::tuple<>>>> = delete;
6694 #endif
6695
6696 using _Sender = __0;
6697 using __legacy_customizations_t = __types<
6698 // For legacy reasons:
6699 tag_invoke_t(
6700 sync_wait_with_variant_t,
6701 get_completion_scheduler_t<set_value_t>(get_env_t(const _Sender&)),
6702 _Sender),
6703 tag_invoke_t(sync_wait_with_variant_t, _Sender)>;
6704
6705 template <class _Sender>
6706 requires __callable<sync_wait_t, __result_of<into_variant, _Sender>>
apply_senderstdexec::__sync_wait::sync_wait_with_variant_t6707 auto apply_sender(_Sender&& __sndr) const
6708 -> std::optional<__variant_for_t<_Sender>>
6709 {
6710 if (auto __opt_values =
6711 sync_wait_t()(into_variant(static_cast<_Sender&&>(__sndr))))
6712 {
6713 return std::move(std::get<0>(*__opt_values));
6714 }
6715 return std::nullopt;
6716 }
6717 };
6718 } // namespace __sync_wait
6719
6720 using __sync_wait::sync_wait_t;
6721 inline constexpr sync_wait_t sync_wait{};
6722
6723 using __sync_wait::sync_wait_with_variant_t;
6724 inline constexpr sync_wait_with_variant_t sync_wait_with_variant{};
6725
6726 //////////////////////////////////////////////////////////////////////////////////////////////////
6727 struct __ignore_sender
6728 {
6729 using sender_concept = sender_t;
6730
6731 template <sender _Sender>
__ignore_senderstdexec::__ignore_sender6732 constexpr __ignore_sender(_Sender&&) noexcept
6733 {}
6734 };
6735
6736 template <auto _Reason = "You cannot pipe one sender into another."_mstr>
6737 struct _CANNOT_PIPE_INTO_A_SENDER_
6738 {};
6739
6740 template <class _Sender>
6741 using __bad_pipe_sink_t =
6742 __mexception<_CANNOT_PIPE_INTO_A_SENDER_<>, _WITH_SENDER_<_Sender>>;
6743 } // namespace stdexec
6744
6745 #if STDEXEC_MSVC()
6746 namespace stdexec
6747 {
6748 // MSVCBUG
6749 // https://developercommunity.visualstudio.com/t/Incorrect-codegen-in-await_suspend-aroun/10454102
6750
6751 // MSVC incorrectly allocates the return buffer for await_suspend calls within
6752 // the suspended coroutine frame. When the suspended coroutine is destroyed
6753 // within await_suspend, the continuation coroutine handle is not only used
6754 // after free, but also overwritten by the debug malloc implementation when NRVO
6755 // is in play.
6756
6757 // This workaround delays the destruction of the suspended coroutine by wrapping
6758 // the continuation in another coroutine which destroys the former and transfers
6759 // execution to the original continuation.
6760
6761 // The wrapping coroutine is thread-local and is reused within the thread for
6762 // each destroy-and-continue sequence. The wrapping coroutine itself is
6763 // destroyed at thread exit.
6764
6765 namespace __destroy_and_continue_msvc
6766 {
6767 struct __task
6768 {
6769 struct promise_type
6770 {
get_return_objectstdexec::__destroy_and_continue_msvc::__task::promise_type6771 __task get_return_object() noexcept
6772 {
6773 return {
6774 __coro::coroutine_handle<promise_type>::from_promise(*this)};
6775 }
6776
initial_suspendstdexec::__destroy_and_continue_msvc::__task::promise_type6777 static std::suspend_never initial_suspend() noexcept
6778 {
6779 return {};
6780 }
6781
final_suspendstdexec::__destroy_and_continue_msvc::__task::promise_type6782 static std::suspend_never final_suspend() noexcept
6783 {
6784 STDEXEC_ASSERT(!"Should never get here");
6785 return {};
6786 }
6787
return_voidstdexec::__destroy_and_continue_msvc::__task::promise_type6788 static void return_void() noexcept
6789 {
6790 STDEXEC_ASSERT(!"Should never get here");
6791 }
6792
unhandled_exceptionstdexec::__destroy_and_continue_msvc::__task::promise_type6793 static void unhandled_exception() noexcept
6794 {
6795 STDEXEC_ASSERT(!"Should never get here");
6796 }
6797 };
6798
6799 __coro::coroutine_handle<> __coro_;
6800 };
6801
6802 struct __continue_t
6803 {
await_readystdexec::__destroy_and_continue_msvc::__continue_t6804 static constexpr bool await_ready() noexcept
6805 {
6806 return false;
6807 }
6808
6809 __coro::coroutine_handle<>
await_suspendstdexec::__destroy_and_continue_msvc::__continue_t6810 await_suspend(__coro::coroutine_handle<>) noexcept
6811 {
6812 return __continue_;
6813 }
6814
await_resumestdexec::__destroy_and_continue_msvc::__continue_t6815 static void await_resume() noexcept {}
6816
6817 __coro::coroutine_handle<> __continue_;
6818 };
6819
6820 struct __context
6821 {
6822 __coro::coroutine_handle<> __destroy_;
6823 __coro::coroutine_handle<> __continue_;
6824 };
6825
__co_impl(__context & __c)6826 inline __task __co_impl(__context& __c)
6827 {
6828 while (true)
6829 {
6830 co_await __continue_t{__c.__continue_};
6831 __c.__destroy_.destroy();
6832 }
6833 }
6834
6835 struct __context_and_coro
6836 {
__context_and_corostdexec::__destroy_and_continue_msvc::__context_and_coro6837 __context_and_coro()
6838 {
6839 __context_.__continue_ = __coro::noop_coroutine();
6840 __coro_ = __co_impl(__context_).__coro_;
6841 }
6842
~__context_and_corostdexec::__destroy_and_continue_msvc::__context_and_coro6843 ~__context_and_coro()
6844 {
6845 __coro_.destroy();
6846 }
6847
6848 __context __context_;
6849 __coro::coroutine_handle<> __coro_;
6850 };
6851
__impl(__coro::coroutine_handle<> __destroy,__coro::coroutine_handle<> __continue)6852 inline __coro::coroutine_handle<> __impl(__coro::coroutine_handle<> __destroy,
6853 __coro::coroutine_handle<> __continue)
6854 {
6855 static thread_local __context_and_coro __c;
6856 __c.__context_.__destroy_ = __destroy;
6857 __c.__context_.__continue_ = __continue;
6858 return __c.__coro_;
6859 }
6860 } // namespace __destroy_and_continue_msvc
6861 } // namespace stdexec
6862
6863 #define STDEXEC_DESTROY_AND_CONTINUE(__destroy, __continue) \
6864 (::stdexec::__destroy_and_continue_msvc::__impl(__destroy, __continue))
6865 #else
6866 #define STDEXEC_DESTROY_AND_CONTINUE(__destroy, __continue) \
6867 (__destroy.destroy(), __continue)
6868 #endif
6869
6870 // For issuing a meaningful diagnostic for the erroneous `snd1 | snd2`.
6871 template <stdexec::sender _Sender>
6872 requires stdexec::__ok<stdexec::__bad_pipe_sink_t<_Sender>>
6873 auto operator|(stdexec::__ignore_sender, _Sender&&) noexcept
6874 -> stdexec::__ignore_sender;
6875
6876 #include "__detail/__p2300.hpp"
6877
6878 STDEXEC_PRAGMA_POP()
6879