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