1 /* 2 * Copyright (c) 2021-2023 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 "../functional.hpp" 19 #include "../stop_token.hpp" 20 #include "__concepts.hpp" 21 #include "__config.hpp" 22 #include "__execution_fwd.hpp" 23 #include "__meta.hpp" 24 25 #include <concepts> 26 #include <exception> 27 #include <type_traits> 28 29 STDEXEC_PRAGMA_PUSH() 30 STDEXEC_PRAGMA_IGNORE_EDG(probable_guiding_friend) 31 STDEXEC_PRAGMA_IGNORE_EDG(type_qualifiers_ignored_on_reference) 32 33 namespace stdexec 34 { 35 // [exec.queries.queryable] 36 template <class T> 37 concept queryable = destructible<T>; 38 39 template <class Tag> 40 struct __query 41 { 42 template <class Sig> 43 static inline constexpr Tag (*signature)(Sig) = nullptr; 44 }; 45 46 ////////////////////////////////////////////////////////////////////////////////////////////////// 47 // [exec.queries] 48 namespace __queries 49 { 50 struct forwarding_query_t 51 { 52 template <class _Query> 53 constexpr bool operator()(_Query __query) const noexcept 54 { 55 if constexpr (tag_invocable<forwarding_query_t, _Query>) 56 { 57 return tag_invoke(*this, (_Query&&)__query); 58 } 59 else if constexpr (std::derived_from<_Query, forwarding_query_t>) 60 { 61 return true; 62 } 63 else 64 { 65 return false; 66 } 67 } 68 }; 69 70 struct query_or_t 71 { 72 template <class _Query, class _Queryable, class _Default> 73 constexpr auto operator()(_Query, _Queryable&&, _Default&& __default) const 74 noexcept(__nothrow_constructible_from<_Default, _Default&&>) -> _Default 75 { 76 return (_Default&&)__default; 77 } 78 79 template <class _Query, class _Queryable, class _Default> 80 requires __callable<_Query, _Queryable> 81 constexpr auto operator()(_Query __query, _Queryable&& __queryable, 82 _Default&&) const 83 noexcept(__nothrow_callable<_Query, _Queryable>) 84 -> __call_result_t<_Query, _Queryable> 85 { 86 return ((_Query&&)__query)((_Queryable&&)__queryable); 87 } 88 }; 89 90 struct execute_may_block_caller_t : __query<execute_may_block_caller_t> 91 { 92 template <class _Tp> 93 requires tag_invocable<execute_may_block_caller_t, __cref_t<_Tp>> 94 constexpr bool operator()(_Tp&& __t) const noexcept 95 { 96 static_assert( 97 same_as<bool, tag_invoke_result_t<execute_may_block_caller_t, 98 __cref_t<_Tp>>>); 99 static_assert( 100 nothrow_tag_invocable<execute_may_block_caller_t, __cref_t<_Tp>>); 101 return tag_invoke(execute_may_block_caller_t{}, std::as_const(__t)); 102 } 103 104 constexpr bool operator()(auto&&) const noexcept 105 { 106 return true; 107 } 108 }; 109 110 struct get_forward_progress_guarantee_t : 111 __query<get_forward_progress_guarantee_t> 112 { 113 template <class _Tp> 114 requires tag_invocable<get_forward_progress_guarantee_t, __cref_t<_Tp>> 115 constexpr auto operator()(_Tp&& __t) const noexcept( 116 nothrow_tag_invocable<get_forward_progress_guarantee_t, __cref_t<_Tp>>) 117 -> tag_invoke_result_t<get_forward_progress_guarantee_t, __cref_t<_Tp>> 118 { 119 return tag_invoke(get_forward_progress_guarantee_t{}, 120 std::as_const(__t)); 121 } 122 123 constexpr stdexec::forward_progress_guarantee 124 operator()(auto&&) const noexcept 125 { 126 return stdexec::forward_progress_guarantee::weakly_parallel; 127 } 128 }; 129 130 struct __has_algorithm_customizations_t : 131 __query<__has_algorithm_customizations_t> 132 { 133 template <class _Tp> 134 using __result_t = 135 tag_invoke_result_t<__has_algorithm_customizations_t, __cref_t<_Tp>>; 136 137 template <class _Tp> 138 requires tag_invocable<__has_algorithm_customizations_t, __cref_t<_Tp>> 139 constexpr __result_t<_Tp> operator()(_Tp&&) const 140 noexcept(noexcept(__result_t<_Tp>{})) 141 { 142 using _Boolean = tag_invoke_result_t<__has_algorithm_customizations_t, 143 __cref_t<_Tp>>; 144 static_assert(_Boolean{} 145 ? true 146 : true); // must be contextually convertible to bool 147 return _Boolean{}; 148 } 149 150 constexpr std::false_type operator()(auto&&) const noexcept 151 { 152 return {}; 153 } 154 }; 155 156 // TODO: implement allocator concept 157 template <class _T0> 158 concept __allocator_c = true; 159 160 struct get_scheduler_t : __query<get_scheduler_t> 161 { 162 friend constexpr bool tag_invoke(forwarding_query_t, 163 const get_scheduler_t&) noexcept 164 { 165 return true; 166 } 167 168 template <class _Env> 169 requires tag_invocable<get_scheduler_t, const _Env&> 170 auto operator()(const _Env& __env) const noexcept 171 -> tag_invoke_result_t<get_scheduler_t, const _Env&>; 172 173 auto operator()() const noexcept; 174 }; 175 176 struct get_delegatee_scheduler_t : __query<get_delegatee_scheduler_t> 177 { 178 friend constexpr bool tag_invoke(forwarding_query_t, 179 const get_delegatee_scheduler_t&) noexcept 180 { 181 return true; 182 } 183 184 template <class _Env> 185 requires tag_invocable<get_delegatee_scheduler_t, const _Env&> 186 auto operator()(const _Env& __t) const noexcept 187 -> tag_invoke_result_t<get_delegatee_scheduler_t, const _Env&>; 188 189 auto operator()() const noexcept; 190 }; 191 192 struct get_allocator_t : __query<get_allocator_t> 193 { 194 friend constexpr bool tag_invoke(forwarding_query_t, 195 const get_allocator_t&) noexcept 196 { 197 return true; 198 } 199 200 template <class _Env> 201 requires tag_invocable<get_allocator_t, const _Env&> 202 auto operator()(const _Env& __env) const noexcept 203 -> tag_invoke_result_t<get_allocator_t, const _Env&> 204 { 205 static_assert(nothrow_tag_invocable<get_allocator_t, const _Env&>); 206 static_assert( 207 __allocator_c<tag_invoke_result_t<get_allocator_t, const _Env&>>); 208 return tag_invoke(get_allocator_t{}, __env); 209 } 210 211 auto operator()() const noexcept; 212 }; 213 214 struct get_stop_token_t : __query<get_stop_token_t> 215 { 216 friend constexpr bool tag_invoke(forwarding_query_t, 217 const get_stop_token_t&) noexcept 218 { 219 return true; 220 } 221 222 template <class _Env> 223 never_stop_token operator()(const _Env&) const noexcept 224 { 225 return {}; 226 } 227 228 template <class _Env> 229 requires tag_invocable<get_stop_token_t, const _Env&> 230 auto operator()(const _Env& __env) const noexcept 231 -> tag_invoke_result_t<get_stop_token_t, const _Env&> 232 { 233 static_assert(nothrow_tag_invocable<get_stop_token_t, const _Env&>); 234 static_assert(stoppable_token< 235 tag_invoke_result_t<get_stop_token_t, const _Env&>>); 236 return tag_invoke(get_stop_token_t{}, __env); 237 } 238 239 auto operator()() const noexcept; 240 }; 241 242 template <class _Queryable, class _CPO> 243 concept __has_completion_scheduler_for = 244 queryable<_Queryable> && // 245 tag_invocable<get_completion_scheduler_t<_CPO>, const _Queryable&>; 246 247 template <__completion_tag _CPO> 248 struct get_completion_scheduler_t : __query<get_completion_scheduler_t<_CPO>> 249 { 250 friend constexpr bool 251 tag_invoke(forwarding_query_t, 252 const get_completion_scheduler_t<_CPO>&) noexcept 253 { 254 return true; 255 } 256 257 template <__has_completion_scheduler_for<_CPO> _Queryable> 258 auto operator()(const _Queryable& __queryable) const noexcept 259 -> tag_invoke_result_t<get_completion_scheduler_t<_CPO>, 260 const _Queryable&>; 261 }; 262 263 struct get_domain_t 264 { 265 template <class _Ty> 266 requires tag_invocable<get_domain_t, const _Ty&> 267 constexpr auto operator()(const _Ty& __ty) const noexcept 268 -> tag_invoke_result_t<get_domain_t, const _Ty&> 269 { 270 static_assert(nothrow_tag_invocable<get_domain_t, const _Ty&>, 271 "Customizations of get_domain must be noexcept."); 272 static_assert(__class<tag_invoke_result_t<get_domain_t, const _Ty&>>, 273 "Customizations of get_domain must return a class type."); 274 return tag_invoke(get_domain_t{}, __ty); 275 } 276 277 friend constexpr bool tag_invoke(forwarding_query_t, get_domain_t) noexcept 278 { 279 return true; 280 } 281 }; 282 } // namespace __queries 283 284 using __queries::__has_algorithm_customizations_t; 285 using __queries::execute_may_block_caller_t; 286 using __queries::forwarding_query_t; 287 using __queries::get_allocator_t; 288 using __queries::get_completion_scheduler_t; 289 using __queries::get_delegatee_scheduler_t; 290 using __queries::get_domain_t; 291 using __queries::get_forward_progress_guarantee_t; 292 using __queries::get_scheduler_t; 293 using __queries::get_stop_token_t; 294 using __queries::query_or_t; 295 296 inline constexpr forwarding_query_t forwarding_query{}; 297 inline constexpr query_or_t query_or{}; // NOT TO SPEC 298 inline constexpr execute_may_block_caller_t execute_may_block_caller{}; 299 inline constexpr __has_algorithm_customizations_t 300 __has_algorithm_customizations{}; 301 inline constexpr get_forward_progress_guarantee_t 302 get_forward_progress_guarantee{}; 303 inline constexpr get_scheduler_t get_scheduler{}; 304 inline constexpr get_delegatee_scheduler_t get_delegatee_scheduler{}; 305 inline constexpr get_allocator_t get_allocator{}; 306 inline constexpr get_stop_token_t get_stop_token{}; 307 #if !STDEXEC_GCC() || defined(__OPTIMIZE_SIZE__) 308 template <__completion_tag _CPO> 309 inline constexpr get_completion_scheduler_t<_CPO> get_completion_scheduler{}; 310 #else 311 template <> 312 inline constexpr get_completion_scheduler_t<set_value_t> 313 get_completion_scheduler<set_value_t>{}; 314 template <> 315 inline constexpr get_completion_scheduler_t<set_error_t> 316 get_completion_scheduler<set_error_t>{}; 317 template <> 318 inline constexpr get_completion_scheduler_t<set_stopped_t> 319 get_completion_scheduler<set_stopped_t>{}; 320 #endif 321 322 template <class _Tag> 323 concept __forwarding_query = forwarding_query(_Tag{}); 324 325 inline constexpr get_domain_t get_domain{}; 326 327 template <class _Tag, class _Queryable, class _Default> 328 using __query_result_or_t = 329 __call_result_t<query_or_t, _Tag, _Queryable, _Default>; 330 331 ///////////////////////////////////////////////////////////////////////////// 332 namespace __env 333 { 334 // For getting an execution environment from a receiver, 335 // or the attributes from a sender. 336 struct get_env_t 337 { 338 template <class _EnvProvider> 339 requires tag_invocable<get_env_t, const _EnvProvider&> 340 STDEXEC_ATTRIBUTE((always_inline)) // 341 constexpr auto 342 operator()(const _EnvProvider& __env_provider) const noexcept 343 -> tag_invoke_result_t<get_env_t, const _EnvProvider&> 344 { 345 static_assert( 346 queryable<tag_invoke_result_t<get_env_t, const _EnvProvider&>>); 347 static_assert(nothrow_tag_invocable<get_env_t, const _EnvProvider&>); 348 return tag_invoke(*this, __env_provider); 349 } 350 351 template <class _EnvProvider> 352 constexpr empty_env operator()(const _EnvProvider&) const noexcept 353 { 354 return {}; 355 } 356 }; 357 358 // To be kept in sync with the promise type used in __connect_awaitable 359 template <class _Env> 360 struct __promise 361 { 362 template <class _Ty> 363 _Ty&& await_transform(_Ty&& __value) noexcept 364 { 365 return (_Ty&&)__value; 366 } 367 368 template <class _Ty> 369 requires tag_invocable<as_awaitable_t, _Ty, __promise&> 370 auto await_transform(_Ty&& __value) // 371 noexcept(nothrow_tag_invocable<as_awaitable_t, _Ty, __promise&>) 372 -> tag_invoke_result_t<as_awaitable_t, _Ty, __promise&> 373 { 374 return tag_invoke(as_awaitable, (_Ty&&)__value, *this); 375 } 376 377 template <same_as<get_env_t> _Tag> 378 friend auto tag_invoke(_Tag, const __promise&) noexcept -> const _Env& 379 { 380 std::terminate(); 381 } 382 }; 383 384 template <class _Value, class _Tag, class... _Tags> 385 struct __with 386 { 387 using __t = __with; 388 using __id = __with; 389 STDEXEC_ATTRIBUTE((no_unique_address)) _Value __value_; 390 391 __with() = default; 392 393 constexpr explicit __with(_Value __value) noexcept( 394 __nothrow_decay_copyable<_Value>) : 395 __value_((_Value&&)__value) 396 {} 397 398 constexpr explicit __with(_Value __value, _Tag, _Tags...) noexcept( 399 __nothrow_decay_copyable<_Value>) : 400 __value_((_Value&&)__value) 401 {} 402 403 template <__one_of<_Tag, _Tags...> _Key> 404 friend auto tag_invoke(_Key, const __with& __self) // 405 noexcept(__nothrow_decay_copyable<_Value>) -> _Value 406 { 407 return __self.__value_; 408 } 409 }; 410 411 template <class _Value, class _Tag, class... _Tags> 412 __with(_Value, _Tag, _Tags...) -> __with<_Value, _Tag, _Tags...>; 413 414 template <class _Env> 415 struct __fwd 416 { 417 static_assert(__nothrow_move_constructible<_Env>); 418 using __t = __fwd; 419 using __id = __fwd; 420 STDEXEC_ATTRIBUTE((no_unique_address)) _Env __env_; 421 422 template <__forwarding_query _Tag> 423 requires tag_invocable<_Tag, const _Env&> 424 friend auto tag_invoke(_Tag, const __fwd& __self) // 425 noexcept(nothrow_tag_invocable<_Tag, const _Env&>) 426 -> tag_invoke_result_t<_Tag, const _Env&> 427 { 428 return _Tag()(__self.__env_); 429 } 430 }; 431 432 template <class _Env> 433 __fwd(_Env&&) -> __fwd<_Env>; 434 435 template <class _Env> 436 struct __ref 437 { 438 using __t = __ref; 439 using __id = __ref; 440 const _Env& __env_; 441 442 template <class _Tag> 443 requires tag_invocable<_Tag, const _Env&> 444 friend auto tag_invoke(_Tag, const __ref& __self) // 445 noexcept(nothrow_tag_invocable<_Tag, const _Env&>) 446 -> tag_invoke_result_t<_Tag, const _Env&> 447 { 448 return _Tag()(__self.__env_); 449 } 450 }; 451 452 template <class _Env> 453 __ref(_Env&) -> __ref<_Env>; 454 455 struct __ref_fn 456 { 457 template <class _Env> 458 constexpr auto operator()(_Env&& __env) const 459 { 460 if constexpr (same_as<_Env, _Env&>) 461 { 462 return __ref{(_Env&&)__env}; 463 } 464 else 465 { 466 return (_Env&&)__env; 467 } 468 } 469 }; 470 471 template <class _Env, class _Tag, class... _Tags> 472 struct __without_ : _Env 473 { 474 static_assert(__nothrow_move_constructible<_Env>); 475 using __t = __without_; 476 using __id = __without_; 477 478 constexpr explicit __without_(_Env&& __env, _Tag, _Tags...) noexcept : 479 _Env((_Env&&)__env) 480 {} 481 482 template <__one_of<_Tag, _Tags...> _Key, class _Self> 483 requires(std::is_base_of_v<__without_, __decay_t<_Self>>) 484 friend auto tag_invoke(_Key, _Self&&) noexcept = delete; 485 }; 486 487 struct __without_fn 488 { 489 template <class _Env, class _Tag, class... _Tags> 490 constexpr decltype(auto) operator()(_Env&& __env, _Tag, 491 _Tags...) const noexcept 492 { 493 if constexpr (tag_invocable<_Tag, _Env> || 494 (tag_invocable<_Tags, _Env> || ...)) 495 { 496 return __without_{__ref_fn()((_Env&&)__env), _Tag(), _Tags()...}; 497 } 498 else 499 { 500 return static_cast<_Env>((_Env&&)__env); 501 } 502 } 503 }; 504 505 inline constexpr __without_fn __without{}; 506 507 template <class _Env, class _Tag, class... _Tags> 508 using __without_t = __result_of<__without, _Env, _Tag, _Tags...>; 509 510 template <class _Second, class _First> 511 struct __joined : _Second 512 { 513 static_assert(__nothrow_move_constructible<_First>); 514 static_assert(__nothrow_move_constructible<_Second>); 515 using __t = __joined; 516 using __id = __joined; 517 518 STDEXEC_ATTRIBUTE((no_unique_address)) _First __env_; 519 520 template <class _Tag> 521 requires tag_invocable<_Tag, const _First&> 522 friend auto tag_invoke(_Tag, const __joined& __self) // 523 noexcept(nothrow_tag_invocable<_Tag, const _First&>) 524 -> tag_invoke_result_t<_Tag, const _First&> 525 { 526 return _Tag()(__self.__env_); 527 } 528 }; 529 530 template <class _Second, class _First> 531 __joined(_Second&&, _First&&) -> __joined<_Second, _First>; 532 533 template <__nothrow_move_constructible _Fun> 534 struct __from 535 { 536 using __t = __from; 537 using __id = __from; 538 STDEXEC_ATTRIBUTE((no_unique_address)) _Fun __fun_; 539 540 template <class _Tag> 541 requires __callable<const _Fun&, _Tag> 542 friend auto tag_invoke(_Tag, const __from& __self) // 543 noexcept(__nothrow_callable<const _Fun&, _Tag>) 544 -> __call_result_t<const _Fun&, _Tag> 545 { 546 return __self.__fun_(_Tag()); 547 } 548 }; 549 550 template <class _Fun> 551 __from(_Fun) -> __from<_Fun>; 552 553 struct __fwd_fn 554 { 555 template <class Env> 556 auto operator()(Env&& env) const 557 { 558 return __fwd{(Env&&)env}; 559 } 560 561 empty_env operator()(empty_env) const 562 { 563 return {}; 564 } 565 }; 566 567 struct __join_fn 568 { 569 empty_env operator()() const 570 { 571 return {}; 572 } 573 574 template <class _Env> 575 _Env operator()(_Env&& __env) const 576 { 577 return (_Env&&)__env; 578 } 579 580 empty_env operator()(empty_env) const 581 { 582 return {}; 583 } 584 585 template <class _Env> 586 _Env operator()(_Env&& __env, empty_env) const 587 { 588 return (_Env&&)__env; 589 } 590 591 empty_env operator()(empty_env, empty_env) const 592 { 593 return {}; 594 } 595 596 template <class... Rest> 597 decltype(auto) operator()(empty_env, Rest&&... rest) const 598 { 599 return __fwd_fn()(__join_fn()((Rest&&)rest...)); 600 } 601 602 template <class First, class... Rest> 603 decltype(auto) operator()(First&& first, Rest&&... rest) const 604 { 605 return __joined{__fwd_fn()(__join_fn()((Rest&&)rest...)), 606 (First&&)first}; 607 } 608 }; 609 610 inline constexpr __join_fn __join{}; 611 612 template <class... _Envs> 613 using __join_t = __result_of<__join, _Envs...>; 614 } // namespace __env 615 616 using __env::empty_env; 617 using __env::get_env_t; 618 inline constexpr get_env_t get_env{}; 619 620 template <class _EnvProvider> 621 concept environment_provider = // 622 requires(_EnvProvider& __ep) { 623 { 624 get_env(std::as_const(__ep)) 625 } -> queryable; 626 }; 627 } // namespace stdexec 628 629 STDEXEC_PRAGMA_POP() 630