xref: /openbmc/sdbusplus/include/sdbusplus/async/stdexec/__detail/__env.hpp (revision 10d0b4b7d1498cfd5c3d37edea271a54d1984e41)
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 "__execution_fwd.hpp"
19 
20 #include "__concepts.hpp"
21 #include "__completion_behavior.hpp"
22 #include "__meta.hpp"
23 #include "__query.hpp"
24 #include "__stop_token.hpp"
25 #include "__tag_invoke.hpp"
26 // #include "__tuple.hpp"
27 
28 #include <exception>  // IWYU pragma: keep for std::terminate
29 #include <functional> // IWYU pragma: keep for unwrap_reference_t
30 #include <type_traits>
31 #include <utility>
32 
33 STDEXEC_PRAGMA_PUSH()
34 STDEXEC_PRAGMA_IGNORE_EDG(probable_guiding_friend)
35 STDEXEC_PRAGMA_IGNORE_EDG(type_qualifiers_ignored_on_reference)
36 
37 namespace stdexec {
38   //////////////////////////////////////////////////////////////////////////////////////////////////
39   // [exec.queries]
40   namespace __queries {
41     struct execute_may_block_caller_t : __query<execute_may_block_caller_t, true> {
42       template <class _Attrs>
STDEXEC_ATTRIBUTEstdexec::__queries::execute_may_block_caller_t43       STDEXEC_ATTRIBUTE(always_inline, host, device)
44       static constexpr void __validate() noexcept {
45         static_assert(same_as<bool, __call_result_t<execute_may_block_caller_t, const _Attrs&>>);
46         static_assert(__nothrow_callable<execute_may_block_caller_t, const _Attrs&>);
47       }
48     };
49 
50     struct get_forward_progress_guarantee_t
51       : __query<
52           get_forward_progress_guarantee_t,
53           forward_progress_guarantee::weakly_parallel,
54           __q1<__decay_t>
55         > {
56       template <class _Attrs>
STDEXEC_ATTRIBUTEstdexec::__queries::get_forward_progress_guarantee_t57       STDEXEC_ATTRIBUTE(always_inline, host, device)
58       static constexpr void __validate() noexcept {
59         using __result_t = __call_result_t<get_forward_progress_guarantee_t, const _Attrs&>;
60         static_assert(same_as<forward_progress_guarantee, __result_t>);
61         static_assert(__nothrow_callable<get_forward_progress_guarantee_t, const _Attrs&>);
62       }
63     };
64 
65     // TODO: implement allocator concept
66     template <class _T0>
67     concept __allocator_c = true;
68 
69     struct get_scheduler_t : __query<get_scheduler_t> {
70       using __query<get_scheduler_t>::operator();
71 
72       template <class _Query = get_scheduler_t>
73       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
74       constexpr auto
75         operator()() const noexcept; // defined in __read_env.hpp // defined in __read_env.hpp
76 
77       template <class _Env>
78       STDEXEC_ATTRIBUTE(always_inline, host, device)
79       static constexpr void __validate() noexcept; // defined in __schedulers.hpp
80 
STDEXEC_ATTRIBUTEstdexec::__queries::get_scheduler_t81       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
82       static consteval auto query(forwarding_query_t) noexcept -> bool {
83         return true;
84       }
85     };
86 
87     //! The type for `get_delegation_scheduler` [exec.get.delegation.scheduler]
88     //! A query object that asks for a scheduler that can be used to delegate
89     //! work to for the purpose of forward progress delegation ([intro.progress]).
90     struct get_delegation_scheduler_t : __query<get_delegation_scheduler_t> {
91       using __query<get_delegation_scheduler_t>::operator();
92 
93       template <class _Query = get_delegation_scheduler_t>
94       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
95       constexpr auto operator()() const noexcept; // defined in __read_env.hpp
96 
97       template <class _Env>
98       STDEXEC_ATTRIBUTE(always_inline, host, device)
99       static constexpr void __validate() noexcept; // defined in __schedulers.hpp
100 
STDEXEC_ATTRIBUTEstdexec::__queries::get_delegation_scheduler_t101       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
102       static consteval auto query(forwarding_query_t) noexcept -> bool {
103         return true;
104       }
105     };
106 
107     struct get_allocator_t : __query<get_allocator_t> {
108       using __query<get_allocator_t>::operator();
109 
110       template <class _Query = get_allocator_t>
111       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
112       constexpr auto operator()() const noexcept; // defined in __read_env.hpp
113 
114       template <class _Env>
STDEXEC_ATTRIBUTEstdexec::__queries::get_allocator_t115       STDEXEC_ATTRIBUTE(always_inline, host, device)
116       static constexpr void __validate() noexcept {
117         static_assert(__nothrow_callable<get_allocator_t, const _Env&>);
118         static_assert(__allocator_c<__call_result_t<get_allocator_t, const _Env&>>);
119       }
120 
STDEXEC_ATTRIBUTEstdexec::__queries::get_allocator_t121       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
122       static consteval auto query(forwarding_query_t) noexcept -> bool {
123         return true;
124       }
125     };
126 
127     using __get_stop_token_t = __query<get_stop_token_t, never_stop_token{}, __q1<__decay_t>>;
128 
129     struct get_stop_token_t : __get_stop_token_t {
130       using __get_stop_token_t::operator();
131 
132       template <class _Query = get_stop_token_t>
133       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
134       constexpr auto operator()() const noexcept; // defined in __read_env.hpp
135 
136       template <class _Env>
STDEXEC_ATTRIBUTEstdexec::__queries::get_stop_token_t137       STDEXEC_ATTRIBUTE(always_inline, host, device)
138       static constexpr void __validate() noexcept {
139         static_assert(__nothrow_callable<get_stop_token_t, const _Env&>);
140         static_assert(stoppable_token<__call_result_t<get_stop_token_t, const _Env&>>);
141       }
142 
STDEXEC_ATTRIBUTEstdexec::__queries::get_stop_token_t143       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
144       static consteval auto query(forwarding_query_t) noexcept -> bool {
145         return true;
146       }
147     };
148 
149     template <__completion_tag _Query>
150     struct get_completion_scheduler_t : __query<get_completion_scheduler_t<_Query>> {
151       template <class _Env>
152       STDEXEC_ATTRIBUTE(always_inline, host, device)
153       static constexpr void __validate() noexcept; // defined in __schedulers.hpp
154 
STDEXEC_ATTRIBUTEstdexec::__queries::get_completion_scheduler_t155       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
156       static consteval auto query(forwarding_query_t) noexcept -> bool {
157         return true;
158       }
159     };
160 
161     struct get_domain_t : __query<get_domain_t, __no_default, __q1<__decay_t>> {
162       template <class _Env>
STDEXEC_ATTRIBUTEstdexec::__queries::get_domain_t163       STDEXEC_ATTRIBUTE(always_inline, host, device)
164       static constexpr void __validate() noexcept {
165         static_assert(
166           __nothrow_callable<get_domain_t, const _Env&>,
167           "Customizations of get_domain must be noexcept.");
168         static_assert(
169           __class<__call_result_t<get_domain_t, const _Env&>>,
170           "Customizations of get_domain must return a class type.");
171       }
172 
STDEXEC_ATTRIBUTEstdexec::__queries::get_domain_t173       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
174       static consteval auto query(forwarding_query_t) noexcept -> bool {
175         return true;
176       }
177     };
178 
179     struct get_domain_override_t : __query<get_domain_override_t, __no_default, __q1<__decay_t>> {
180       template <class _Env>
STDEXEC_ATTRIBUTEstdexec::__queries::get_domain_override_t181       STDEXEC_ATTRIBUTE(always_inline, host, device)
182       static constexpr void __validate() noexcept {
183         static_assert(
184           __nothrow_callable<get_domain_override_t, const _Env&>,
185           "Customizations of get_domain_override must be noexcept.");
186         static_assert(
187           __class<__call_result_t<get_domain_override_t, const _Env&>>,
188           "Customizations of get_domain_override must return a class type.");
189       }
190 
STDEXEC_ATTRIBUTEstdexec::__queries::get_domain_override_t191       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
192       static consteval auto query(forwarding_query_t) noexcept -> bool {
193         return false;
194       }
195     };
196 
197     struct __is_scheduler_affine_t {
198       template <class _Result>
STDEXEC_ATTRIBUTEstdexec::__queries::__is_scheduler_affine_t199       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
200       static consteval auto __ensure_bool_constant() noexcept {
201         if constexpr (__is_bool_constant<_Result>) {
202           return static_cast<bool>(_Result::value);
203         } else {
204           static_assert(
205             __is_bool_constant<_Result>,
206             "The __is_scheduler_affine query must be one of the following forms:\n"
207             "  static constexpr bool query(__is_scheduler_affine_t) noexcept;\n"
208             "  bool_constant<Bool> query(__is_scheduler_affine_t) const noexcept;\n"
209             "  bool_constant<Bool> query(__is_scheduler_affine_t, const Env&) const noexcept;\n");
210         }
211       }
212 
213       template <class _Attrs, class... _Env>
STDEXEC_ATTRIBUTEstdexec::__queries::__is_scheduler_affine_t214       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
215       consteval auto operator()() const noexcept -> bool {
216         return __completes_inline<_Attrs, const _Env&...>;
217       }
218 
219       template <__queryable_with<__is_scheduler_affine_t> _Attrs, class... _Env>
STDEXEC_ATTRIBUTEstdexec::__queries::__is_scheduler_affine_t220       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
221       consteval auto operator()() const noexcept -> bool {
222         if constexpr (__statically_queryable_with<_Attrs, __is_scheduler_affine_t>) {
223           return _Attrs::query(__is_scheduler_affine_t());
224         } else {
225           return __ensure_bool_constant<__query_result_t<_Attrs, __is_scheduler_affine_t>>();
226         }
227       }
228 
229       template <class _Attrs, class _Env>
230         requires __queryable_with<_Attrs, __is_scheduler_affine_t, const _Env&>
STDEXEC_ATTRIBUTEstdexec::__queries::__is_scheduler_affine_t231       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
232       consteval auto operator()() const noexcept -> bool {
233         using __result_t = __query_result_t<_Attrs, __is_scheduler_affine_t, const _Env&>;
234         return __ensure_bool_constant<__result_t>();
235       }
236 
237       template <class _Attrs, class... _Env>
STDEXEC_ATTRIBUTEstdexec::__queries::__is_scheduler_affine_t238       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
239       consteval auto operator()(const _Attrs&, const _Env&...) const noexcept -> bool {
240         return operator()<_Attrs, _Env...>();
241       }
242 
STDEXEC_ATTRIBUTEstdexec::__queries::__is_scheduler_affine_t243       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
244       static consteval auto query(forwarding_query_t) noexcept -> bool {
245         return false;
246       }
247     };
248 
249     struct __root_t : __query<__root_t> {
STDEXEC_ATTRIBUTEstdexec::__queries::__root_t250       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
251       static consteval auto query(forwarding_query_t) noexcept -> bool {
252         return false;
253       }
254     };
255 
256     struct __root_env {
257       using __t = __root_env;
258       using __id = __root_env;
259 
STDEXEC_ATTRIBUTEstdexec::__queries::__root_env260       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
261       static constexpr auto query(__root_t) noexcept -> bool {
262         return true;
263       }
264     };
265   } // namespace __queries
266 
267   using __queries::execute_may_block_caller_t;
268   using __queries::get_forward_progress_guarantee_t;
269   using __queries::get_allocator_t;
270   using __queries::get_scheduler_t;
271   using __queries::get_delegation_scheduler_t;
272   using get_delegatee_scheduler_t
273     [[deprecated("get_delegatee_scheduler_t has been renamed get_delegation_scheduler_t")]] =
274       get_delegation_scheduler_t;
275   using __queries::get_stop_token_t;
276   using __queries::get_completion_scheduler_t;
277   using __queries::get_domain_t;
278   using __queries::get_domain_override_t;
279   using __queries::__is_scheduler_affine_t;
280   using __queries::__root_t;
281   using __queries::__root_env;
282 
283   inline constexpr execute_may_block_caller_t execute_may_block_caller{};
284   inline constexpr get_forward_progress_guarantee_t get_forward_progress_guarantee{};
285   inline constexpr get_scheduler_t get_scheduler{};
286   inline constexpr get_delegation_scheduler_t get_delegation_scheduler{};
287   inline constexpr auto& get_delegatee_scheduler
288     [[deprecated("get_delegatee_scheduler has been renamed get_delegation_scheduler")]]
289     = get_delegation_scheduler;
290   inline constexpr get_allocator_t get_allocator{};
291   inline constexpr get_stop_token_t get_stop_token{};
292 #if !STDEXEC_GCC() || defined(__OPTIMIZE_SIZE__)
293   template <__completion_tag _Query>
294   inline constexpr get_completion_scheduler_t<_Query> get_completion_scheduler{};
295 #else
296   template <>
297   inline constexpr get_completion_scheduler_t<set_value_t> get_completion_scheduler<set_value_t>{};
298   template <>
299   inline constexpr get_completion_scheduler_t<set_error_t> get_completion_scheduler<set_error_t>{};
300   template <>
301   inline constexpr get_completion_scheduler_t<set_stopped_t>
302     get_completion_scheduler<set_stopped_t>{};
303 #endif
304 
305   inline constexpr get_domain_t get_domain{};
306   inline constexpr get_domain_override_t get_domain_override{};
307 
308   template <class _Query, class _Queryable, class _Default>
309   using __query_result_or_t = __call_result_t<query_or_t, _Query, _Queryable, _Default>;
310 
311   namespace __env {
312     template <class _Tp, class _Promise>
313     concept __has_as_awaitable_member = requires(_Tp&& __t, _Promise& __promise) {
314       static_cast<_Tp &&>(__t).as_awaitable(__promise);
315     };
316 
317     template <class _Promise>
318     struct __with_await_transform {
319       template <class _Ty>
STDEXEC_ATTRIBUTEstdexec::__env::__with_await_transform320       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
321       auto await_transform(_Ty&& __value) noexcept -> _Ty&& {
322         return static_cast<_Ty&&>(__value);
323       }
324 
325       template <class _Ty>
326         requires __has_as_awaitable_member<_Ty, _Promise&>
STDEXEC_ATTRIBUTEstdexec::__env::__with_await_transform327       STDEXEC_ATTRIBUTE(nodiscard, host, device)
328       auto await_transform(_Ty&& __value)
329         noexcept(noexcept(__declval<_Ty>().as_awaitable(__declval<_Promise&>())))
330           -> decltype(__declval<_Ty>().as_awaitable(__declval<_Promise&>())) {
331         return static_cast<_Ty&&>(__value).as_awaitable(static_cast<_Promise&>(*this));
332       }
333 
334       template <class _Ty>
335         requires(!__has_as_awaitable_member<_Ty, _Promise&>)
336              && tag_invocable<as_awaitable_t, _Ty, _Promise&>
STDEXEC_ATTRIBUTEstdexec::__env::__with_await_transform337       STDEXEC_ATTRIBUTE(nodiscard, host, device)
338       auto await_transform(_Ty&& __value)
339         noexcept(nothrow_tag_invocable<as_awaitable_t, _Ty, _Promise&>)
340           -> tag_invoke_result_t<as_awaitable_t, _Ty, _Promise&> {
341         return tag_invoke(as_awaitable, static_cast<_Ty&&>(__value), static_cast<_Promise&>(*this));
342       }
343     };
344 
345     template <class _Env>
346     struct __promise : __with_await_transform<__promise<_Env>> {
347       STDEXEC_ATTRIBUTE(nodiscard, host, device)
348       auto get_env() const noexcept -> const _Env&;
349     };
350 
351     template <class ValueType>
352     struct __prop_like {
353       template <class _Query>
STDEXEC_ATTRIBUTEstdexec::__env::__prop_like354       STDEXEC_ATTRIBUTE(noreturn, nodiscard, host, device)
355       constexpr auto query(_Query) const noexcept -> const ValueType& {
356         STDEXEC_TERMINATE();
357       }
358     };
359 
360     // A singleton environment from a query/value pair
361     template <class _Query, class _Value>
362     struct prop {
363       using __t = prop;
364       using __id = prop;
365 
366       static_assert(__callable<_Query, __prop_like<_Value>>);
367 
STDEXEC_ATTRIBUTEstdexec::__env::prop368       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
369       constexpr auto query(_Query) const noexcept -> const _Value& {
370         return __value;
371       }
372 
373       STDEXEC_ATTRIBUTE(no_unique_address) _Query __query;
374       STDEXEC_ATTRIBUTE(no_unique_address) _Value __value;
375     };
376 
377     template <class _Query, class _Value>
378     STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE
379       prop(_Query, _Value) -> prop<_Query, std::unwrap_reference_t<_Value>>;
380 
381     template <class _Query, auto _Value>
382     struct cprop {
383       using __t = cprop;
384       using __id = cprop;
385 
STDEXEC_ATTRIBUTEstdexec::__env::cprop386       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
387       static constexpr auto query(_Query) noexcept {
388         return _Value;
389       }
390     };
391 
392     //////////////////////////////////////////////////////////////////////
393     // env
394     template <class... Envs>
395     struct env;
396 
397     template <>
398     struct env<> {
399       using __t = env;
400       using __id = env;
401 
402       STDEXEC_ATTRIBUTE(nodiscard, host, device)
403       auto query() const = delete;
404     };
405 
406     template <class Env>
407     struct env<Env> : Env {
408       using __t = env;
409       using __id = env;
410     };
411 
412     template <class Env>
413     struct env<Env&> {
414       using __t = env;
415       using __id = env;
416 
417       template <class Query, class... _Args>
418         requires __queryable_with<Env, Query, _Args...>
STDEXEC_ATTRIBUTEstdexec::__env::env419       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
420       constexpr auto query(Query, _Args&&... __args) const
421         noexcept(__nothrow_queryable_with<Env, Query, _Args...>)
422           -> __query_result_t<Env, Query, _Args...> {
423         return __query<Query>()(env_, static_cast<_Args&&>(__args)...);
424       }
425 
426       Env& env_;
427     };
428 
429     template <class Env>
430     using __env_base = __if_c<std::is_reference_v<Env>, env<Env>, Env>;
431 
432     template <class Env1, class Env2>
433     struct env<Env1, Env2> : __env_base<Env1> {
434       using __t = env;
435       using __id = env;
436 
437       using __env_base<Env1>::query;
438 
439       template <class Query, class... _Args>
440         requires(!__queryable_with<Env1, Query, _Args...>)
441              && __queryable_with<Env2, Query, _Args...>
STDEXEC_ATTRIBUTEstdexec::__env::env442       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
443       constexpr auto query(Query, _Args&&... __args) const
444         noexcept(__nothrow_queryable_with<Env2, Query, _Args...>)
445           -> __query_result_t<Env2, Query, _Args...> {
446         return __query<Query>()(env2_, static_cast<_Args&&>(__args)...);
447       }
448 
449       STDEXEC_ATTRIBUTE(no_unique_address) Env2 env2_;
450     };
451 
452     template <class Env1, class Env2, class... Envs>
453     struct env<Env1, Env2, Envs...> : env<env<Env1, Env2>, Envs...> {
454       using __t = env;
455       using __id = env;
456     };
457 
458     template <class... _Envs>
459     STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE env(_Envs...) -> env<std::unwrap_reference_t<_Envs>...>;
460 
461     template <class _EnvId>
462     struct __fwd {
463       using _Env = __cvref_t<_EnvId>;
464       static_assert(__nothrow_move_constructible<_Env>);
465 
466       struct __t {
467         using __id = __fwd;
468         using __fwd_env_t = __t;
469 
470         template <__forwarding_query _Query, class... _Args>
471           requires __queryable_with<_Env, _Query, _Args...>
STDEXEC_ATTRIBUTEstdexec::__env::__fwd::__t472         STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
473         constexpr auto query(_Query, _Args&&... __args) const
474           noexcept(__nothrow_queryable_with<_Env, _Query, _Args...>)
475             -> __query_result_t<_Env, _Query, _Args...> {
476           return __query<_Query>()(__env_, static_cast<_Args&&>(__args)...);
477         }
478 
479         STDEXEC_ATTRIBUTE(no_unique_address)
480         _Env __env_;
481       };
482     };
483 
484     template <class _Env>
485     concept __is_fwd_env = __same_as<_Env, typename _Env::__fwd_env_t>;
486 
487     struct __fwd_fn {
488       template <class _Env>
STDEXEC_ATTRIBUTEstdexec::__env::__fwd_fn489       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
490       constexpr auto operator()(_Env&& __env) const -> decltype(auto) {
491         if constexpr (__decays_to<_Env, env<>> || __is_fwd_env<__decay_t<_Env>>) {
492           return static_cast<_Env>(static_cast<_Env&&>(__env));
493         } else {
494           return __t<__fwd<__cvref_id<_Env>>>{static_cast<_Env&&>(__env)};
495         }
496       }
497     };
498 
499     template <class _Env>
500     using __fwd_env_t = __call_result_t<__fwd_fn, _Env>;
501 
502     template <class _EnvId, class _Query>
503     struct __without_ {
504       using _Env = __cvref_t<_EnvId>;
505       static_assert(__nothrow_move_constructible<_Env>);
506 
507       struct __t : __env_base<_Env> {
508         using __id = __without_;
509         using __env_base<_Env>::query;
510 
511         STDEXEC_ATTRIBUTE(nodiscard, host, device)
512         auto query(_Query) const noexcept = delete;
513       };
514     };
515 
516     struct __without_fn {
517       template <class _Env, class _Query>
operator ()stdexec::__env::__without_fn518       constexpr auto operator()(_Env&& __env, _Query) const noexcept -> auto {
519         if constexpr (__queryable_with<_Env, _Query>) {
520           using _Without = __t<__without_<__cvref_id<_Env>, _Query>>;
521           return _Without{static_cast<_Env&&>(__env)};
522         } else {
523           return static_cast<_Env&&>(__env);
524         }
525       }
526     };
527 
528     inline constexpr __without_fn __without{};
529 
530     template <class _Env, class _Query, class... _Tags>
531     using __without_t = __result_of<__without, _Env, _Query, _Tags...>;
532 
533     template <__nothrow_move_constructible _Fun>
534     struct __from {
535       using __t = __from;
536       using __id = __from;
537       STDEXEC_ATTRIBUTE(no_unique_address) _Fun __fun_;
538 
539       template <class _Query, class... _Args>
540         requires __callable<const _Fun&, _Query, _Args...>
STDEXEC_ATTRIBUTEstdexec::__env::__from541       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
542       auto query(_Query, _Args&&... __args) const
543         noexcept(__nothrow_callable<const _Fun&, _Query, _Args...>)
544           -> __call_result_t<const _Fun&, _Query, _Args...> {
545         return __fun_(_Query(), static_cast<_Args&&>(__args)...);
546       }
547     };
548 
549     template <class _Fun>
550     STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE __from(_Fun) -> __from<_Fun>;
551 
552     struct __join_fn {
553       template <class _Env1, class _Env2>
STDEXEC_ATTRIBUTEstdexec::__env::__join_fn554       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
555       constexpr auto operator()(_Env1&& __env1, _Env2&& __env2) const noexcept -> decltype(auto) {
556         if constexpr (__decays_to<_Env1, env<>>) {
557           return __fwd_fn()(static_cast<_Env2&&>(__env2));
558         } else if constexpr (__decays_to<_Env2, env<>>) {
559           return static_cast<_Env1>(static_cast<_Env1&&>(__env1));
560         } else {
561           return env<_Env1, __fwd_env_t<_Env2>>{
562             {static_cast<_Env1&&>(__env1)}, __fwd_fn()(static_cast<_Env2&&>(__env2))};
563         }
564       }
565     };
566 
567     inline constexpr __join_fn __join{};
568 
569     template <class _First, class... _Second>
570     using __join_env_t = __result_of<__join, _First, _Second...>;
571 
572     struct __as_root_env_fn {
573       template <class _Env>
STDEXEC_ATTRIBUTEstdexec::__env::__as_root_env_fn574       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
575       constexpr auto operator()(_Env __env) const noexcept
576         -> __join_env_t<__root_env, std::unwrap_reference_t<_Env>> {
577         return __join(__root_env{}, static_cast<std::unwrap_reference_t<_Env>&&>(__env));
578       }
579     };
580 
581     inline constexpr __as_root_env_fn __as_root_env{};
582 
583     template <class _Env>
584     using __as_root_env_t = __result_of<__as_root_env, _Env>;
585   } // namespace __env
586 
587   using __env::__join_env_t;
588   using __env::__fwd_env_t;
589 
590   /////////////////////////////////////////////////////////////////////////////
591   namespace __get_env {
592     template <class _EnvProvider>
593     concept __has_get_env = requires(const _EnvProvider& __env_provider) {
594       __env_provider.get_env();
595     };
596 
597     // For getting an execution environment from a receiver or the attributes from a sender.
598     struct get_env_t {
599       template <class _EnvProvider>
600         requires __has_get_env<_EnvProvider>
STDEXEC_ATTRIBUTEstdexec::__get_env::get_env_t601       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
602       constexpr auto operator()(const _EnvProvider& __env_provider) const noexcept
603         -> decltype(__env_provider.get_env()) {
604         static_assert(queryable<decltype(__env_provider.get_env())>);
605         static_assert(noexcept(__env_provider.get_env()), "get_env() members must be noexcept");
606         return __env_provider.get_env();
607       }
608 
609       template <class _EnvProvider>
610         requires(!__has_get_env<_EnvProvider>) && tag_invocable<get_env_t, const _EnvProvider&>
STDEXEC_ATTRIBUTEstdexec::__get_env::get_env_t611       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
612       constexpr auto operator()(const _EnvProvider& __env_provider) const noexcept
613         -> tag_invoke_result_t<get_env_t, const _EnvProvider&> {
614         static_assert(queryable<tag_invoke_result_t<get_env_t, const _EnvProvider&>>);
615         static_assert(nothrow_tag_invocable<get_env_t, const _EnvProvider&>);
616         return tag_invoke(*this, __env_provider);
617       }
618 
619       template <class _EnvProvider>
STDEXEC_ATTRIBUTEstdexec::__get_env::get_env_t620       STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
621       constexpr auto operator()(const _EnvProvider&) const noexcept -> env<> {
622         return {};
623       }
624     };
625   } // namespace __get_env
626 
627   using __get_env::get_env_t;
628   inline constexpr get_env_t get_env{};
629 
630   template <class _EnvProvider>
631   concept environment_provider = requires(_EnvProvider& __ep) {
632     { get_env(std::as_const(__ep)) } -> queryable;
633   };
634 
635   template <class _Scheduler, class _LateDomain = __none_such>
636   struct __sched_attrs {
637     using __t = __sched_attrs;
638     using __id = __sched_attrs;
639 
640     using __scheduler_t = __decay_t<_Scheduler>;
641     using __sched_domain_t = __query_result_or_t<get_domain_t, __scheduler_t, default_domain>;
642 
STDEXEC_ATTRIBUTEstdexec::__sched_attrs643     STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
644     constexpr auto query(get_completion_scheduler_t<set_value_t>) const noexcept -> __scheduler_t {
645       return __sched_;
646     }
647 
STDEXEC_ATTRIBUTEstdexec::__sched_attrs648     STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
649     constexpr auto query(get_domain_t) const noexcept -> __sched_domain_t {
650       return {};
651     }
652 
STDEXEC_ATTRIBUTEstdexec::__sched_attrs653     STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
654     constexpr auto query(get_domain_override_t) const noexcept -> _LateDomain
655       requires(!same_as<_LateDomain, __none_such>)
656     {
657       return {};
658     }
659 
660     _Scheduler __sched_;
STDEXEC_ATTRIBUTEstdexec::__sched_attrs661     STDEXEC_ATTRIBUTE(no_unique_address) _LateDomain __late_domain_ { };
662   };
663 
664   template <class _Scheduler, class _LateDomain = __none_such>
665   STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE __sched_attrs(_Scheduler, _LateDomain = {})
666     -> __sched_attrs<std::unwrap_reference_t<_Scheduler>, _LateDomain>;
667 
668   template <class _Scheduler>
669   struct __sched_env {
670     using __t = __sched_env;
671     using __id = __sched_env;
672 
673     using __scheduler_t = __decay_t<_Scheduler>;
674     using __sched_domain_t = __query_result_or_t<get_domain_t, __scheduler_t, default_domain>;
675 
STDEXEC_ATTRIBUTEstdexec::__sched_env676     STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
677     constexpr auto query(get_scheduler_t) const noexcept -> __scheduler_t {
678       return __sched_;
679     }
680 
STDEXEC_ATTRIBUTEstdexec::__sched_env681     STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device)
682     constexpr auto query(get_domain_t) const noexcept -> __sched_domain_t {
683       return {};
684     }
685 
686     _Scheduler __sched_;
687   };
688 
689   template <class _Scheduler>
690   STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE
691     __sched_env(_Scheduler) -> __sched_env<std::unwrap_reference_t<_Scheduler>>;
692 
693   using __env::__as_root_env_t;
694   using __env::__as_root_env;
695 
696   template <class _Env>
697   concept __is_root_env = requires(_Env&& __env) {
698     { __root_t{}(__env) } -> same_as<bool>;
699   };
700 
701   template <class _Sender, class... _Env>
702   concept __is_scheduler_affine = requires {
703     requires __is_scheduler_affine_t().operator()<env_of_t<_Sender>, _Env...>();
704   };
705 
706   // The attributes of a sender adaptor that does not introduce asynchrony.
707   template <class _Sender>
708   struct __sync_attrs {
709     using __t = __sync_attrs;
710     using __id = __sync_attrs;
711 
712     [[nodiscard]]
querystdexec::__sync_attrs713     constexpr auto query(__is_scheduler_affine_t) const noexcept {
714       return __mbool<__is_scheduler_affine<_Sender>>();
715     }
716 
717     template <class... _Env>
718     [[nodiscard]]
querystdexec::__sync_attrs719     constexpr auto query(get_completion_behavior_t, const _Env&...) const noexcept {
720       return get_completion_behavior<_Sender, _Env...>();
721     }
722 
723     template <__forwarding_query _Query, class... _Args>
724       requires __queryable_with<env_of_t<_Sender>, _Query, _Args...>
725     [[nodiscard]]
querystdexec::__sync_attrs726     constexpr auto query(_Query, _Args&&... __args) const
727       noexcept(__nothrow_queryable_with<env_of_t<_Sender>, _Query, _Args...>)
728         -> __query_result_t<env_of_t<_Sender>, _Query, _Args...> {
729       return __query<_Query>()(get_env(__sndr_), static_cast<_Args&&>(__args)...);
730     }
731 
732     const _Sender& __sndr_;
733   };
734 
735   template <class _Sender>
736   STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE __sync_attrs(const _Sender&) -> __sync_attrs<_Sender>;
737 
738 } // namespace stdexec
739 
740 STDEXEC_PRAGMA_POP()
741