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 struct get_delegatee_scheduler_t : __query<get_delegatee_scheduler_t>
196 {
197     static constexpr auto query(forwarding_query_t) noexcept -> bool
198     {
199         return true;
200     }
201 
202     template <class _Env>
203         requires tag_invocable<get_delegatee_scheduler_t, const _Env&>
204     auto operator()(const _Env& __t) const noexcept
205         -> tag_invoke_result_t<get_delegatee_scheduler_t, const _Env&>;
206 
207     template <class _Tag = get_delegatee_scheduler_t>
208     auto operator()() const noexcept;
209 };
210 
211 struct get_allocator_t : __query<get_allocator_t>
212 {
213     static constexpr auto query(forwarding_query_t) noexcept -> bool
214     {
215         return true;
216     }
217 
218     template <class _Env>
219         requires tag_invocable<get_allocator_t, const _Env&>
220     auto operator()(const _Env& __env) const noexcept
221         -> tag_invoke_result_t<get_allocator_t, const _Env&>
222     {
223         static_assert(nothrow_tag_invocable<get_allocator_t, const _Env&>);
224         static_assert(
225             __allocator_c<tag_invoke_result_t<get_allocator_t, const _Env&>>);
226         return tag_invoke(get_allocator_t{}, __env);
227     }
228 
229     template <class _Tag = get_allocator_t>
230     auto operator()() const noexcept;
231 };
232 
233 struct get_stop_token_t : __query<get_stop_token_t>
234 {
235     static constexpr auto query(forwarding_query_t) noexcept -> bool
236     {
237         return true;
238     }
239 
240     template <class _Env, class _Token = never_stop_token>
241     auto operator()(const _Env&) const noexcept -> _Token
242     {
243         return {};
244     }
245 
246     template <class _Env, class = void>
247         requires tag_invocable<get_stop_token_t, const _Env&>
248     auto operator()(const _Env& __env) const noexcept
249         -> tag_invoke_result_t<get_stop_token_t, const _Env&>
250     {
251         static_assert(nothrow_tag_invocable<get_stop_token_t, const _Env&>);
252         static_assert(
253             stoppable_token<
254                 __decay_t<tag_invoke_result_t<get_stop_token_t, const _Env&>>>);
255         return tag_invoke(get_stop_token_t{}, __env);
256     }
257 
258     template <class _Tag = get_stop_token_t>
259     auto operator()() const noexcept;
260 };
261 
262 template <class _Queryable, class _Tag>
263 concept __has_completion_scheduler_for =
264     queryable<_Queryable> && //
265     tag_invocable<get_completion_scheduler_t<_Tag>, const _Queryable&>;
266 
267 template <__completion_tag _Tag>
268 struct get_completion_scheduler_t : __query<get_completion_scheduler_t<_Tag>>
269 {
270     static constexpr auto query(forwarding_query_t) noexcept -> bool
271     {
272         return true;
273     }
274 
275     template <__has_completion_scheduler_for<_Tag> _Queryable>
276     auto operator()(const _Queryable& __queryable) const noexcept
277         -> tag_invoke_result_t<get_completion_scheduler_t<_Tag>,
278                                const _Queryable&>;
279 };
280 
281 struct get_domain_t
282 {
283     template <class _Ty>
284         requires tag_invocable<get_domain_t, const _Ty&>
285     constexpr auto operator()(const _Ty& __ty) const noexcept
286         -> __decay_t<tag_invoke_result_t<get_domain_t, const _Ty&>>
287     {
288         static_assert(nothrow_tag_invocable<get_domain_t, const _Ty&>,
289                       "Customizations of get_domain must be noexcept.");
290         static_assert(
291             __class<__decay_t<tag_invoke_result_t<get_domain_t, const _Ty&>>>,
292             "Customizations of get_domain must return a class type.");
293         return {};
294     }
295 
296     static constexpr auto query(forwarding_query_t) noexcept -> bool
297     {
298         return true;
299     }
300 };
301 
302 struct __is_scheduler_affine_t
303 {
304     template <class _Env>
305     constexpr auto operator()(const _Env&) const noexcept
306     {
307         if constexpr (tag_invocable<__is_scheduler_affine_t, const _Env&>)
308         {
309             using _Result =
310                 tag_invoke_result_t<__is_scheduler_affine_t, const _Env&>;
311             static_assert(__same_as<decltype(__v<_Result>), const bool>);
312             return _Result();
313         }
314         else
315         {
316             return std::false_type();
317         }
318     }
319 
320     static constexpr auto query(forwarding_query_t) noexcept -> bool
321     {
322         return false;
323     }
324 };
325 
326 struct __root_t
327 {
328     template <class _Env>
329         requires tag_invocable<__root_t, const _Env&>
330     constexpr auto operator()(const _Env& __env) const noexcept -> bool
331     {
332         STDEXEC_ASSERT(tag_invoke(__root_t{}, __env) == true);
333         return true;
334     }
335 
336     static constexpr auto query(forwarding_query_t) noexcept -> bool
337     {
338         return false;
339     }
340 };
341 
342 struct __root_env
343 {
344     using __t = __root_env;
345     using __id = __root_env;
346 
347     constexpr STDEXEC_MEMFN_DECL(auto __root)(this const __root_env&) noexcept
348         -> bool
349     {
350         return true;
351     }
352 };
353 } // namespace __queries
354 
355 using __queries::__has_algorithm_customizations_t;
356 using __queries::__is_scheduler_affine_t;
357 using __queries::__root_env;
358 using __queries::__root_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_completion_scheduler_t;
363 using __queries::get_delegatee_scheduler_t;
364 using __queries::get_domain_t;
365 using __queries::get_forward_progress_guarantee_t;
366 using __queries::get_scheduler_t;
367 using __queries::get_stop_token_t;
368 using __queries::query_or_t;
369 
370 inline constexpr forwarding_query_t forwarding_query{};
371 inline constexpr query_or_t query_or{}; // NOT TO SPEC
372 inline constexpr execute_may_block_caller_t execute_may_block_caller{};
373 inline constexpr __has_algorithm_customizations_t
374     __has_algorithm_customizations{};
375 inline constexpr get_forward_progress_guarantee_t
376     get_forward_progress_guarantee{};
377 inline constexpr get_scheduler_t get_scheduler{};
378 inline constexpr get_delegatee_scheduler_t get_delegatee_scheduler{};
379 inline constexpr get_allocator_t get_allocator{};
380 inline constexpr get_stop_token_t get_stop_token{};
381 #if !STDEXEC_GCC() || defined(__OPTIMIZE_SIZE__)
382 template <__completion_tag _Tag>
383 inline constexpr get_completion_scheduler_t<_Tag> get_completion_scheduler{};
384 #else
385 template <>
386 inline constexpr get_completion_scheduler_t<set_value_t>
387     get_completion_scheduler<set_value_t>{};
388 template <>
389 inline constexpr get_completion_scheduler_t<set_error_t>
390     get_completion_scheduler<set_error_t>{};
391 template <>
392 inline constexpr get_completion_scheduler_t<set_stopped_t>
393     get_completion_scheduler<set_stopped_t>{};
394 #endif
395 
396 template <class _Tag>
397 concept __forwarding_query = forwarding_query(_Tag{});
398 
399 inline constexpr get_domain_t get_domain{};
400 
401 template <class _Env>
402 using __domain_of_t = __decay_t<__call_result_t<get_domain_t, _Env>>;
403 
404 template <class _Tag, class _Queryable, class _Default>
405 using __query_result_or_t =
406     __call_result_t<query_or_t, _Tag, _Queryable, _Default>;
407 
408 namespace __env
409 {
410 // To be kept in sync with the promise type used in __connect_awaitable
411 template <class _Env>
412 struct __promise
413 {
414     template <class _Ty>
415     auto await_transform(_Ty&& __value) noexcept -> _Ty&&
416     {
417         return static_cast<_Ty&&>(__value);
418     }
419 
420     template <class _Ty>
421         requires tag_invocable<as_awaitable_t, _Ty, __promise&>
422     auto await_transform(_Ty&& __value) //
423         noexcept(nothrow_tag_invocable<as_awaitable_t, _Ty, __promise&>)
424             -> tag_invoke_result_t<as_awaitable_t, _Ty, __promise&>
425     {
426         return tag_invoke(as_awaitable, static_cast<_Ty&&>(__value), *this);
427     }
428 
429     auto get_env() const noexcept -> const _Env&;
430 };
431 
432 template <class _Env, class _Query, class... _Args>
433 concept __queryable = //
434     tag_invocable<_Query, const _Env&, _Args...>;
435 
436 template <class _Env, class _Query, class... _Args>
437 concept __nothrow_queryable = //
438     nothrow_tag_invocable<_Query, const _Env&, _Args...>;
439 
440 template <class _Env, class _Query, class... _Args>
441 using __query_result_t = //
442     tag_invoke_result_t<_Query, const _Env&, _Args...>;
443 
444 // A singleton environment from a query/value pair
445 template <class _Query, class _Value>
446 struct prop
447 {
448     using __t = prop;
449     using __id = prop;
450 
451     STDEXEC_ATTRIBUTE((no_unique_address))
452     _Query __query;
453 
454     STDEXEC_ATTRIBUTE((no_unique_address))
455     _Value __value;
456 
457     STDEXEC_ATTRIBUTE((nodiscard))
458     constexpr const _Value& query(_Query) const noexcept
459     {
460         return __value;
461     }
462 
463     prop& operator=(const prop&) = delete;
464 };
465 
466 template <class _Query, class _Value>
467 prop(_Query, _Value) -> prop<_Query, std::unwrap_reference_t<_Value>>;
468 
469 // utility for joining multiple environments
470 template <class... _Envs>
471 struct env
472 {
473     using __t = env;
474     using __id = env;
475 
476     __tuple_for<_Envs...> __tup_;
477 
478     // return a reference to the first child env for which
479     // __queryable<_Envs, _Query, _Args...> is true.
480     template <class _Query, class... _Args>
481     STDEXEC_ATTRIBUTE((always_inline))
482     constexpr decltype(auto) __get_1st() const noexcept
483     {
484         constexpr bool __flags[] = {__queryable<_Envs, _Query, _Args...>...};
485         constexpr std::size_t __idx =
486             __pos_of(__flags, __flags + sizeof...(_Envs));
487         return __tup::get<__idx>(__tup_);
488     }
489 
490     template <class _Query, class... _Args>
491         requires(__queryable<_Envs, _Query, _Args...> || ...)
492     STDEXEC_ATTRIBUTE((always_inline))
493     constexpr decltype(auto) query(_Query __q, _Args&&... __args) const
494         noexcept(__nothrow_queryable<decltype(__get_1st<_Query, _Args...>()),
495                                      _Query, _Args...>)
496     {
497         return tag_invoke(__q, __get_1st<_Query, _Args...>(),
498                           static_cast<_Args&&>(__args)...);
499     }
500 
501     env& operator=(const env&) = delete;
502 };
503 
504 // specialization for two envs to avoid warnings about elided braces
505 template <class _Env0, class _Env1>
506 struct env<_Env0, _Env1>
507 {
508     using __t = env;
509     using __id = env;
510 
511     STDEXEC_ATTRIBUTE((no_unique_address))
512     _Env0 __env0_;
513     STDEXEC_ATTRIBUTE((no_unique_address))
514     _Env1 __env1_;
515 
516     // return a reference to the first child env for which
517     // __queryable<_Envs, _Query, _Args...> is true.
518     template <class _Query, class... _Args>
519     STDEXEC_ATTRIBUTE((always_inline))
520     constexpr decltype(auto) __get_1st() const noexcept
521     {
522         if constexpr (__queryable<_Env0, _Query, _Args...>)
523         {
524             return (__env0_);
525         }
526         else
527         {
528             return (__env1_);
529         }
530     }
531 
532     template <class _Query, class... _Args>
533         requires __queryable<_Env0, _Query, _Args...> ||
534                  __queryable<_Env1, _Query, _Args...>
535     STDEXEC_ATTRIBUTE((always_inline))
536     constexpr decltype(auto) query(_Query __q, _Args&&... __args) const
537         noexcept(__nothrow_queryable<decltype(__get_1st<_Query, _Args...>()),
538                                      _Query, _Args...>)
539     {
540         return tag_invoke(__q, __get_1st<_Query, _Args...>(),
541                           static_cast<_Args&&>(__args)...);
542     }
543 
544     env& operator=(const env&) = delete;
545 };
546 
547 template <class... _Envs>
548 env(_Envs...) -> env<std::unwrap_reference_t<_Envs>...>;
549 
550 template <class _Value, class _Tag, class... _Tags>
551 struct __with
552 {
553     using __t = __with;
554     using __id = __with;
555     STDEXEC_ATTRIBUTE((no_unique_address))
556     _Value __value_;
557 
558     __with() = default;
559 
560     constexpr explicit __with(_Value __value) noexcept(
561         __nothrow_decay_copyable<_Value>) :
562         __value_(static_cast<_Value&&>(__value))
563     {}
564 
565     constexpr explicit __with(_Value __value, _Tag, _Tags...) noexcept(
566         __nothrow_decay_copyable<_Value>) :
567         __value_(static_cast<_Value&&>(__value))
568     {}
569 
570     template <__one_of<_Tag, _Tags...> _Key>
571     auto query(_Key) const noexcept -> const _Value&
572     {
573         return __value_;
574     }
575 
576     __with& operator=(const __with&) = delete;
577 };
578 
579 template <class _Value, class _Tag, class... _Tags>
580 __with(_Value, _Tag, _Tags...) -> __with<_Value, _Tag, _Tags...>;
581 
582 template <class _EnvId>
583 struct __fwd
584 {
585     using _Env = __cvref_t<_EnvId>;
586     static_assert(__nothrow_move_constructible<_Env>);
587 
588     struct __t
589     {
590         using __id = __fwd;
591         STDEXEC_ATTRIBUTE((no_unique_address))
592         _Env __env_;
593 
594 #if STDEXEC_GCC() && __GNUC__ < 12
595         using __cvref_env_t = std::add_const_t<_Env>&;
596 #else
597         using __cvref_env_t = const _Env&;
598 #endif
599 
600         template <__forwarding_query _Tag>
601             requires tag_invocable<_Tag, __cvref_env_t>
602         auto query(_Tag) const
603             noexcept(nothrow_tag_invocable<_Tag, __cvref_env_t>)
604                 -> tag_invoke_result_t<_Tag, __cvref_env_t>
605         {
606             return tag_invoke(_Tag(), __env_);
607         }
608 
609         __t& operator=(const __t&) = delete;
610     };
611 };
612 
613 struct __fwd_fn
614 {
615     template <class _Env>
616     auto operator()(_Env&& __env) const
617     {
618         return __t<__fwd<__cvref_id<_Env>>>{static_cast<_Env&&>(__env)};
619     }
620 
621     auto operator()(empty_env) const -> empty_env
622     {
623         return {};
624     }
625 };
626 
627 template <class _EnvId, class _Tag>
628 struct __without_
629 {
630     using _Env = __cvref_t<_EnvId>;
631     static_assert(__nothrow_move_constructible<_Env>);
632 
633     struct __t
634     {
635         using __id = __without_;
636         _Env __env_;
637 
638 #if STDEXEC_GCC() && __GNUC__ < 12
639         using __cvref_env_t = std::add_const_t<_Env>&;
640 #else
641         using __cvref_env_t = const _Env&;
642 #endif
643 
644         auto query(_Tag) const noexcept = delete;
645 
646         template <tag_invocable<__cvref_env_t> _Key>
647         STDEXEC_ATTRIBUTE((always_inline))
648         auto query(_Key) const noexcept(
649             nothrow_tag_invocable<_Key, __cvref_env_t>) -> decltype(auto)
650         {
651             return tag_invoke(_Key(), __env_);
652         }
653 
654         __t& operator=(const __t&) = delete;
655     };
656 };
657 
658 struct __without_fn
659 {
660     template <class _Env, class _Tag>
661     constexpr auto operator()(_Env&& __env,
662                               _Tag) const noexcept -> decltype(auto)
663     {
664         if constexpr (tag_invocable<_Tag, _Env>)
665         {
666             using _Without = __t<__without_<__cvref_id<_Env>, _Tag>>;
667             return _Without{static_cast<_Env&&>(__env)};
668         }
669         else
670         {
671             return static_cast<_Env>(static_cast<_Env&&>(__env));
672         }
673     }
674 };
675 
676 inline constexpr __without_fn __without{};
677 
678 template <class _Env, class _Tag, class... _Tags>
679 using __without_t = __result_of<__without, _Env, _Tag, _Tags...>;
680 
681 template <__nothrow_move_constructible _Fun>
682 struct __from
683 {
684     using __t = __from;
685     using __id = __from;
686     STDEXEC_ATTRIBUTE((no_unique_address))
687     _Fun __fun_;
688 
689     template <class _Tag>
690         requires __callable<const _Fun&, _Tag>
691     auto query(_Tag) const noexcept(__nothrow_callable<const _Fun&, _Tag>)
692         -> __call_result_t<const _Fun&, _Tag>
693     {
694         return __fun_(_Tag());
695     }
696 
697     __from& operator=(const __from&) = delete;
698 };
699 
700 template <class _Fun>
701 __from(_Fun) -> __from<_Fun>;
702 
703 struct __join_fn
704 {
705     auto operator()(empty_env, empty_env) const noexcept -> empty_env
706     {
707         return {};
708     }
709 
710     template <class _Env>
711     auto operator()(_Env&& __env, empty_env = {}) const noexcept -> _Env
712     {
713         return static_cast<_Env&&>(__env);
714     }
715 
716     template <class _Env>
717     auto operator()(empty_env, _Env&& __env) const noexcept -> decltype(auto)
718     {
719         return __fwd_fn()(static_cast<_Env&&>(__env));
720     }
721 
722     template <class _First, class _Second>
723     auto operator()(_First&& __first, _Second&& __second) const noexcept
724         -> env<_First, __call_result_t<__fwd_fn, _Second>>
725     {
726         return {static_cast<_First&&>(__first),
727                 __fwd_fn()(static_cast<_Second&&>(__second))};
728     }
729 };
730 
731 inline constexpr __join_fn __join{};
732 
733 template <class _First, class... _Second>
734 using __join_t = __result_of<__join, _First, _Second...>;
735 
736 struct __as_root_env_fn
737 {
738     template <class _Env>
739     constexpr auto operator()(_Env __env) const noexcept
740         -> __join_t<__root_env, std::unwrap_reference_t<_Env>>
741     {
742         return __join(__root_env{},
743                       static_cast<std::unwrap_reference_t<_Env>&&>(__env));
744     }
745 };
746 
747 inline constexpr __as_root_env_fn __as_root_env{};
748 
749 template <class _Env>
750 using __as_root_env_t = __result_of<__as_root_env, _Env>;
751 } // namespace __env
752 
753 using __env::env;
754 using __env::prop;
755 using empty_env = env<>;
756 
757 /////////////////////////////////////////////////////////////////////////////
758 namespace __get_env
759 {
760 // For getting an execution environment from a receiver or the attributes from a
761 // sender.
762 struct get_env_t
763 {
764     template <__same_as<get_env_t> _Self, class _EnvProvider>
765     STDEXEC_ATTRIBUTE((always_inline))
766     friend auto tag_invoke(_Self, const _EnvProvider& __env_provider) noexcept
767         -> decltype(__env_provider.get_env())
768     {
769         static_assert(noexcept(__env_provider.get_env()),
770                       "get_env() members must be noexcept");
771         return __env_provider.get_env();
772     }
773 
774     template <class _EnvProvider>
775         requires tag_invocable<get_env_t, const _EnvProvider&>
776     STDEXEC_ATTRIBUTE((always_inline))
777     constexpr auto operator()(const _EnvProvider& __env_provider) const noexcept
778         -> tag_invoke_result_t<get_env_t, const _EnvProvider&>
779     {
780         static_assert(
781             queryable<tag_invoke_result_t<get_env_t, const _EnvProvider&>>);
782         static_assert(nothrow_tag_invocable<get_env_t, const _EnvProvider&>);
783         return tag_invoke(*this, __env_provider);
784     }
785 
786     template <class _EnvProvider>
787     constexpr auto operator()(const _EnvProvider&) const noexcept -> empty_env
788     {
789         return {};
790     }
791 };
792 } // namespace __get_env
793 
794 using __get_env::get_env_t;
795 inline constexpr get_env_t get_env{};
796 
797 template <class _EnvProvider>
798 concept environment_provider = //
799     requires(_EnvProvider& __ep) {
800         { get_env(std::as_const(__ep)) } -> queryable;
801     };
802 
803 using __env::__as_root_env;
804 using __env::__as_root_env_t;
805 
806 template <class _Env>
807 concept __is_root_env = requires(_Env&& __env) {
808                             { __root_t{}(__env) } -> same_as<bool>;
809                         };
810 
811 template <class _Sender>
812 concept __is_scheduler_affine = //
813     requires {
814         requires __v<
815             __call_result_t<__is_scheduler_affine_t, env_of_t<_Sender>>>;
816     };
817 } // namespace stdexec
818 
819 STDEXEC_PRAGMA_POP()
820