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 "__meta.hpp" 26 #include "__any_receiver_ref.hpp" // IWYU pragma: keep for __any::__receiver_ref 27 #include "__schedulers.hpp" 28 #include "__sender_adaptor_closure.hpp" 29 #include "__senders.hpp" 30 #include "__submit.hpp" 31 #include "__transform_sender.hpp" 32 #include "__transform_completion_signatures.hpp" 33 #include "__variant.hpp" 34 35 #include <exception> 36 37 namespace stdexec { 38 ////////////////////////////////////////////////////////////////////////////// 39 // [exec.let] 40 namespace __let { 41 // A dummy scheduler that is used by the metaprogramming below when the input sender doesn't 42 // have a completion scheduler. 43 struct __unknown_scheduler { 44 struct __attrs { querystdexec::__let::__unknown_scheduler::__attrs45 static constexpr auto query(__is_scheduler_affine_t) noexcept -> bool { 46 return true; 47 } 48 49 [[nodiscard]] querystdexec::__let::__unknown_scheduler::__attrs50 constexpr auto query(get_completion_scheduler_t<set_value_t>) const noexcept { 51 return __unknown_scheduler{}; 52 } 53 }; 54 55 struct __sender { 56 using sender_concept = sender_t; 57 58 [[nodiscard]] get_envstdexec::__let::__unknown_scheduler::__sender59 constexpr auto get_env() const noexcept -> __attrs { 60 return {}; 61 } 62 }; 63 64 [[nodiscard]] schedulestdexec::__let::__unknown_scheduler65 auto schedule() const noexcept { 66 return __sender(); 67 } 68 69 auto operator==(const __unknown_scheduler&) const noexcept -> bool = default; 70 }; 71 72 inline constexpr auto __get_rcvr = [](auto& __op_state) noexcept -> decltype(auto) { 73 return (__op_state.__rcvr_); 74 }; 75 76 inline constexpr auto __get_env = [](auto& __op_state) noexcept -> decltype(auto) { 77 return __op_state.__state_.__get_env(__op_state.__rcvr_); 78 }; 79 80 template <class _Set, class _Domain = dependent_domain> 81 struct __let_t; 82 83 template <class _Set> 84 inline constexpr __mstring __in_which_let_msg{"In stdexec::let_value(Sender, Function)..."}; 85 86 template <> 87 inline constexpr __mstring __in_which_let_msg<set_error_t>{ 88 "In stdexec::let_error(Sender, Function)..."}; 89 90 template <> 91 inline constexpr __mstring __in_which_let_msg<set_stopped_t>{ 92 "In stdexec::let_stopped(Sender, Function)..."}; 93 94 template <class _Set> 95 using __on_not_callable = __callable_error<__in_which_let_msg<_Set>>; 96 97 template <class _ReceiverId, class _SchedulerId> 98 struct __rcvr_sch { 99 using _Receiver = stdexec::__t<_ReceiverId>; 100 using _Scheduler = stdexec::__t<_SchedulerId>; 101 102 struct __t { 103 using receiver_concept = receiver_t; 104 using __id = __rcvr_sch; 105 _Receiver __rcvr_; 106 _Scheduler __sched_; 107 108 template <class... _As> set_valuestdexec::__let::__rcvr_sch::__t109 void set_value(_As&&... __as) noexcept { 110 stdexec::set_value(static_cast<_Receiver&&>(__rcvr_), static_cast<_As&&>(__as)...); 111 } 112 113 template <class _Error> set_errorstdexec::__let::__rcvr_sch::__t114 void set_error(_Error&& __err) noexcept { 115 stdexec::set_error(static_cast<_Receiver&&>(__rcvr_), static_cast<_Error&&>(__err)); 116 } 117 set_stoppedstdexec::__let::__rcvr_sch::__t118 void set_stopped() noexcept { 119 stdexec::set_stopped(static_cast<_Receiver&&>(__rcvr_)); 120 } 121 get_envstdexec::__let::__rcvr_sch::__t122 auto get_env() const noexcept { 123 return __env::__join(__sched_env{__sched_}, stdexec::get_env(__rcvr_)); 124 } 125 }; 126 }; 127 128 template <class _Receiver, class _Scheduler> 129 using __receiver_with_sched_t = __t<__rcvr_sch<__id<_Receiver>, __id<_Scheduler>>>; 130 131 // If the input sender knows its completion scheduler, make it the current scheduler 132 // in the environment seen by the result sender. 133 template <class _Scheduler, class _Env> 134 using __result_env_t = __if_c< 135 __is_scheduler_affine<schedule_result_t<_Scheduler>>, 136 _Env, 137 __join_env_t<__sched_env<_Scheduler>, _Env> 138 >; 139 140 template <__mstring _Where, __mstring _What> 141 struct _FUNCTION_MUST_RETURN_A_VALID_SENDER_IN_THE_CURRENT_ENVIRONMENT_ { }; 142 143 #if STDEXEC_EDG() 144 template <class _Sender, class _Set, class... _Env> 145 struct __bad_result_sender_ { 146 using __t = __mexception< 147 _FUNCTION_MUST_RETURN_A_VALID_SENDER_IN_THE_CURRENT_ENVIRONMENT_< 148 __in_which_let_msg<_Set>, 149 "The function must return a valid sender for the current environment"_mstr 150 >, 151 _WITH_SENDER_<_Sender>, 152 _WITH_ENVIRONMENT_<_Env>... 153 >; 154 }; 155 template <class _Sender, class _Set, class... _Env> 156 using __bad_result_sender = __t<__bad_result_sender_<_Sender, _Set, _Env...>>; 157 #else 158 template <class _Sender, class _Set, class... _Env> 159 using __bad_result_sender = __mexception< 160 _FUNCTION_MUST_RETURN_A_VALID_SENDER_IN_THE_CURRENT_ENVIRONMENT_< 161 __in_which_let_msg<_Set>, 162 "The function must return a valid sender for the current environment"_mstr 163 >, 164 _WITH_SENDER_<_Sender>, 165 _WITH_ENVIRONMENT_<_Env>... 166 >; 167 #endif 168 169 template <class _Sender, class... _Env> 170 concept __potentially_valid_sender_in = sender_in<_Sender, _Env...> 171 || (sender<_Sender> && (sizeof...(_Env) == 0)); 172 173 template <class _Set, class _Sender, class... _Env> 174 using __ensure_sender = __minvoke_if_c< 175 __potentially_valid_sender_in<_Sender, _Env...>, 176 __q<__midentity>, 177 __mbind_back_q<__bad_result_sender, _Set, _Env...>, 178 _Sender 179 >; 180 181 // A metafunction that computes the result sender type for a given set of argument types 182 template <class _Set, class _Fun, class _Sched, class... _Env> 183 struct __result_sender_fn { 184 template <class... _Args> 185 using __f = __meval< 186 __ensure_sender, 187 _Set, 188 __mcall<__mtry_catch_q<__call_result_t, __on_not_callable<_Set>>, _Fun, __decay_t<_Args>&...>, 189 __result_env_t<_Sched, _Env>... 190 >; 191 }; 192 193 // The receiver that gets connected to the result sender is the input receiver, 194 // possibly augmented with the input sender's completion scheduler (which is 195 // where the result sender will be started). 196 template <class _Receiver, class _Scheduler> 197 using __result_receiver_t = __if_c< 198 __is_scheduler_affine<schedule_result_t<_Scheduler>>, 199 _Receiver, 200 __receiver_with_sched_t<_Receiver, _Scheduler> 201 >; 202 203 template <class _ResultSender, class _Scheduler, class... _Env> 204 using __receiver_ref_t = __meval< 205 __any_::__receiver_ref, 206 __completion_signatures_of_t<_ResultSender, __result_env_t<_Scheduler, _Env>...>, 207 __result_env_t<_Scheduler, _Env>... 208 >; 209 210 template <class _ResultSender, class _Scheduler, class _Receiver> 211 concept __needs_receiver_ref = 212 __nothrow_connectable< 213 _ResultSender, 214 __receiver_ref_t<_ResultSender, _Scheduler, env_of_t<_Receiver>> 215 > 216 && !__nothrow_connectable<_ResultSender, __result_receiver_t<_Receiver, _Scheduler>>; 217 218 template <class _Sender, class _Receiver> 219 using __nothrow_connectable_t = __mbool<__nothrow_connectable<_Sender, _Receiver>>; 220 221 template <class _ResultSender, class _Scheduler, class... _Env> 222 using __nothrow_connectable_receiver_ref_t = __meval< 223 __nothrow_connectable_t, 224 _ResultSender, 225 __receiver_ref_t<_ResultSender, _Scheduler, _Env...> 226 >; 227 228 template <class _ResultSender, class _Scheduler, class _Receiver> 229 using __checked_result_receiver_t = __if_c< 230 __needs_receiver_ref<_ResultSender, _Scheduler, _Receiver>, 231 __receiver_ref_t<_ResultSender, _Scheduler, env_of_t<_Receiver>>, 232 __result_receiver_t<_Receiver, _Scheduler> 233 >; 234 235 template <class _ResultSender, class _Scheduler, class _Receiver> 236 using __submit_result = submit_result< 237 _ResultSender, 238 __checked_result_receiver_t<_ResultSender, _Scheduler, _Receiver> 239 >; 240 241 template <class _SetTag, class _Fun, class _Sched, class... _Env> 242 struct __transform_signal_fn { 243 template <class... _Args> 244 using __nothrow_connect = __mand< 245 __mbool<(__nothrow_decay_copyable<_Args> && ...) && __nothrow_callable<_Fun, _Args...>>, 246 __nothrow_connectable_receiver_ref_t< 247 __mcall<__result_sender_fn<_SetTag, _Fun, _Sched, _Env...>, _Args...>, 248 _Sched, 249 _Env... 250 > 251 >; 252 253 template <class... _Args> 254 using __f = __mcall< 255 __mtry_q<__concat_completion_signatures>, 256 __completion_signatures_of_t< 257 __mcall<__result_sender_fn<_SetTag, _Fun, _Sched, _Env...>, _Args...>, 258 __result_env_t<_Sched, _Env>... 259 >, 260 __eptr_completion_if_t<__nothrow_connect<_Args...>> 261 >; 262 }; 263 264 template <class _Sender, class _Set> 265 using __completion_sched = 266 __query_result_or_t<get_completion_scheduler_t<_Set>, env_of_t<_Sender>, __unknown_scheduler>; 267 268 template <class _LetTag, class _Fun, class _CvrefSender, class... _Env> 269 using __completions = __gather_completion_signatures< 270 __completion_signatures_of_t<_CvrefSender, _Env...>, 271 __t<_LetTag>, 272 __transform_signal_fn< 273 __t<_LetTag>, 274 _Fun, 275 __completion_sched<_CvrefSender, __t<_LetTag>>, 276 _Env... 277 >::template __f, 278 __sigs::__default_completion, 279 __mtry_q<__concat_completion_signatures>::__f 280 >; 281 282 template <__mstring _Where, __mstring _What> 283 struct _NO_COMMON_DOMAIN_ { }; 284 285 template <class _Set> 286 using __no_common_domain_t = _NO_COMMON_DOMAIN_< 287 __in_which_let_msg<_Set>, 288 "The senders returned by Function do not all share a common domain"_mstr 289 >; 290 291 template <class _Set, class _Sched> 292 struct __try_common_domain_fn { 293 struct __error_fn { 294 template <class... _Senders> 295 using __f = __mexception<__no_common_domain_t<_Set>, _WITH_SENDERS_<_Senders...>>; 296 }; 297 298 // If a sender is "scheduler affine", then it will complete on the same execution 299 // context on which it was started (e.g., just(42)). In this case, the domain of the 300 // scheduler is the domain of the sender. 301 template <class... _Senders> 302 using __common_domain = __common_domain_t< 303 __if_c<__is_scheduler_affine<_Senders>, schedule_result_t<_Sched>, _Senders>... 304 >; 305 306 template <class... _Senders> 307 using __f = __mcall<__mtry_catch_q<__common_domain, __error_fn>, _Senders...>; 308 }; 309 310 // Compute all the domains of all the result senders and make sure they're all the same 311 template <class _Set, class _Child, class _Fun, class _Sched, class... _Env> 312 using __result_domain_t = __gather_completions< 313 _Set, 314 __completion_signatures_of_t<_Child, _Env...>, 315 __result_sender_fn<_Set, _Fun, _Sched, _Env...>, 316 __try_common_domain_fn<_Set, _Sched> 317 >; 318 319 template <class _LetTag, class _Env> __mk_transform_env_fn(_Env && __env)320 auto __mk_transform_env_fn(_Env&& __env) noexcept { 321 using _Set = __t<_LetTag>; 322 return [&]<class _Fun, class _Child>(__ignore, _Fun&&, _Child&& __child) -> decltype(auto) { 323 using __completions_t = __completion_signatures_of_t<_Child, _Env>; 324 if constexpr (__merror<__completions_t>) { 325 return __completions_t(); 326 } else { 327 using _Scheduler = __completion_sched<_Child, _Set>; 328 if constexpr (__is_scheduler_affine<schedule_result_t<_Scheduler>>) { 329 return (__env); 330 } else { 331 return __env::__join( 332 __sched_env{get_completion_scheduler<_Set>(stdexec::get_env(__child))}, 333 static_cast<_Env&&>(__env)); 334 } 335 } 336 }; 337 } 338 339 template <class _LetTag, class _Env> __mk_transform_sender_fn(_Env &&)340 auto __mk_transform_sender_fn(_Env&&) noexcept { 341 using _Set = __t<_LetTag>; 342 343 return []<class _Fun, class _Child>(__ignore, _Fun&& __fun, _Child&& __child) { 344 using __completions_t = __completion_signatures_of_t<_Child, _Env>; 345 346 if constexpr (__merror<__completions_t>) { 347 return __completions_t(); 348 } else { 349 using _Sched = __completion_sched<_Child, _Set>; 350 using _Domain = __result_domain_t<_Set, _Child, _Fun, _Sched, _Env>; 351 352 if constexpr (__merror<_Domain>) { 353 return _Domain(); 354 } else if constexpr (same_as<_Domain, dependent_domain>) { 355 using _Domain2 = __late_domain_of_t<_Child, _Env>; 356 return __make_sexpr<__let_t<_Set, _Domain2>>( 357 static_cast<_Fun&&>(__fun), static_cast<_Child&&>(__child)); 358 } else { 359 static_assert(!same_as<_Domain, __unknown_scheduler>); 360 return __make_sexpr<__let_t<_Set, _Domain>>( 361 static_cast<_Fun&&>(__fun), static_cast<_Child&&>(__child)); 362 } 363 } 364 }; 365 } 366 367 //! Metafunction creating the operation state needed to connect the result of calling 368 //! the sender factory function, `_Fun`, and passing its result to a receiver. 369 template <class _Receiver, class _Fun, class _Set, class _Sched> 370 struct __submit_datum_for { 371 // compute the result of calling submit with the result of executing _Fun 372 // with _Args. if the result is void, substitute with __ignore. 373 template <class... _Args> 374 using __f = __submit_result< 375 __mcall<__result_sender_fn<_Set, _Fun, _Sched, env_of_t<_Receiver>>, _Args...>, 376 _Sched, 377 _Receiver 378 >; 379 }; 380 381 //! The core of the operation state for `let_*`. 382 //! This gets bundled up into a larger operation state (`__detail::__op_state<...>`). 383 template <class _Receiver, class _Fun, class _Set, class _Sched, class... _Tuples> 384 struct __let_state { 385 using __fun_t = _Fun; 386 using __sched_t = _Sched; 387 using __env_t = __result_env_t<_Sched, env_of_t<_Receiver>>; 388 using __rcvr_t = __receiver_with_sched_t<_Receiver, _Sched>; 389 using __result_variant = __variant_for<__monostate, _Tuples...>; 390 using __submit_variant = __variant_for< 391 __monostate, 392 __mapply<__submit_datum_for<_Receiver, _Fun, _Set, _Sched>, _Tuples>... 393 >; 394 395 template <class _ResultSender, class _OpState> __get_result_receiverstdexec::__let::__let_state396 auto __get_result_receiver(const _ResultSender&, _OpState& __op_state) -> decltype(auto) { 397 if constexpr (__needs_receiver_ref<_ResultSender, _Sched, _Receiver>) { 398 using __receiver_ref = __receiver_ref_t<_ResultSender, _Sched, env_of_t<_Receiver>>; 399 return __receiver_ref{__op_state, __let::__get_env, __let::__get_rcvr}; 400 } else { 401 _Receiver& __rcvr = __op_state.__rcvr_; 402 if constexpr (__is_scheduler_affine<schedule_result_t<_Sched>>) { 403 return static_cast<_Receiver&&>(__rcvr); 404 } else { 405 return __rcvr_t{static_cast<_Receiver&&>(__rcvr), this->__sched_}; 406 } 407 } 408 } 409 __get_envstdexec::__let::__let_state410 auto __get_env(const _Receiver& __rcvr) const noexcept -> __env_t { 411 if constexpr (__is_scheduler_affine<schedule_result_t<_Sched>>) { 412 return stdexec::get_env(__rcvr); 413 } else { 414 return __env::__join(__sched_env{__sched_}, stdexec::get_env(__rcvr)); 415 } 416 } 417 418 STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS 419 _Fun __fun_; 420 STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS 421 _Sched __sched_; 422 //! Variant to hold the results passed from upstream before passing them to the function: 423 __result_variant __args_{}; 424 //! Variant type for holding the operation state from connecting 425 //! the function result to the downstream receiver: 426 __submit_variant __storage_{}; 427 }; 428 429 //! Implementation of the `let_*_t` types, where `_Set` is, e.g., `set_value_t` for `let_value`. 430 template <class _Set, class _Domain> 431 struct __let_t { 432 using __domain_t = _Domain; 433 using __t = _Set; 434 435 template <sender _Sender, __movable_value _Fun> operator ()stdexec::__let::__let_t436 auto operator()(_Sender&& __sndr, _Fun __fun) const -> __well_formed_sender auto { 437 auto __domain = __get_early_domain(__sndr); 438 return stdexec::transform_sender( 439 __domain, 440 __make_sexpr<__let_t<_Set>>(static_cast<_Fun&&>(__fun), static_cast<_Sender&&>(__sndr))); 441 } 442 443 template <class _Fun> STDEXEC_ATTRIBUTEstdexec::__let::__let_t444 STDEXEC_ATTRIBUTE(always_inline) 445 auto operator()(_Fun __fun) const -> __binder_back<__let_t, _Fun> { 446 return {{static_cast<_Fun&&>(__fun)}, {}, {}}; 447 } 448 449 template <sender_expr_for<__let_t<_Set>> _Sender, class _Env> transform_envstdexec::__let::__let_t450 static auto transform_env(_Sender&& __sndr, const _Env& __env) -> decltype(auto) { 451 return __sexpr_apply( 452 static_cast<_Sender&&>(__sndr), __mk_transform_env_fn<__let_t<_Set>>(__env)); 453 } 454 455 template <sender_expr_for<__let_t<_Set>> _Sender, class _Env> 456 requires same_as<__early_domain_of_t<_Sender>, dependent_domain> transform_senderstdexec::__let::__let_t457 static auto transform_sender(_Sender&& __sndr, const _Env& __env) -> decltype(auto) { 458 return __sexpr_apply( 459 static_cast<_Sender&&>(__sndr), __mk_transform_sender_fn<__let_t<_Set>>(__env)); 460 } 461 }; 462 463 template <class _Set, class _Domain> 464 struct __let_impl : __sexpr_defaults { 465 static constexpr auto get_attrs = 466 []<class _Fun, class _Child>(const _Fun&, const _Child& __child) noexcept { 467 if constexpr (!same_as<_Domain, dependent_domain>) { 468 return __env::__join(prop{get_domain, _Domain()}, stdexec::get_env(__child)); 469 } else { 470 using _Sched = __completion_sched<_Child, _Set>; 471 using _Domain2 = __result_domain_t<_Set, _Child, _Fun, _Sched>; 472 473 if constexpr (__merror<_Domain2>) { 474 return __env::__join(prop{get_domain, dependent_domain()}, stdexec::get_env(__child)); 475 } else { 476 return __env::__join(prop{get_domain, _Domain2()}, stdexec::get_env(__child)); 477 } 478 } 479 }; 480 481 static constexpr auto get_completion_signatures = 482 []<class _Self, class... _Env>(_Self&&, _Env&&...) noexcept 483 -> __completions<__let_t<_Set, _Domain>, __data_of<_Self>, __child_of<_Self>, _Env...> { 484 static_assert(sender_expr_for<_Self, __let_t<_Set, _Domain>>); 485 return {}; 486 }; 487 488 static constexpr auto get_state = 489 []<class _Sender, class _Receiver>(_Sender&& __sndr, _Receiver&) { 490 static_assert(sender_expr_for<_Sender, __let_t<_Set, _Domain>>); 491 using _Fun = __data_of<_Sender>; 492 using _Child = __child_of<_Sender>; 493 using _Sched = __decay_t<__completion_sched<_Child, _Set>>; 494 using __mk_let_state = __mbind_front_q<__let_state, _Receiver, _Fun, _Set, _Sched>; 495 496 using __let_state_t = __gather_completions_of< 497 _Set, 498 _Child, 499 env_of_t<_Receiver>, 500 __q<__decayed_tuple>, 501 __mk_let_state 502 >; 503 504 return __sndr.apply( 505 static_cast<_Sender&&>(__sndr), 506 [&]<class _Fn, class _Child>(__ignore, _Fn&& __fn, _Child&& __child) { 507 _Sched __sched = query_or( 508 get_completion_scheduler<_Set>, stdexec::get_env(__child), __unknown_scheduler()); 509 return __let_state_t{static_cast<_Fn&&>(__fn), __sched}; 510 }); 511 }; 512 513 //! Helper function to actually invoke the function to produce `let_*`'s sender, 514 //! connect it to the downstream receiver, and start it. This is the heart of 515 //! `let_*`. 516 template <class _State, class _OpState, class... _As> __bind_stdexec::__let::__let_impl517 static void __bind_(_State& __state, _OpState& __op_state, _As&&... __as) { 518 // Store the passed-in (received) args: 519 auto& __args = __state.__args_.emplace_from(__tup::__mktuple, static_cast<_As&&>(__as)...); 520 // Apply the function to the args to get the sender: 521 auto __sndr2 = __args.apply(std::move(__state.__fun_), __args); 522 // Create a receiver based on the state, the computed sender, and the operation state: 523 auto __rcvr2 = __state.__get_result_receiver(__sndr2, __op_state); 524 // Connect the sender to the receiver and start it: 525 using __result_t = decltype(submit_result{std::move(__sndr2), std::move(__rcvr2)}); 526 auto& __op = __state.__storage_ 527 .template emplace<__result_t>(std::move(__sndr2), std::move(__rcvr2)); 528 __op.submit(std::move(__sndr2), std::move(__rcvr2)); 529 } 530 531 template <class _OpState, class... _As> __bindstdexec::__let::__let_impl532 static void __bind(_OpState& __op_state, _As&&... __as) noexcept { 533 using _State = decltype(__op_state.__state_); 534 using _Receiver = decltype(__op_state.__rcvr_); 535 using _Fun = _State::__fun_t; 536 using _Sched = _State::__sched_t; 537 using _ResultSender = 538 __mcall<__result_sender_fn<_Set, _Fun, _Sched, env_of_t<_Receiver>>, _As...>; 539 540 _State& __state = __op_state.__state_; 541 _Receiver& __rcvr = __op_state.__rcvr_; 542 543 if constexpr ( 544 (__nothrow_decay_copyable<_As> && ...) && __nothrow_callable<_Fun, _As...> 545 && __v<__nothrow_connectable_receiver_ref_t<_ResultSender, _Sched, env_of_t<_Receiver>>>) { 546 __bind_(__state, __op_state, static_cast<_As&&>(__as)...); 547 } else { 548 STDEXEC_TRY { 549 __bind_(__state, __op_state, static_cast<_As&&>(__as)...); 550 } 551 STDEXEC_CATCH_ALL { 552 using _Receiver = decltype(__op_state.__rcvr_); 553 stdexec::set_error(static_cast<_Receiver&&>(__rcvr), std::current_exception()); 554 } 555 } 556 } 557 558 static constexpr auto complete = []<class _OpState, class _Tag, class... _As>( 559 __ignore, 560 _OpState& __op_state, 561 _Tag, 562 _As&&... __as) noexcept -> void { 563 if constexpr (__same_as<_Tag, _Set>) { 564 // Intercept the channel of interest to compute the sender and connect it: 565 __bind(__op_state, static_cast<_As&&>(__as)...); 566 } else { 567 // Forward the other channels downstream: 568 using _Receiver = decltype(__op_state.__rcvr_); 569 _Tag()(static_cast<_Receiver&&>(__op_state.__rcvr_), static_cast<_As&&>(__as)...); 570 } 571 }; 572 }; 573 } // namespace __let 574 575 using let_value_t = __let::__let_t<set_value_t>; 576 inline constexpr let_value_t let_value{}; 577 578 using let_error_t = __let::__let_t<set_error_t>; 579 inline constexpr let_error_t let_error{}; 580 581 using let_stopped_t = __let::__let_t<set_stopped_t>; 582 inline constexpr let_stopped_t let_stopped{}; 583 584 template <class _Set, class _Domain> 585 struct __sexpr_impl<__let::__let_t<_Set, _Domain>> : __let::__let_impl<_Set, _Domain> { }; 586 } // namespace stdexec 587