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 "__concepts.hpp"
19 #include "__cpo.hpp"
20 #include "__execution_fwd.hpp"
21 #include "__meta.hpp"
22 #include "__stop_token.hpp"
23 #include "__tag_invoke.hpp"
24 #include "__tuple.hpp"
25 
26 #include <exception>
27 #include <functional>
28 #include <type_traits>
29 
30 STDEXEC_PRAGMA_PUSH()
31 STDEXEC_PRAGMA_IGNORE_EDG(probable_guiding_friend)
32 STDEXEC_PRAGMA_IGNORE_EDG(type_qualifiers_ignored_on_reference)
33 
34 namespace stdexec
35 {
36 // [exec.queries.queryable]
37 template <class T>
38 concept queryable = destructible<T>;
39 
40 template <class Tag>
41 struct __query
42 {
43     template <class Sig>
44     static inline constexpr Tag (*signature)(Sig) = nullptr;
45 };
46 
47 //////////////////////////////////////////////////////////////////////////////////////////////////
48 // [exec.queries]
49 namespace __queries
50 {
51 template <class _Tp>
52 concept __is_bool_constant = //
53     requires {               //
54         typename __mbool<_Tp::value>;
55     };
56 
57 struct forwarding_query_t
58 {
59     template <class _Query>
60     consteval auto operator()(_Query __query) const noexcept -> bool
61     {
62         if constexpr (tag_invocable<forwarding_query_t, _Query>)
63         {
64             using __result_t = tag_invoke_result_t<forwarding_query_t, _Query>;
65             // If this a integral type wrapper, unpack it and return the value.
66             // Otherwise, return the result of the tag_invoke call expression.
67             if constexpr (__is_bool_constant<__result_t>)
68             {
69                 return __result_t::value;
70             }
71             else
72             {
73                 return tag_invoke(*this, static_cast<_Query&&>(__query));
74             }
75         }
76         else if constexpr (derived_from<_Query, forwarding_query_t>)
77         {
78             return true;
79         }
80         else
81         {
82             return false;
83         }
84     }
85 };
86 
87 struct query_or_t
88 {
89     template <class _Query, class _Queryable, class _Default>
90     constexpr auto operator()(_Query, _Queryable&&, _Default&& __default) const
91         noexcept(__nothrow_constructible_from<_Default, _Default&&>) -> _Default
92     {
93         return static_cast<_Default&&>(__default);
94     }
95 
96     template <class _Query, class _Queryable, class _Default>
97         requires __callable<_Query, _Queryable>
98     constexpr auto operator()(_Query __query, _Queryable&& __queryable,
99                               _Default&&) const
100         noexcept(__nothrow_callable<_Query, _Queryable>)
101             -> __call_result_t<_Query, _Queryable>
102     {
103         return static_cast<_Query&&>(__query)(
104             static_cast<_Queryable&&>(__queryable));
105     }
106 };
107 
108 struct execute_may_block_caller_t : __query<execute_may_block_caller_t>
109 {
110     template <class _Tp>
111         requires tag_invocable<execute_may_block_caller_t, __cref_t<_Tp>>
112     constexpr auto operator()(_Tp&& __t) const noexcept -> bool
113     {
114         static_assert(
115             same_as<bool, tag_invoke_result_t<execute_may_block_caller_t,
116                                               __cref_t<_Tp>>>);
117         static_assert(
118             nothrow_tag_invocable<execute_may_block_caller_t, __cref_t<_Tp>>);
119         return tag_invoke(execute_may_block_caller_t{}, std::as_const(__t));
120     }
121 
122     constexpr auto operator()(auto&&) const noexcept -> bool
123     {
124         return true;
125     }
126 };
127 
128 struct get_forward_progress_guarantee_t :
129     __query<get_forward_progress_guarantee_t>
130 {
131     template <class _Tp>
132         requires tag_invocable<get_forward_progress_guarantee_t, __cref_t<_Tp>>
133     constexpr auto operator()(_Tp&& __t) const noexcept(
134         nothrow_tag_invocable<get_forward_progress_guarantee_t, __cref_t<_Tp>>)
135         -> __decay_t<tag_invoke_result_t<get_forward_progress_guarantee_t,
136                                          __cref_t<_Tp>>>
137     {
138         return tag_invoke(get_forward_progress_guarantee_t{},
139                           std::as_const(__t));
140     }
141 
142     constexpr auto
143         operator()(auto&&) const noexcept -> stdexec::forward_progress_guarantee
144     {
145         return stdexec::forward_progress_guarantee::weakly_parallel;
146     }
147 };
148 
149 struct __has_algorithm_customizations_t :
150     __query<__has_algorithm_customizations_t>
151 {
152     template <class _Tp>
153     using __result_t =
154         tag_invoke_result_t<__has_algorithm_customizations_t, __cref_t<_Tp>>;
155 
156     template <class _Tp>
157         requires tag_invocable<__has_algorithm_customizations_t, __cref_t<_Tp>>
158     constexpr auto operator()(_Tp&&) const
159         noexcept(noexcept(__result_t<_Tp>{})) -> __result_t<_Tp>
160     {
161         using _Boolean = tag_invoke_result_t<__has_algorithm_customizations_t,
162                                              __cref_t<_Tp>>;
163         static_assert(_Boolean{}
164                           ? true
165                           : false); // must be contextually convertible to bool
166         return _Boolean{};
167     }
168 
169     constexpr auto operator()(auto&&) const noexcept -> std::false_type
170     {
171         return {};
172     }
173 };
174 
175 // TODO: implement allocator concept
176 template <class _T0>
177 concept __allocator_c = true;
178 
179 struct get_scheduler_t : __query<get_scheduler_t>
180 {
181     static constexpr auto query(forwarding_query_t) noexcept -> bool
182     {
183         return true;
184     }
185 
186     template <class _Env>
187         requires tag_invocable<get_scheduler_t, const _Env&>
188     auto operator()(const _Env& __env) const noexcept
189         -> tag_invoke_result_t<get_scheduler_t, const _Env&>;
190 
191     template <class _Tag = get_scheduler_t>
192     auto operator()() const noexcept;
193 };
194 
195 //! The type for `get_delegation_scheduler` [exec.get.delegation.scheduler]
196 //! A query object that asks for a scheduler that can be used to delegate
197 //! work to for the purpose of forward progress delegation ([intro.progress]).
198 struct get_delegation_scheduler_t : __query<get_delegation_scheduler_t>
199 {
200     static constexpr auto query(forwarding_query_t) noexcept -> bool
201     {
202         return true;
203     }
204 
205     template <class _Env>
206         requires tag_invocable<get_delegation_scheduler_t, const _Env&>
207     auto operator()(const _Env& __t) const noexcept
208         -> tag_invoke_result_t<get_delegation_scheduler_t, const _Env&>;
209 
210     template <class _Tag = get_delegation_scheduler_t>
211     auto operator()() const noexcept;
212 };
213 
214 struct get_allocator_t : __query<get_allocator_t>
215 {
216     static constexpr auto query(forwarding_query_t) noexcept -> bool
217     {
218         return true;
219     }
220 
221     template <class _Env>
222         requires tag_invocable<get_allocator_t, const _Env&>
223     auto operator()(const _Env& __env) const noexcept
224         -> tag_invoke_result_t<get_allocator_t, const _Env&>
225     {
226         static_assert(nothrow_tag_invocable<get_allocator_t, const _Env&>);
227         static_assert(
228             __allocator_c<tag_invoke_result_t<get_allocator_t, const _Env&>>);
229         return tag_invoke(get_allocator_t{}, __env);
230     }
231 
232     template <class _Tag = get_allocator_t>
233     auto operator()() const noexcept;
234 };
235 
236 struct get_stop_token_t : __query<get_stop_token_t>
237 {
238     static constexpr auto query(forwarding_query_t) noexcept -> bool
239     {
240         return true;
241     }
242 
243     template <class _Env, class _Token = never_stop_token>
244     auto operator()(const _Env&) const noexcept -> _Token
245     {
246         return {};
247     }
248 
249     template <class _Env, class = void>
250         requires tag_invocable<get_stop_token_t, const _Env&>
251     auto operator()(const _Env& __env) const noexcept
252         -> tag_invoke_result_t<get_stop_token_t, const _Env&>
253     {
254         static_assert(nothrow_tag_invocable<get_stop_token_t, const _Env&>);
255         static_assert(
256             stoppable_token<
257                 __decay_t<tag_invoke_result_t<get_stop_token_t, const _Env&>>>);
258         return tag_invoke(get_stop_token_t{}, __env);
259     }
260 
261     template <class _Tag = get_stop_token_t>
262     auto operator()() const noexcept;
263 };
264 
265 template <class _Queryable, class _Tag>
266 concept __has_completion_scheduler_for =
267     queryable<_Queryable> && //
268     tag_invocable<get_completion_scheduler_t<_Tag>, const _Queryable&>;
269 
270 template <__completion_tag _Tag>
271 struct get_completion_scheduler_t : __query<get_completion_scheduler_t<_Tag>>
272 {
273     static constexpr auto query(forwarding_query_t) noexcept -> bool
274     {
275         return true;
276     }
277 
278     template <__has_completion_scheduler_for<_Tag> _Queryable>
279     auto operator()(const _Queryable& __queryable) const noexcept
280         -> tag_invoke_result_t<get_completion_scheduler_t<_Tag>,
281                                const _Queryable&>;
282 };
283 
284 struct get_domain_t
285 {
286     template <class _Ty>
287         requires tag_invocable<get_domain_t, const _Ty&>
288     constexpr auto operator()(const _Ty& __ty) const noexcept
289         -> __decay_t<tag_invoke_result_t<get_domain_t, const _Ty&>>
290     {
291         static_assert(nothrow_tag_invocable<get_domain_t, const _Ty&>,
292                       "Customizations of get_domain must be noexcept.");
293         static_assert(
294             __class<__decay_t<tag_invoke_result_t<get_domain_t, const _Ty&>>>,
295             "Customizations of get_domain must return a class type.");
296         return {};
297     }
298 
299     static constexpr auto query(forwarding_query_t) noexcept -> bool
300     {
301         return true;
302     }
303 };
304 
305 struct __is_scheduler_affine_t
306 {
307     template <class _Env>
308     constexpr auto operator()(const _Env&) const noexcept
309     {
310         if constexpr (tag_invocable<__is_scheduler_affine_t, const _Env&>)
311         {
312             using _Result =
313                 tag_invoke_result_t<__is_scheduler_affine_t, const _Env&>;
314             static_assert(__same_as<decltype(__v<_Result>), const bool>);
315             return _Result();
316         }
317         else
318         {
319             return std::false_type();
320         }
321     }
322 
323     static constexpr auto query(forwarding_query_t) noexcept -> bool
324     {
325         return false;
326     }
327 };
328 
329 struct __root_t
330 {
331     template <class _Env>
332         requires tag_invocable<__root_t, const _Env&>
333     constexpr auto operator()(const _Env& __env) const noexcept -> bool
334     {
335         STDEXEC_ASSERT(tag_invoke(__root_t{}, __env) == true);
336         return true;
337     }
338 
339     static constexpr auto query(forwarding_query_t) noexcept -> bool
340     {
341         return false;
342     }
343 };
344 
345 struct __root_env
346 {
347     using __t = __root_env;
348     using __id = __root_env;
349 
350     constexpr STDEXEC_MEMFN_DECL(auto __root)(this const __root_env&) noexcept
351         -> bool
352     {
353         return true;
354     }
355 };
356 } // namespace __queries
357 
358 using __queries::__has_algorithm_customizations_t;
359 using __queries::execute_may_block_caller_t;
360 using __queries::forwarding_query_t;
361 using __queries::get_allocator_t;
362 using __queries::get_delegation_scheduler_t;
363 using __queries::get_forward_progress_guarantee_t;
364 using __queries::get_scheduler_t;
365 using __queries::query_or_t;
366 using get_delegatee_scheduler_t [[deprecated(
367     "get_delegatee_scheduler_t has been renamed get_delegation_scheduler_t")]] =
368     get_delegation_scheduler_t;
369 using __queries::__is_scheduler_affine_t;
370 using __queries::__root_env;
371 using __queries::__root_t;
372 using __queries::get_completion_scheduler_t;
373 using __queries::get_domain_t;
374 using __queries::get_stop_token_t;
375 
376 inline constexpr forwarding_query_t forwarding_query{};
377 inline constexpr query_or_t query_or{}; // NOT TO SPEC
378 inline constexpr execute_may_block_caller_t execute_may_block_caller{};
379 inline constexpr __has_algorithm_customizations_t
380     __has_algorithm_customizations{};
381 inline constexpr get_forward_progress_guarantee_t
382     get_forward_progress_guarantee{};
383 inline constexpr get_scheduler_t get_scheduler{};
384 inline constexpr get_delegation_scheduler_t get_delegation_scheduler{};
385 inline constexpr auto& get_delegatee_scheduler [[deprecated(
386     "get_delegatee_scheduler has been renamed get_delegation_scheduler")]] =
387     get_delegation_scheduler;
388 inline constexpr get_allocator_t get_allocator{};
389 inline constexpr get_stop_token_t get_stop_token{};
390 #if !STDEXEC_GCC() || defined(__OPTIMIZE_SIZE__)
391 template <__completion_tag _Tag>
392 inline constexpr get_completion_scheduler_t<_Tag> get_completion_scheduler{};
393 #else
394 template <>
395 inline constexpr get_completion_scheduler_t<set_value_t>
396     get_completion_scheduler<set_value_t>{};
397 template <>
398 inline constexpr get_completion_scheduler_t<set_error_t>
399     get_completion_scheduler<set_error_t>{};
400 template <>
401 inline constexpr get_completion_scheduler_t<set_stopped_t>
402     get_completion_scheduler<set_stopped_t>{};
403 #endif
404 
405 template <class _Tag>
406 concept __forwarding_query = forwarding_query(_Tag{});
407 
408 inline constexpr get_domain_t get_domain{};
409 
410 template <class _Env>
411 using __domain_of_t = __decay_t<__call_result_t<get_domain_t, _Env>>;
412 
413 template <class _Tag, class _Queryable, class _Default>
414 using __query_result_or_t =
415     __call_result_t<query_or_t, _Tag, _Queryable, _Default>;
416 
417 namespace __env
418 {
419 // To be kept in sync with the promise type used in __connect_awaitable
420 template <class _Env>
421 struct __promise
422 {
423     template <class _Ty>
424     auto await_transform(_Ty&& __value) noexcept -> _Ty&&
425     {
426         return static_cast<_Ty&&>(__value);
427     }
428 
429     template <class _Ty>
430         requires tag_invocable<as_awaitable_t, _Ty, __promise&>
431     auto await_transform(_Ty&& __value) //
432         noexcept(nothrow_tag_invocable<as_awaitable_t, _Ty, __promise&>)
433             -> tag_invoke_result_t<as_awaitable_t, _Ty, __promise&>
434     {
435         return tag_invoke(as_awaitable, static_cast<_Ty&&>(__value), *this);
436     }
437 
438     auto get_env() const noexcept -> const _Env&;
439 };
440 
441 template <class _Env, class _Query, class... _Args>
442 concept __queryable = //
443     tag_invocable<_Query, const _Env&, _Args...>;
444 
445 template <class _Env, class _Query, class... _Args>
446 concept __nothrow_queryable = //
447     nothrow_tag_invocable<_Query, const _Env&, _Args...>;
448 
449 template <class _Env, class _Query, class... _Args>
450 using __query_result_t = //
451     tag_invoke_result_t<_Query, const _Env&, _Args...>;
452 
453 // A singleton environment from a query/value pair
454 template <class _Query, class _Value>
455 struct prop
456 {
457     using __t = prop;
458     using __id = prop;
459 
460     STDEXEC_ATTRIBUTE((no_unique_address))
461     _Query __query;
462 
463     STDEXEC_ATTRIBUTE((no_unique_address))
464     _Value __value;
465 
466     STDEXEC_ATTRIBUTE((nodiscard))
467     constexpr const _Value& query(_Query) const noexcept
468     {
469         return __value;
470     }
471 
472     prop& operator=(const prop&) = delete;
473 };
474 
475 template <class _Query, class _Value>
476 prop(_Query, _Value) -> prop<_Query, std::unwrap_reference_t<_Value>>;
477 
478 // utility for joining multiple environments
479 template <class... _Envs>
480 struct env
481 {
482     using __t = env;
483     using __id = env;
484 
485     __tuple_for<_Envs...> __tup_;
486 
487     // return a reference to the first child env for which
488     // __queryable<_Envs, _Query, _Args...> is true.
489     template <class _Query, class... _Args>
490     STDEXEC_ATTRIBUTE((always_inline))
491     constexpr decltype(auto) __get_1st() const noexcept
492     {
493         constexpr bool __flags[] = {__queryable<_Envs, _Query, _Args...>...};
494         constexpr std::size_t __idx =
495             __pos_of(__flags, __flags + sizeof...(_Envs));
496         return __tup::get<__idx>(__tup_);
497     }
498 
499     template <class _Query, class... _Args>
500         requires(__queryable<_Envs, _Query, _Args...> || ...)
501     STDEXEC_ATTRIBUTE((always_inline))
502     constexpr decltype(auto) query(_Query __q, _Args&&... __args) const
503         noexcept(__nothrow_queryable<decltype(__get_1st<_Query, _Args...>()),
504                                      _Query, _Args...>)
505     {
506         return tag_invoke(__q, __get_1st<_Query, _Args...>(),
507                           static_cast<_Args&&>(__args)...);
508     }
509 
510     env& operator=(const env&) = delete;
511 };
512 
513 // specialization for two envs to avoid warnings about elided braces
514 template <class _Env0, class _Env1>
515 struct env<_Env0, _Env1>
516 {
517     using __t = env;
518     using __id = env;
519 
520     STDEXEC_ATTRIBUTE((no_unique_address))
521     _Env0 __env0_;
522     STDEXEC_ATTRIBUTE((no_unique_address))
523     _Env1 __env1_;
524 
525     // return a reference to the first child env for which
526     // __queryable<_Envs, _Query, _Args...> is true.
527     template <class _Query, class... _Args>
528     STDEXEC_ATTRIBUTE((always_inline))
529     constexpr decltype(auto) __get_1st() const noexcept
530     {
531         if constexpr (__queryable<_Env0, _Query, _Args...>)
532         {
533             return (__env0_);
534         }
535         else
536         {
537             return (__env1_);
538         }
539     }
540 
541     template <class _Query, class... _Args>
542         requires __queryable<_Env0, _Query, _Args...> ||
543                  __queryable<_Env1, _Query, _Args...>
544     STDEXEC_ATTRIBUTE((always_inline))
545     constexpr decltype(auto) query(_Query __q, _Args&&... __args) const
546         noexcept(__nothrow_queryable<decltype(__get_1st<_Query, _Args...>()),
547                                      _Query, _Args...>)
548     {
549         return tag_invoke(__q, __get_1st<_Query, _Args...>(),
550                           static_cast<_Args&&>(__args)...);
551     }
552 
553     env& operator=(const env&) = delete;
554 };
555 
556 template <class... _Envs>
557 env(_Envs...) -> env<std::unwrap_reference_t<_Envs>...>;
558 
559 template <class _Value, class _Tag, class... _Tags>
560 struct __with
561 {
562     using __t = __with;
563     using __id = __with;
564     STDEXEC_ATTRIBUTE((no_unique_address))
565     _Value __value_;
566 
567     __with() = default;
568 
569     constexpr explicit __with(_Value __value) noexcept(
570         __nothrow_decay_copyable<_Value>) :
571         __value_(static_cast<_Value&&>(__value))
572     {}
573 
574     constexpr explicit __with(_Value __value, _Tag, _Tags...) noexcept(
575         __nothrow_decay_copyable<_Value>) :
576         __value_(static_cast<_Value&&>(__value))
577     {}
578 
579     template <__one_of<_Tag, _Tags...> _Key>
580     auto query(_Key) const noexcept -> const _Value&
581     {
582         return __value_;
583     }
584 
585     __with& operator=(const __with&) = delete;
586 };
587 
588 template <class _Value, class _Tag, class... _Tags>
589 __with(_Value, _Tag, _Tags...) -> __with<_Value, _Tag, _Tags...>;
590 
591 template <class _EnvId>
592 struct __fwd
593 {
594     using _Env = __cvref_t<_EnvId>;
595     static_assert(__nothrow_move_constructible<_Env>);
596 
597     struct __t
598     {
599         using __id = __fwd;
600         STDEXEC_ATTRIBUTE((no_unique_address))
601         _Env __env_;
602 
603 #if STDEXEC_GCC() && __GNUC__ < 12
604         using __cvref_env_t = std::add_const_t<_Env>&;
605 #else
606         using __cvref_env_t = const _Env&;
607 #endif
608 
609         template <__forwarding_query _Tag>
610             requires tag_invocable<_Tag, __cvref_env_t>
611         auto query(_Tag) const
612             noexcept(nothrow_tag_invocable<_Tag, __cvref_env_t>)
613                 -> tag_invoke_result_t<_Tag, __cvref_env_t>
614         {
615             return tag_invoke(_Tag(), __env_);
616         }
617 
618         __t& operator=(const __t&) = delete;
619     };
620 };
621 
622 struct __fwd_fn
623 {
624     template <class _Env>
625     auto operator()(_Env&& __env) const
626     {
627         return __t<__fwd<__cvref_id<_Env>>>{static_cast<_Env&&>(__env)};
628     }
629 
630     auto operator()(empty_env) const -> empty_env
631     {
632         return {};
633     }
634 };
635 
636 template <class _EnvId, class _Tag>
637 struct __without_
638 {
639     using _Env = __cvref_t<_EnvId>;
640     static_assert(__nothrow_move_constructible<_Env>);
641 
642     struct __t
643     {
644         using __id = __without_;
645         _Env __env_;
646 
647 #if STDEXEC_GCC() && __GNUC__ < 12
648         using __cvref_env_t = std::add_const_t<_Env>&;
649 #else
650         using __cvref_env_t = const _Env&;
651 #endif
652 
653         auto query(_Tag) const noexcept = delete;
654 
655         template <tag_invocable<__cvref_env_t> _Key>
656         STDEXEC_ATTRIBUTE((always_inline))
657         auto query(_Key) const noexcept(
658             nothrow_tag_invocable<_Key, __cvref_env_t>) -> decltype(auto)
659         {
660             return tag_invoke(_Key(), __env_);
661         }
662 
663         __t& operator=(const __t&) = delete;
664     };
665 };
666 
667 struct __without_fn
668 {
669     template <class _Env, class _Tag>
670     constexpr auto operator()(_Env&& __env,
671                               _Tag) const noexcept -> decltype(auto)
672     {
673         if constexpr (tag_invocable<_Tag, _Env>)
674         {
675             using _Without = __t<__without_<__cvref_id<_Env>, _Tag>>;
676             return _Without{static_cast<_Env&&>(__env)};
677         }
678         else
679         {
680             return static_cast<_Env>(static_cast<_Env&&>(__env));
681         }
682     }
683 };
684 
685 inline constexpr __without_fn __without{};
686 
687 template <class _Env, class _Tag, class... _Tags>
688 using __without_t = __result_of<__without, _Env, _Tag, _Tags...>;
689 
690 template <__nothrow_move_constructible _Fun>
691 struct __from
692 {
693     using __t = __from;
694     using __id = __from;
695     STDEXEC_ATTRIBUTE((no_unique_address))
696     _Fun __fun_;
697 
698     template <class _Tag>
699         requires __callable<const _Fun&, _Tag>
700     auto query(_Tag) const noexcept(__nothrow_callable<const _Fun&, _Tag>)
701         -> __call_result_t<const _Fun&, _Tag>
702     {
703         return __fun_(_Tag());
704     }
705 
706     __from& operator=(const __from&) = delete;
707 };
708 
709 template <class _Fun>
710 __from(_Fun) -> __from<_Fun>;
711 
712 struct __join_fn
713 {
714     auto operator()(empty_env, empty_env) const noexcept -> empty_env
715     {
716         return {};
717     }
718 
719     template <class _Env>
720     auto operator()(_Env&& __env, empty_env = {}) const noexcept -> _Env
721     {
722         return static_cast<_Env&&>(__env);
723     }
724 
725     template <class _Env>
726     auto operator()(empty_env, _Env&& __env) const noexcept -> decltype(auto)
727     {
728         return __fwd_fn()(static_cast<_Env&&>(__env));
729     }
730 
731     template <class _First, class _Second>
732     auto operator()(_First&& __first, _Second&& __second) const noexcept
733         -> env<_First, __call_result_t<__fwd_fn, _Second>>
734     {
735         return {static_cast<_First&&>(__first),
736                 __fwd_fn()(static_cast<_Second&&>(__second))};
737     }
738 };
739 
740 inline constexpr __join_fn __join{};
741 
742 template <class _First, class... _Second>
743 using __join_t = __result_of<__join, _First, _Second...>;
744 
745 struct __as_root_env_fn
746 {
747     template <class _Env>
748     constexpr auto operator()(_Env __env) const noexcept
749         -> __join_t<__root_env, std::unwrap_reference_t<_Env>>
750     {
751         return __join(__root_env{},
752                       static_cast<std::unwrap_reference_t<_Env>&&>(__env));
753     }
754 };
755 
756 inline constexpr __as_root_env_fn __as_root_env{};
757 
758 template <class _Env>
759 using __as_root_env_t = __result_of<__as_root_env, _Env>;
760 } // namespace __env
761 
762 using __env::env;
763 using __env::prop;
764 using empty_env = env<>;
765 
766 /////////////////////////////////////////////////////////////////////////////
767 namespace __get_env
768 {
769 // For getting an execution environment from a receiver or the attributes from a
770 // sender.
771 struct get_env_t
772 {
773     template <__same_as<get_env_t> _Self, class _EnvProvider>
774     STDEXEC_ATTRIBUTE((always_inline))
775     friend auto tag_invoke(_Self, const _EnvProvider& __env_provider) noexcept
776         -> decltype(__env_provider.get_env())
777     {
778         static_assert(noexcept(__env_provider.get_env()),
779                       "get_env() members must be noexcept");
780         return __env_provider.get_env();
781     }
782 
783     template <class _EnvProvider>
784         requires tag_invocable<get_env_t, const _EnvProvider&>
785     STDEXEC_ATTRIBUTE((always_inline))
786     constexpr auto operator()(const _EnvProvider& __env_provider) const noexcept
787         -> tag_invoke_result_t<get_env_t, const _EnvProvider&>
788     {
789         static_assert(
790             queryable<tag_invoke_result_t<get_env_t, const _EnvProvider&>>);
791         static_assert(nothrow_tag_invocable<get_env_t, const _EnvProvider&>);
792         return tag_invoke(*this, __env_provider);
793     }
794 
795     template <class _EnvProvider>
796     constexpr auto operator()(const _EnvProvider&) const noexcept -> empty_env
797     {
798         return {};
799     }
800 };
801 } // namespace __get_env
802 
803 using __get_env::get_env_t;
804 inline constexpr get_env_t get_env{};
805 
806 template <class _EnvProvider>
807 concept environment_provider = //
808     requires(_EnvProvider& __ep) {
809         { get_env(std::as_const(__ep)) } -> queryable;
810     };
811 
812 using __env::__as_root_env;
813 using __env::__as_root_env_t;
814 
815 template <class _Env>
816 concept __is_root_env = requires(_Env&& __env) {
817                             { __root_t{}(__env) } -> same_as<bool>;
818                         };
819 
820 template <class _Sender>
821 concept __is_scheduler_affine = //
822     requires {
823         requires __v<
824             __call_result_t<__is_scheduler_affine_t, env_of_t<_Sender>>>;
825     };
826 } // namespace stdexec
827 
828 STDEXEC_PRAGMA_POP()
829