1 /*
2 * Copyright (c) 2021-2024 NVIDIA Corporation
3 *
4 * Licensed under the Apache License Version 2.0 with LLVM Exceptions
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * https://llvm.org/LICENSE.txt
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #pragma once
17
18 #include "__execution_fwd.hpp"
19
20 // include these after __execution_fwd.hpp
21 #include "__basic_sender.hpp"
22 #include "__diagnostics.hpp"
23 #include "__domain.hpp"
24 #include "__env.hpp"
25 #include "__inline_scheduler.hpp"
26 #include "__meta.hpp"
27 #include "__receiver_ref.hpp"
28 #include "__sender_adaptor_closure.hpp"
29 #include "__senders.hpp"
30 #include "__tag_invoke.hpp"
31 #include "__transform_completion_signatures.hpp"
32 #include "__transform_sender.hpp"
33 #include "__variant.hpp"
34
35 #include <exception>
36
37 namespace stdexec
38 {
39 //////////////////////////////////////////////////////////////////////////////
40 // [exec.let]
41 namespace __let
42 {
43 // A dummy scheduler that is used by the metaprogramming below when the input
44 // sender doesn't have a completion scheduler.
45 struct __unknown_scheduler
46 {
47 struct __env
48 {
querystdexec::__let::__unknown_scheduler::__env49 static constexpr bool query(__is_scheduler_affine_t) noexcept
50 {
51 return true;
52 }
53
54 constexpr auto
querystdexec::__let::__unknown_scheduler::__env55 query(get_completion_scheduler_t<set_value_t>) const noexcept
56 {
57 return __unknown_scheduler{};
58 }
59 };
60
61 struct __sender
62 {
63 using sender_concept = sender_t;
64
get_envstdexec::__let::__unknown_scheduler::__sender65 constexpr auto get_env() const noexcept -> __env
66 {
67 return __env();
68 }
69 };
70
schedulestdexec::__let::__unknown_scheduler71 auto schedule() const noexcept
72 {
73 return __sender();
74 }
75
76 bool operator==(const __unknown_scheduler&) const noexcept = default;
77 };
78
79 inline constexpr auto __get_rcvr =
80 [](auto& __op_state) noexcept -> decltype(auto) {
81 return (__op_state.__rcvr_);
82 };
83
84 inline constexpr auto __get_env =
85 [](auto& __op_state) noexcept -> decltype(auto) {
86 return __op_state.__state_.__get_env(__op_state.__rcvr_);
87 };
88
89 template <class _Set, class _Domain = dependent_domain>
90 struct __let_t;
91
92 template <class _Set>
93 inline constexpr __mstring __in_which_let_msg{
94 "In stdexec::let_value(Sender, Function)..."};
95
96 template <>
97 inline constexpr __mstring __in_which_let_msg<set_error_t>{
98 "In stdexec::let_error(Sender, Function)..."};
99
100 template <>
101 inline constexpr __mstring __in_which_let_msg<set_stopped_t>{
102 "In stdexec::let_stopped(Sender, Function)..."};
103
104 template <class _Set>
105 using __on_not_callable = __callable_error<__in_which_let_msg<_Set>>;
106
107 template <class _Receiver, class _Scheduler>
108 struct __receiver_with_sched
109 {
110 using receiver_concept = receiver_t;
111 _Receiver __rcvr_;
112 _Scheduler __sched_;
113
114 template <class... _As>
set_valuestdexec::__let::__receiver_with_sched115 void set_value(_As&&... __as) noexcept
116 {
117 stdexec::set_value(static_cast<_Receiver&&>(__rcvr_),
118 static_cast<_As&&>(__as)...);
119 }
120
121 template <class _Error>
set_errorstdexec::__let::__receiver_with_sched122 void set_error(_Error&& __err) noexcept
123 {
124 stdexec::set_error(static_cast<_Receiver&&>(__rcvr_),
125 static_cast<_Error&&>(__err));
126 }
127
set_stoppedstdexec::__let::__receiver_with_sched128 void set_stopped() noexcept
129 {
130 stdexec::set_stopped(static_cast<_Receiver&&>(__rcvr_));
131 }
132
get_envstdexec::__let::__receiver_with_sched133 auto get_env() const noexcept
134 {
135 return __env::__join(
136 prop{get_scheduler, __sched_},
137 __env::__without(stdexec::get_env(__rcvr_), get_domain));
138 }
139 };
140
141 template <class _Receiver, class _Scheduler>
142 __receiver_with_sched(_Receiver, _Scheduler)
143 -> __receiver_with_sched<_Receiver, _Scheduler>;
144
145 // If the input sender knows its completion scheduler, make it the current
146 // scheduler in the environment seen by the result sender.
147 template <class _Scheduler, class _Env>
148 using __result_env_t =
149 __if_c<__is_scheduler_affine<schedule_result_t<_Scheduler>>, _Env,
150 __env::__join_t< //
151 prop<get_scheduler_t, _Scheduler>,
152 __env::__without_t<_Env, get_domain_t>>>;
153
154 template <__mstring _Where, __mstring _What>
155 struct _FUNCTION_MUST_RETURN_A_VALID_SENDER_IN_THE_CURRENT_ENVIRONMENT_
156 {};
157
158 #if STDEXEC_NVHPC()
159 template <class _Sender, class _Set, class... _Env>
160 struct __bad_result_sender_
161 {
162 using __t = __mexception<
163 _FUNCTION_MUST_RETURN_A_VALID_SENDER_IN_THE_CURRENT_ENVIRONMENT_<
164 __in_which_let_msg<_Set>,
165 "The function must return a valid sender for the current environment"_mstr>,
166 _WITH_SENDER_<_Sender>, _WITH_ENVIRONMENT_<_Env>...>;
167 };
168 template <class _Sender, class _Set, class... _Env>
169 using __bad_result_sender = __t<__bad_result_sender_<_Sender, _Set, _Env...>>;
170 #else
171 template <class _Sender, class _Set, class... _Env>
172 using __bad_result_sender = __mexception<
173 _FUNCTION_MUST_RETURN_A_VALID_SENDER_IN_THE_CURRENT_ENVIRONMENT_<
174 __in_which_let_msg<_Set>,
175 "The function must return a valid sender for the current environment"_mstr>,
176 _WITH_SENDER_<_Sender>, _WITH_ENVIRONMENT_<_Env>...>;
177 #endif
178
179 template <class _Sender, class... _Env>
180 concept __potentially_valid_sender_in =
181 sender_in<_Sender, _Env...> || (sender<_Sender> && (sizeof...(_Env) == 0));
182
183 template <class _Set, class _Sender, class... _Env>
184 using __ensure_sender = //
185 __minvoke_if_c<__potentially_valid_sender_in<_Sender, _Env...>,
186 __q<__midentity>,
187 __mbind_back_q<__bad_result_sender, _Set, _Env...>, _Sender>;
188
189 // A metafunction that computes the result sender type for a given set of
190 // argument types
191 template <class _Set, class _Fun, class _Sched, class... _Env>
192 struct __result_sender_fn
193 {
194 template <class... _Args>
195 using __f = //
196 __meval<
197 __ensure_sender, _Set,
198 __mcall<__mtry_catch_q<__call_result_t, __on_not_callable<_Set>>,
199 _Fun, __decay_t<_Args>&...>,
200 __result_env_t<_Sched, _Env>...>;
201 };
202
203 // The receiver that gets connected to the result sender is the input receiver,
204 // possibly augmented with the input sender's completion scheduler (which is
205 // where the result sender will be started).
206 template <class _Receiver, class _Scheduler>
207 using __result_receiver_t =
208 __if_c<__is_scheduler_affine<schedule_result_t<_Scheduler>>, _Receiver,
209 __receiver_with_sched<_Receiver, _Scheduler>>;
210
211 template <class _ResultSender, class _Scheduler, class... _Env>
212 using __receiver_ref_t = //
213 __meval<__any_::__receiver_ref,
214 __completion_signatures_of_t<_ResultSender,
215 __result_env_t<_Scheduler, _Env>...>,
216 __result_env_t<_Scheduler, _Env>...>;
217
218 template <class _ResultSender, class _Scheduler, class _Receiver>
219 concept __needs_receiver_ref =
220 __nothrow_connectable<
221 _ResultSender,
222 __receiver_ref_t<_ResultSender, _Scheduler, env_of_t<_Receiver>>> &&
223 !__nothrow_connectable<_ResultSender,
224 __result_receiver_t<_Receiver, _Scheduler>>;
225
226 template <class _Sender, class _Receiver>
227 using __nothrow_connectable_t =
228 __mbool<__nothrow_connectable<_Sender, _Receiver>>;
229
230 template <class _ResultSender, class _Scheduler, class... _Env>
231 using __nothrow_connectable_receiver_ref_t =
232 __meval<__nothrow_connectable_t, _ResultSender,
233 __receiver_ref_t<_ResultSender, _Scheduler, _Env...>>;
234
235 template <class _ResultSender, class _Scheduler, class _Receiver>
236 using __checked_result_receiver_t = //
237 __if_c<__needs_receiver_ref<_ResultSender, _Scheduler, _Receiver>,
238 __receiver_ref_t<_ResultSender, _Scheduler, env_of_t<_Receiver>>,
239 __result_receiver_t<_Receiver, _Scheduler>>;
240
241 template <class _ResultSender, class _Scheduler, class _Receiver>
242 using __op_state_t = connect_result_t<
243 _ResultSender,
244 __checked_result_receiver_t<_ResultSender, _Scheduler, _Receiver>>;
245
246 template <class _SetTag, class _Fun, class _Sched, class... _Env>
247 struct __transform_signal_fn
248 {
249 template <class... _Args>
250 using __nothrow_connect =
251 __mand<__mbool<(__nothrow_decay_copyable<_Args> && ...) &&
252 __nothrow_callable<_Fun, _Args...>>,
253 __nothrow_connectable_receiver_ref_t<
254 __mcall<__result_sender_fn<_SetTag, _Fun, _Sched, _Env...>,
255 _Args...>,
256 _Sched, _Env...>>;
257
258 template <class... _Args>
259 using __f = //
260 __mcall<__mtry_q<__concat_completion_signatures>,
261 __completion_signatures_of_t<
262 __mcall<__result_sender_fn<_SetTag, _Fun, _Sched, _Env...>,
263 _Args...>,
264 __result_env_t<_Sched, _Env>...>,
265 __eptr_completion_if_t<__nothrow_connect<_Args...>>>;
266 };
267
268 template <class _Sender, class _Set>
269 using __completion_sched =
270 __query_result_or_t<get_completion_scheduler_t<_Set>, env_of_t<_Sender>,
271 __unknown_scheduler>;
272
273 template <class _LetTag, class _Fun, class _CvrefSender, class... _Env>
274 using __completions = //
275 __gather_completion_signatures<
276 __completion_signatures_of_t<_CvrefSender, _Env...>, __t<_LetTag>,
277 __transform_signal_fn<__t<_LetTag>, _Fun,
278 __completion_sched<_CvrefSender, __t<_LetTag>>,
279 _Env...>::template __f,
280 __sigs::__default_completion,
281 __mtry_q<__concat_completion_signatures>::__f>;
282
283 template <__mstring _Where, __mstring _What>
284 struct _NO_COMMON_DOMAIN_
285 {};
286
287 template <class _Set>
288 using __no_common_domain_t = //
289 _NO_COMMON_DOMAIN_<
290 __in_which_let_msg<_Set>,
291 "The senders returned by Function do not all share a common domain"_mstr>;
292
293 template <class _Set>
294 struct __try_common_domain_fn
295 {
296 struct __error_fn
297 {
298 template <class... _Senders>
299 using __f = __mexception<__no_common_domain_t<_Set>,
300 _WITH_SENDERS_<_Senders...>>;
301 };
302
303 template <class... _Senders>
304 using __f = __mcall<__mtry_catch_q<__domain::__common_domain_t, __error_fn>,
305 _Senders...>;
306 };
307
308 // Compute all the domains of all the result senders and make sure they're all
309 // the same
310 template <class _Set, class _Child, class _Fun, class _Env, class _Sched>
311 using __result_domain_t = //
312 __gather_completions<_Set, __completion_signatures_of_t<_Child, _Env>,
313 __result_sender_fn<_Set, _Fun, _Sched, _Env>,
314 __try_common_domain_fn<_Set>>;
315
316 template <class _LetTag, class _Env>
__mk_transform_env_fn(_Env && __env)317 auto __mk_transform_env_fn(_Env&& __env) noexcept
318 {
319 using _Set = __t<_LetTag>;
320 return [&]<class _Fun, class _Child>(__ignore, _Fun&&,
321 _Child&& __child) -> decltype(auto) {
322 using __completions_t = __completion_signatures_of_t<_Child, _Env>;
323 if constexpr (__merror<__completions_t>)
324 {
325 return __completions_t();
326 }
327 else
328 {
329 using _Scheduler = __completion_sched<_Child, _Set>;
330 if constexpr (__is_scheduler_affine<schedule_result_t<_Scheduler>>)
331 {
332 return (__env);
333 }
334 else
335 {
336 return __env::__join(
337 prop{get_scheduler, get_completion_scheduler<_Set>(
338 stdexec::get_env(__child))},
339 __env::__without(static_cast<_Env&&>(__env), get_domain));
340 }
341 }
342 };
343 }
344
345 template <class _LetTag, class _Env>
__mk_transform_sender_fn(_Env &&)346 auto __mk_transform_sender_fn(_Env&&) noexcept
347 {
348 using _Set = __t<_LetTag>;
349
350 return []<class _Fun, class _Child>(__ignore, _Fun&& __fun,
351 _Child&& __child) {
352 using __completions_t = __completion_signatures_of_t<_Child, _Env>;
353
354 if constexpr (__merror<__completions_t>)
355 {
356 return __completions_t();
357 }
358 else
359 {
360 using _Sched = __completion_sched<_Child, _Set>;
361 using _Domain = __result_domain_t<_Set, _Child, _Fun, _Env, _Sched>;
362
363 if constexpr (__merror<_Domain>)
364 {
365 return _Domain();
366 }
367 else if constexpr (same_as<_Domain, dependent_domain>)
368 {
369 using _Domain2 = __late_domain_of_t<_Child, _Env>;
370 return __make_sexpr<__let_t<_Set, _Domain2>>(
371 static_cast<_Fun&&>(__fun), static_cast<_Child&&>(__child));
372 }
373 else
374 {
375 static_assert(!same_as<_Domain, __unknown_scheduler>);
376 return __make_sexpr<__let_t<_Set, _Domain>>(
377 static_cast<_Fun&&>(__fun), static_cast<_Child&&>(__child));
378 }
379 }
380 };
381 }
382
383 template <class _Receiver, class _Fun, class _Set, class _Sched>
384 struct __op_state_for
385 {
386 template <class... _Args>
387 using __f = __op_state_t<
388 __mcall<__result_sender_fn<_Set, _Fun, _Sched, env_of_t<_Receiver>>,
389 _Args...>,
390 _Sched, _Receiver>;
391 };
392
393 template <class _Receiver, class _Fun, class _Set, class _Sched,
394 class... _Tuples>
395 struct __let_state
396 {
397 using __fun_t = _Fun;
398 using __sched_t = _Sched;
399 using __env_t = __result_env_t<_Sched, env_of_t<_Receiver>>;
400 using __result_variant = __variant_for<__monostate, _Tuples...>;
401 using __op_state_variant = //
402 __variant_for<__monostate,
403 __mapply<__op_state_for<_Receiver, _Fun, _Set, _Sched>,
404 _Tuples>...>;
405
406 template <class _ResultSender, class _OpState>
__get_result_receiverstdexec::__let::__let_state407 auto __get_result_receiver(const _ResultSender&,
408 _OpState& __op_state) -> decltype(auto)
409 {
410 if constexpr (__needs_receiver_ref<_ResultSender, _Sched, _Receiver>)
411 {
412 using __receiver_ref =
413 __receiver_ref_t<_ResultSender, _Sched, env_of_t<_Receiver>>;
414 return __receiver_ref{__op_state, __let::__get_env,
415 __let::__get_rcvr};
416 }
417 else
418 {
419 _Receiver& __rcvr = __op_state.__rcvr_;
420 if constexpr (__is_scheduler_affine<schedule_result_t<_Sched>>)
421 {
422 return static_cast<_Receiver&&>(__rcvr);
423 }
424 else
425 {
426 return __receiver_with_sched{static_cast<_Receiver&&>(__rcvr),
427 this->__sched_};
428 }
429 }
430 }
431
__get_envstdexec::__let::__let_state432 auto __get_env(const _Receiver& __rcvr) const noexcept -> __env_t
433 {
434 if constexpr (__is_scheduler_affine<schedule_result_t<_Sched>>)
435 {
436 return stdexec::get_env(__rcvr);
437 }
438 else
439 {
440 return __env::__join(
441 prop{get_scheduler, __sched_},
442 __env::__without(stdexec::get_env(__rcvr), get_domain));
443 }
444 }
445
446 STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS _Fun __fun_;
447 STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS _Sched __sched_;
448 __result_variant __args_{};
449 __op_state_variant __op_state3_{};
450 };
451
452 template <class _Set, class _Domain>
453 struct __let_t
454 {
455 using __domain_t = _Domain;
456 using __t = _Set;
457
458 template <sender _Sender, __movable_value _Fun>
operator ()stdexec::__let::__let_t459 auto operator()(_Sender&& __sndr, _Fun __fun) const -> __well_formed_sender
460 auto
461 {
462 auto __domain = __get_early_domain(__sndr);
463 return stdexec::transform_sender(
464 __domain,
465 __make_sexpr<__let_t<_Set>>(static_cast<_Fun&&>(__fun),
466 static_cast<_Sender&&>(__sndr)));
467 }
468
469 template <class _Fun>
470 STDEXEC_ATTRIBUTE((always_inline))
operator ()stdexec::__let::__let_t471 auto operator()(_Fun __fun) const -> __binder_back<__let_t, _Fun>
472 {
473 return {{static_cast<_Fun&&>(__fun)}, {}, {}};
474 }
475
476 using _Sender = __1;
477 using _Function = __0;
478 using __legacy_customizations_t =
479 __types<tag_invoke_t(__let_t,
480 get_completion_scheduler_t<set_value_t>(
481 get_env_t(const _Sender&)),
482 _Sender, _Function),
483 tag_invoke_t(__let_t, _Sender, _Function)>;
484
485 template <sender_expr_for<__let_t<_Set>> _Sender, class _Env>
transform_envstdexec::__let::__let_t486 static auto transform_env(_Sender&& __sndr,
487 const _Env& __env) -> decltype(auto)
488 {
489 return __sexpr_apply(static_cast<_Sender&&>(__sndr),
490 __mk_transform_env_fn<__let_t<_Set>>(__env));
491 }
492
493 template <sender_expr_for<__let_t<_Set>> _Sender, class _Env>
494 requires same_as<__early_domain_of_t<_Sender>, dependent_domain>
495 static auto
transform_senderstdexec::__let::__let_t496 transform_sender(_Sender&& __sndr, const _Env& __env) -> decltype(auto)
497 {
498 return __sexpr_apply(static_cast<_Sender&&>(__sndr),
499 __mk_transform_sender_fn<__let_t<_Set>>(__env));
500 }
501 };
502
503 template <class _Set, class _Domain>
504 struct __let_impl : __sexpr_defaults
505 {
506 static constexpr auto get_attrs = //
507 []<class _Child>(__ignore, const _Child& __child) noexcept {
508 return __env::__join(prop{get_domain, _Domain()},
509 stdexec::get_env(__child));
510 };
511
512 static constexpr auto get_completion_signatures = //
513 []<class _Self, class... _Env>(_Self&&, _Env&&...) noexcept
514 -> __completions<__let_t<_Set, _Domain>, __data_of<_Self>,
515 __child_of<_Self>, _Env...> {
516 static_assert(sender_expr_for<_Self, __let_t<_Set, _Domain>>);
517 return {};
518 };
519
520 static constexpr auto get_state = //
521 []<class _Sender, class _Receiver>(_Sender&& __sndr, _Receiver&) {
522 static_assert(sender_expr_for<_Sender, __let_t<_Set, _Domain>>);
523 using _Fun = __data_of<_Sender>;
524 using _Child = __child_of<_Sender>;
525 using _Sched = __completion_sched<_Child, _Set>;
526 using __mk_let_state =
527 __mbind_front_q<__let_state, _Receiver, _Fun, _Set, _Sched>;
528
529 using __let_state_t =
530 __gather_completions_of<_Set, _Child, env_of_t<_Receiver>,
531 __q<__decayed_tuple>, __mk_let_state>;
532
533 return __sndr.apply(
534 static_cast<_Sender&&>(__sndr),
535 [&]<class _Fn, class _Child>(__ignore, _Fn&& __fn,
536 _Child&& __child) {
537 _Sched __sched = query_or(get_completion_scheduler<_Set>,
538 stdexec::get_env(__child),
539 __unknown_scheduler());
540 return __let_state_t{static_cast<_Fn&&>(__fn), __sched};
541 });
542 };
543
544 template <class _State, class _OpState, class... _As>
__bind_stdexec::__let::__let_impl545 static void __bind_(_State& __state, _OpState& __op_state, _As&&... __as)
546 {
547 auto& __args = __state.__args_.emplace_from(
548 __tup::__mktuple, static_cast<_As&&>(__as)...);
549 auto __sndr2 = __args.apply(std::move(__state.__fun_), __args);
550 auto __rcvr2 = __state.__get_result_receiver(__sndr2, __op_state);
551 auto& __op2 = __state.__op_state3_.emplace_from(
552 stdexec::connect, std::move(__sndr2), std::move(__rcvr2));
553 stdexec::start(__op2);
554 }
555
556 template <class _OpState, class... _As>
__bindstdexec::__let::__let_impl557 static void __bind(_OpState& __op_state, _As&&... __as) noexcept
558 {
559 using _State = decltype(__op_state.__state_);
560 using _Receiver = decltype(__op_state.__rcvr_);
561 using _Fun = typename _State::__fun_t;
562 using _Sched = typename _State::__sched_t;
563 using _ResultSender =
564 __mcall<__result_sender_fn<_Set, _Fun, _Sched, env_of_t<_Receiver>>,
565 _As...>;
566
567 _State& __state = __op_state.__state_;
568 _Receiver& __rcvr = __op_state.__rcvr_;
569
570 if constexpr ((__nothrow_decay_copyable<_As> && ...) &&
571 __nothrow_callable<_Fun, _As...> &&
572 __v<__nothrow_connectable_receiver_ref_t<
573 _ResultSender, _Sched, env_of_t<_Receiver>>>)
574 {
575 __bind_(__state, __op_state, static_cast<_As&&>(__as)...);
576 }
577 else
578 {
579 try
580 {
581 __bind_(__state, __op_state, static_cast<_As&&>(__as)...);
582 }
583 catch (...)
584 {
585 using _Receiver = decltype(__op_state.__rcvr_);
586 stdexec::set_error(static_cast<_Receiver&&>(__rcvr),
587 std::current_exception());
588 }
589 }
590 }
591
592 static constexpr auto complete = //
593 []<class _OpState, class _Tag, class... _As>(
594 __ignore, _OpState& __op_state, _Tag,
595 _As&&... __as) noexcept -> void {
596 if constexpr (__same_as<_Tag, _Set>)
597 {
598 __bind(__op_state, static_cast<_As&&>(__as)...);
599 }
600 else
601 {
602 using _Receiver = decltype(__op_state.__rcvr_);
603 _Tag()(static_cast<_Receiver&&>(__op_state.__rcvr_),
604 static_cast<_As&&>(__as)...);
605 }
606 };
607 };
608 } // namespace __let
609
610 using let_value_t = __let::__let_t<set_value_t>;
611 inline constexpr let_value_t let_value{};
612
613 using let_error_t = __let::__let_t<set_error_t>;
614 inline constexpr let_error_t let_error{};
615
616 using let_stopped_t = __let::__let_t<set_stopped_t>;
617 inline constexpr let_stopped_t let_stopped{};
618
619 template <class _Set, class _Domain>
620 struct __sexpr_impl<__let::__let_t<_Set, _Domain>> :
621 __let::__let_impl<_Set, _Domain>
622 {};
623 } // namespace stdexec
624