xref: /openbmc/sdbusplus/include/sdbusplus/async/stdexec/__detail/__env.hpp (revision 36137e09614746b13603b5fbae79e6f70819c46b)
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>
operator ()stdexec::__queries::forwarding_query_t60     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>
operator ()stdexec::__queries::query_or_t90     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>
operator ()stdexec::__queries::query_or_t98     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>>
operator ()stdexec::__queries::execute_may_block_caller_t112     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 
operator ()stdexec::__queries::execute_may_block_caller_t122     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>>
operator ()stdexec::__queries::get_forward_progress_guarantee_t133     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 
operator ()stdexec::__queries::get_forward_progress_guarantee_t142     constexpr auto operator()(auto&&) const noexcept
143         -> 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>>
operator ()stdexec::__queries::__has_algorithm_customizations_t158     constexpr auto operator()(_Tp&&) const noexcept(noexcept(__result_t<_Tp>{}))
159         -> __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 
operator ()stdexec::__queries::__has_algorithm_customizations_t169     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 {
querystdexec::__queries::get_scheduler_t181     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 {
querystdexec::__queries::get_delegation_scheduler_t200     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 {
querystdexec::__queries::get_allocator_t216     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&>
operator ()stdexec::__queries::get_allocator_t223     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 {
querystdexec::__queries::get_stop_token_t238     static constexpr auto query(forwarding_query_t) noexcept -> bool
239     {
240         return true;
241     }
242 
243     template <class _Env, class _Token = never_stop_token>
operator ()stdexec::__queries::get_stop_token_t244     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&>
operator ()stdexec::__queries::get_stop_token_t251     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 {
querystdexec::__queries::get_completion_scheduler_t273     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&>
operator ()stdexec::__queries::get_domain_t288     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 
querystdexec::__queries::get_domain_t299     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>
operator ()stdexec::__queries::__is_scheduler_affine_t308     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 
querystdexec::__queries::__is_scheduler_affine_t323     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&>
operator ()stdexec::__queries::__root_t333     constexpr auto operator()(const _Env& __env) const noexcept -> bool
334     {
335         STDEXEC_ASSERT(tag_invoke(__root_t{}, __env) == true);
336         return true;
337     }
338 
querystdexec::__queries::__root_t339     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 
STDEXEC_MEMFN_DECLstdexec::__queries::__root_env350     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>
await_transformstdexec::__env::__promise424     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&>
await_transformstdexec::__env::__promise431     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))
querystdexec::__env::prop467     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))
__get_1ststdexec::__env::env491     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))
querystdexec::__env::env502     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))
__get_1ststdexec::__env::env529     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 
__withstdexec::__env::__with569     constexpr explicit __with(_Value __value) noexcept(
570         __nothrow_decay_copyable<_Value>) :
571         __value_(static_cast<_Value&&>(__value))
572     {}
573 
__withstdexec::__env::__with574     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>
querystdexec::__env::__with580     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>
querystdexec::__env::__fwd::__t611         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>
operator ()stdexec::__env::__fwd_fn625     auto operator()(_Env&& __env) const
626     {
627         return __t<__fwd<__cvref_id<_Env>>>{static_cast<_Env&&>(__env)};
628     }
629 
operator ()stdexec::__env::__fwd_fn630     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))
querystdexec::__env::__without_::__t657         auto query(_Key) const
658             noexcept(nothrow_tag_invocable<_Key, __cvref_env_t>)
659                 -> decltype(auto)
660         {
661             return tag_invoke(_Key(), __env_);
662         }
663 
664         __t& operator=(const __t&) = delete;
665     };
666 };
667 
668 struct __without_fn
669 {
670     template <class _Env, class _Tag>
operator ()stdexec::__env::__without_fn671     constexpr auto operator()(_Env&& __env, _Tag) const noexcept
672         -> decltype(auto)
673     {
674         if constexpr (tag_invocable<_Tag, _Env>)
675         {
676             using _Without = __t<__without_<__cvref_id<_Env>, _Tag>>;
677             return _Without{static_cast<_Env&&>(__env)};
678         }
679         else
680         {
681             return static_cast<_Env>(static_cast<_Env&&>(__env));
682         }
683     }
684 };
685 
686 inline constexpr __without_fn __without{};
687 
688 template <class _Env, class _Tag, class... _Tags>
689 using __without_t = __result_of<__without, _Env, _Tag, _Tags...>;
690 
691 template <__nothrow_move_constructible _Fun>
692 struct __from
693 {
694     using __t = __from;
695     using __id = __from;
696     STDEXEC_ATTRIBUTE((no_unique_address))
697     _Fun __fun_;
698 
699     template <class _Tag>
700         requires __callable<const _Fun&, _Tag>
querystdexec::__env::__from701     auto query(_Tag) const noexcept(__nothrow_callable<const _Fun&, _Tag>)
702         -> __call_result_t<const _Fun&, _Tag>
703     {
704         return __fun_(_Tag());
705     }
706 
707     __from& operator=(const __from&) = delete;
708 };
709 
710 template <class _Fun>
711 __from(_Fun) -> __from<_Fun>;
712 
713 struct __join_fn
714 {
operator ()stdexec::__env::__join_fn715     auto operator()(empty_env, empty_env) const noexcept -> empty_env
716     {
717         return {};
718     }
719 
720     template <class _Env>
operator ()stdexec::__env::__join_fn721     auto operator()(_Env&& __env, empty_env = {}) const noexcept -> _Env
722     {
723         return static_cast<_Env&&>(__env);
724     }
725 
726     template <class _Env>
operator ()stdexec::__env::__join_fn727     auto operator()(empty_env, _Env&& __env) const noexcept -> decltype(auto)
728     {
729         return __fwd_fn()(static_cast<_Env&&>(__env));
730     }
731 
732     template <class _First, class _Second>
operator ()stdexec::__env::__join_fn733     auto operator()(_First&& __first, _Second&& __second) const noexcept
734         -> env<_First, __call_result_t<__fwd_fn, _Second>>
735     {
736         return {static_cast<_First&&>(__first),
737                 __fwd_fn()(static_cast<_Second&&>(__second))};
738     }
739 };
740 
741 inline constexpr __join_fn __join{};
742 
743 template <class _First, class... _Second>
744 using __join_t = __result_of<__join, _First, _Second...>;
745 
746 struct __as_root_env_fn
747 {
748     template <class _Env>
operator ()stdexec::__env::__as_root_env_fn749     constexpr auto operator()(_Env __env) const noexcept
750         -> __join_t<__root_env, std::unwrap_reference_t<_Env>>
751     {
752         return __join(__root_env{},
753                       static_cast<std::unwrap_reference_t<_Env>&&>(__env));
754     }
755 };
756 
757 inline constexpr __as_root_env_fn __as_root_env{};
758 
759 template <class _Env>
760 using __as_root_env_t = __result_of<__as_root_env, _Env>;
761 } // namespace __env
762 
763 using __env::env;
764 using __env::prop;
765 using empty_env = env<>;
766 
767 /////////////////////////////////////////////////////////////////////////////
768 namespace __get_env
769 {
770 // For getting an execution environment from a receiver or the attributes from a
771 // sender.
772 struct get_env_t
773 {
774     template <__same_as<get_env_t> _Self, class _EnvProvider>
775     STDEXEC_ATTRIBUTE((always_inline))
tag_invoke(_Self,const _EnvProvider & __env_provider)776     friend auto tag_invoke(_Self, const _EnvProvider& __env_provider) noexcept
777         -> decltype(__env_provider.get_env())
778     {
779         static_assert(noexcept(__env_provider.get_env()),
780                       "get_env() members must be noexcept");
781         return __env_provider.get_env();
782     }
783 
784     template <class _EnvProvider>
785         requires tag_invocable<get_env_t, const _EnvProvider&>
786     STDEXEC_ATTRIBUTE((always_inline))
operator ()stdexec::__get_env::get_env_t787     constexpr auto operator()(const _EnvProvider& __env_provider) const noexcept
788         -> tag_invoke_result_t<get_env_t, const _EnvProvider&>
789     {
790         static_assert(
791             queryable<tag_invoke_result_t<get_env_t, const _EnvProvider&>>);
792         static_assert(nothrow_tag_invocable<get_env_t, const _EnvProvider&>);
793         return tag_invoke(*this, __env_provider);
794     }
795 
796     template <class _EnvProvider>
operator ()stdexec::__get_env::get_env_t797     constexpr auto operator()(const _EnvProvider&) const noexcept -> empty_env
798     {
799         return {};
800     }
801 };
802 } // namespace __get_env
803 
804 using __get_env::get_env_t;
805 inline constexpr get_env_t get_env{};
806 
807 template <class _EnvProvider>
808 concept environment_provider = //
809     requires(_EnvProvider& __ep) {
810         { get_env(std::as_const(__ep)) } -> queryable;
811     };
812 
813 using __env::__as_root_env;
814 using __env::__as_root_env_t;
815 
816 template <class _Env>
817 concept __is_root_env = requires(_Env&& __env) {
818                             { __root_t{}(__env) } -> same_as<bool>;
819                         };
820 
821 template <class _Sender>
822 concept __is_scheduler_affine = //
823     requires {
824         requires __v<
825             __call_result_t<__is_scheduler_affine_t, env_of_t<_Sender>>>;
826     };
827 } // namespace stdexec
828 
829 STDEXEC_PRAGMA_POP()
830