From bcaaaf2b5f232a4d385de0d5e8c8200113390b85 Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Sat, 27 Sep 2025 23:09:36 -0700 Subject: [PATCH] simplify the implementation of `env<>` --- include/stdexec/__detail/__env.hpp | 426 ++++++++---------- include/stdexec/__detail/__read_env.hpp | 12 +- include/stdexec/__detail/__schedulers.hpp | 9 +- .../stdexec/concepts/test_concepts_sender.cpp | 2 +- 4 files changed, 202 insertions(+), 247 deletions(-) diff --git a/include/stdexec/__detail/__env.hpp b/include/stdexec/__detail/__env.hpp index f941d1b23..7214907df 100644 --- a/include/stdexec/__detail/__env.hpp +++ b/include/stdexec/__detail/__env.hpp @@ -21,7 +21,7 @@ #include "__meta.hpp" #include "__stop_token.hpp" #include "__tag_invoke.hpp" -#include "__tuple.hpp" +// #include "__tuple.hpp" #include // IWYU pragma: keep for std::terminate #include // IWYU pragma: keep for unwrap_reference_t @@ -51,9 +51,9 @@ namespace stdexec { { __env.query(__query, __args()...) } noexcept; }; - template - using __member_query_result_t = decltype(__declval() - .query(__declval(), __declval<_Args>()...)); + template + using __member_query_result_t = + decltype(__declval().query(__declval(), __declval<_Args>()...)); constexpr __none_such __no_default{}; @@ -66,8 +66,9 @@ namespace stdexec { using __query<_Query, __no_default, _Transform>::operator(); template - constexpr auto - operator()(__ignore, _Args&&...) const noexcept -> __mcall1<_Transform, __mtypeof<_Default>> { + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto operator()(__ignore, _Args&&...) const noexcept // + -> __mcall1<_Transform, __mtypeof<_Default>> { return _Default; } }; @@ -78,12 +79,12 @@ namespace stdexec { static inline constexpr _Query (*signature)(Sig) = nullptr; // Query with a .query member function: - template - requires __member_queryable_with - STDEXEC_ATTRIBUTE(always_inline) + template + requires __member_queryable_with + STDEXEC_ATTRIBUTE(always_inline, host, device) constexpr auto operator()(const _Env& __env, _Args&&... __args) const - noexcept(__nothrow_member_queryable_with<_Env, _Q, _Args...>) - -> __mcall1<_Transform, __member_query_result_t<_Env, _Q, _Args...>> { + noexcept(__nothrow_member_queryable_with<_Env, _Qy, _Args...>) + -> __mcall1<_Transform, __member_query_result_t<_Env, _Qy, _Args...>> { if constexpr (__has_validation<_Query, _Env, _Args...>) { _Query::template __validate<_Env, _Args...>(); } @@ -91,13 +92,13 @@ namespace stdexec { } // Query with tag_invoke (legacy): - template - requires(!__member_queryable_with) - && tag_invocable<_Q, const _Env&, _Args...> - STDEXEC_ATTRIBUTE(always_inline) + template + requires(!__member_queryable_with) + && tag_invocable<_Qy, const _Env&, _Args...> + STDEXEC_ATTRIBUTE(always_inline, host, device) constexpr auto operator()(const _Env& __env, _Args&&... __args) const - noexcept(nothrow_tag_invocable<_Q, const _Env&, _Args...>) - -> __mcall1<_Transform, tag_invoke_result_t<_Q, const _Env&, _Args...>> { + noexcept(nothrow_tag_invocable<_Qy, const _Env&, _Args...>) + -> __mcall1<_Transform, tag_invoke_result_t<_Qy, const _Env&, _Args...>> { if constexpr (__has_validation<_Query, _Env, _Args...>) { _Query::template __validate<_Env, _Args...>(); } @@ -106,13 +107,13 @@ namespace stdexec { }; template - concept __queryable_with = __callable<__query<_Query>, const _Env&, _Args...>; + concept __queryable_with = __callable<__query<_Query>, _Env&, _Args...>; template - concept __nothrow_queryable_with = __nothrow_callable<__query<_Query>, const _Env&, _Args...>; + concept __nothrow_queryable_with = __nothrow_callable<__query<_Query>, _Env&, _Args...>; template - using __query_result_t = __call_result_t<__query<_Query>, const _Env&, _Args...>; + using __query_result_t = __call_result_t<__query<_Query>, _Env&, _Args...>; ////////////////////////////////////////////////////////////////////////////////////////////////// // [exec.queries] @@ -122,6 +123,7 @@ namespace stdexec { struct forwarding_query_t { template + STDEXEC_ATTRIBUTE(nodiscard, host, device) consteval auto operator()(_Query) const noexcept -> bool { if constexpr (__member_queryable_with<_Query, forwarding_query_t>) { return _Query().query(forwarding_query_t()); @@ -142,6 +144,7 @@ namespace stdexec { struct query_or_t { template + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) constexpr auto operator()(_Query, _Queryable&&, _Default&& __default) const noexcept(__nothrow_constructible_from<_Default, _Default&&>) -> _Default { return static_cast<_Default&&>(__default); @@ -149,6 +152,7 @@ namespace stdexec { template requires __callable<_Query, _Queryable> + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) constexpr auto operator()(_Query __query, _Queryable&& __queryable, _Default&&) const noexcept(__nothrow_callable<_Query, _Queryable>) -> __call_result_t<_Query, _Queryable> { return static_cast<_Query&&>(__query)(static_cast<_Queryable&&>(__queryable)); @@ -157,7 +161,7 @@ namespace stdexec { struct execute_may_block_caller_t : __query { template - STDEXEC_ATTRIBUTE(always_inline) + STDEXEC_ATTRIBUTE(always_inline, host, device) static constexpr void __validate() noexcept { static_assert(same_as>); static_assert(__nothrow_callable); @@ -171,7 +175,7 @@ namespace stdexec { __q1<__decay_t> > { template - STDEXEC_ATTRIBUTE(always_inline) + STDEXEC_ATTRIBUTE(always_inline, host, device) static constexpr void __validate() noexcept { using __result_t = __call_result_t; static_assert(same_as); @@ -186,12 +190,16 @@ namespace stdexec { struct get_scheduler_t : __query { using __query::operator(); - template - auto operator()() const noexcept; + template + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto + operator()() const noexcept; // defined in __read_env.hpp // defined in __read_env.hpp template - static constexpr void __validate() noexcept; + STDEXEC_ATTRIBUTE(always_inline, host, device) + static constexpr void __validate() noexcept; // defined in __schedulers.hpp + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) static constexpr auto query(forwarding_query_t) noexcept -> bool { return true; } @@ -203,12 +211,15 @@ namespace stdexec { struct get_delegation_scheduler_t : __query { using __query::operator(); - template - auto operator()() const noexcept; + template + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto operator()() const noexcept; // defined in __read_env.hpp template - static constexpr void __validate() noexcept; + STDEXEC_ATTRIBUTE(always_inline, host, device) + static constexpr void __validate() noexcept; // defined in __schedulers.hpp + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) static constexpr auto query(forwarding_query_t) noexcept -> bool { return true; } @@ -217,15 +228,18 @@ namespace stdexec { struct get_allocator_t : __query { using __query::operator(); - template - auto operator()() const noexcept; + template + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto operator()() const noexcept; // defined in __read_env.hpp template + STDEXEC_ATTRIBUTE(always_inline, host, device) static constexpr void __validate() noexcept { static_assert(__nothrow_callable); static_assert(__allocator_c<__call_result_t>); } + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) static constexpr auto query(forwarding_query_t) noexcept -> bool { return true; } @@ -236,25 +250,30 @@ namespace stdexec { struct get_stop_token_t : __get_stop_token_t { using __get_stop_token_t::operator(); - template - auto operator()() const noexcept; + template + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto operator()() const noexcept; // defined in __read_env.hpp template + STDEXEC_ATTRIBUTE(always_inline, host, device) static constexpr void __validate() noexcept { static_assert(__nothrow_callable); static_assert(stoppable_token<__call_result_t>); } + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) static constexpr auto query(forwarding_query_t) noexcept -> bool { return true; } }; - template <__completion_tag _Tag> - struct get_completion_scheduler_t : __query> { + template <__completion_tag _Query> + struct get_completion_scheduler_t : __query> { template - static constexpr void __validate() noexcept; + STDEXEC_ATTRIBUTE(always_inline, host, device) + static constexpr void __validate() noexcept; // defined in __schedulers.hpp + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) static constexpr auto query(forwarding_query_t) noexcept -> bool { return true; } @@ -262,6 +281,7 @@ namespace stdexec { struct get_domain_t : __query> { template + STDEXEC_ATTRIBUTE(always_inline, host, device) static constexpr void __validate() noexcept { static_assert( __nothrow_callable, @@ -271,6 +291,7 @@ namespace stdexec { "Customizations of get_domain must return a class type."); } + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) static constexpr auto query(forwarding_query_t) noexcept -> bool { return true; } @@ -278,6 +299,7 @@ namespace stdexec { struct get_domain_override_t : __query> { template + STDEXEC_ATTRIBUTE(always_inline, host, device) static constexpr void __validate() noexcept { static_assert( __nothrow_callable, @@ -287,6 +309,7 @@ namespace stdexec { "Customizations of get_domain_override must return a class type."); } + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) static constexpr auto query(forwarding_query_t) noexcept -> bool { return false; } @@ -294,6 +317,7 @@ namespace stdexec { struct __is_scheduler_affine_t { template + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) constexpr auto operator()(const _Env& __env) const noexcept { if constexpr (requires { _Env::query(*this); }) { return _Env::query(*this); @@ -304,12 +328,14 @@ namespace stdexec { } } + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) static constexpr auto query(forwarding_query_t) noexcept -> bool { return false; } }; struct __root_t : __query<__root_t> { + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) static constexpr auto query(forwarding_query_t) noexcept -> bool { return false; } @@ -319,7 +345,7 @@ namespace stdexec { using __t = __root_env; using __id = __root_env; - [[nodiscard]] + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) static constexpr auto query(__root_t) noexcept -> bool { return true; } @@ -356,8 +382,8 @@ namespace stdexec { inline constexpr get_allocator_t get_allocator{}; inline constexpr get_stop_token_t get_stop_token{}; #if !STDEXEC_GCC() || defined(__OPTIMIZE_SIZE__) - template <__completion_tag _Tag> - inline constexpr get_completion_scheduler_t<_Tag> get_completion_scheduler{}; + template <__completion_tag _Query> + inline constexpr get_completion_scheduler_t<_Query> get_completion_scheduler{}; #else template <> inline constexpr get_completion_scheduler_t get_completion_scheduler{}; @@ -368,14 +394,14 @@ namespace stdexec { get_completion_scheduler{}; #endif - template - concept __forwarding_query = forwarding_query(_Tag{}); + template + concept __forwarding_query = forwarding_query(_Query{}); inline constexpr get_domain_t get_domain{}; inline constexpr get_domain_override_t get_domain_override{}; - template - using __query_result_or_t = __call_result_t; + template + using __query_result_or_t = __call_result_t; namespace __env { template @@ -386,12 +412,14 @@ namespace stdexec { template struct __with_await_transform { template + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) auto await_transform(_Ty&& __value) noexcept -> _Ty&& { return static_cast<_Ty&&>(__value); } template requires __has_as_awaitable_member<_Ty, _Promise&> + STDEXEC_ATTRIBUTE(nodiscard, host, device) auto await_transform(_Ty&& __value) noexcept(noexcept(__declval<_Ty>().as_awaitable(__declval<_Promise&>()))) -> decltype(__declval<_Ty>().as_awaitable(__declval<_Promise&>())) { @@ -401,6 +429,7 @@ namespace stdexec { template requires(!__has_as_awaitable_member<_Ty, _Promise&>) && tag_invocable + STDEXEC_ATTRIBUTE(nodiscard, host, device) auto await_transform(_Ty&& __value) noexcept(nothrow_tag_invocable) -> tag_invoke_result_t { @@ -410,6 +439,7 @@ namespace stdexec { template struct __promise : __with_await_transform<__promise<_Env>> { + STDEXEC_ATTRIBUTE(nodiscard, host, device) auto get_env() const noexcept -> const _Env&; }; @@ -425,7 +455,7 @@ namespace stdexec { template struct __prop_like { template - STDEXEC_ATTRIBUTE(nodiscard) + STDEXEC_ATTRIBUTE(noreturn, nodiscard, host, device) constexpr auto query(_Query) const noexcept -> const ValueType& { STDEXEC_TERMINATE(); } @@ -439,13 +469,13 @@ namespace stdexec { static_assert(__callable<_Query, __prop_like<_Value>>); - STDEXEC_ATTRIBUTE(no_unique_address) _Query __query; - - STDEXEC_ATTRIBUTE(no_unique_address) _Value __value; - - STDEXEC_ATTRIBUTE(nodiscard) constexpr auto query(_Query) const noexcept -> const _Value& { + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto query(_Query) const noexcept -> const _Value& { return __value; } + + STDEXEC_ATTRIBUTE(no_unique_address) _Query __query; + STDEXEC_ATTRIBUTE(no_unique_address) _Value __value; }; template @@ -457,14 +487,15 @@ namespace stdexec { using __t = cprop; using __id = cprop; - STDEXEC_ATTRIBUTE(nodiscard) + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) static constexpr auto query(_Query) noexcept { return _Value; } }; - // utility for joining multiple environments - template + ////////////////////////////////////////////////////////////////////// + // env + template struct env; template <> @@ -472,224 +503,136 @@ namespace stdexec { using __t = env; using __id = env; + STDEXEC_ATTRIBUTE(nodiscard, host, device) auto query() const = delete; }; - template - struct env { + template + struct env : Env { using __t = env; using __id = env; - - __tuple_for<_Envs..., env<>> __tup_; - - // return a reference to the first child env for which - // __queryable_with<_Envs, _Query, _Args...> is true. - template - STDEXEC_ATTRIBUTE(always_inline) - static constexpr auto __get_1st(const env& __self) noexcept -> decltype(auto) { - // NOLINTNEXTLINE (modernize-avoid-c-arrays) - constexpr bool __flags[] = {__queryable_with<_Envs, _Query, _Args...>..., true}; - constexpr std::size_t __idx = __pos_of(__flags, __flags + sizeof...(_Envs)); - return __self.__tup_.template __get<__idx>(__self.__tup_); - } - - template - using __1st_env_t = decltype(env::__get_1st<_Query, _Args...>(__declval())); - - // NOT TO SPEC: a static query memfn for those envs that have a static query memfn. - // This is useful for constexpr evaluation of queries. - template - requires __statically_queryable_with<__1st_env_t<_Query, _Args...>, _Query, _Args...> - STDEXEC_ATTRIBUTE(nodiscard, always_inline) - static constexpr auto query(_Query, _Args&&... __args) - noexcept(__nothrow_queryable_with<__1st_env_t<_Query, _Args...>, _Query, _Args...>) - -> decltype(auto) { - return std::remove_reference_t<__1st_env_t<_Query, _Args...>>::query( - _Query(), static_cast<_Args&&>(__args)...); - } - - template - requires(!__statically_queryable_with<__1st_env_t<_Query, _Args...>, _Query, _Args...>) - && __queryable_with<__1st_env_t<_Query, _Args...>, _Query, _Args...> - STDEXEC_ATTRIBUTE(nodiscard, always_inline) - constexpr auto query(_Query, _Args&&... __args) const - noexcept(__nothrow_queryable_with<__1st_env_t<_Query, _Args...>, _Query, _Args...>) - -> decltype(auto) { - return __query<_Query>()( - env::__get_1st<_Query, _Args...>(*this), static_cast<_Args&&>(__args)...); - } }; - // specialization for two envs to avoid warnings about elided braces - template - struct env<_Env0, _Env1> { + template + struct env { using __t = env; using __id = env; - STDEXEC_ATTRIBUTE(no_unique_address) _Env0 __env0_; - STDEXEC_ATTRIBUTE(no_unique_address) _Env1 __env1_; - - // return a reference to the first child env for which - // __member_queryable_with<_Envs, _Query, _Args...> is true. - template - STDEXEC_ATTRIBUTE(always_inline) - static constexpr auto __get_1st(const env& __self) noexcept -> decltype(auto) { - if constexpr (__queryable_with<_Env0, _Query, _Args...>) { - return (__self.__env0_); - } else { - return (__self.__env1_); - } + template + requires __queryable_with + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto query(Query, _Args&&... __args) const + noexcept(__nothrow_queryable_with) + -> __query_result_t { + return __query()(env_, static_cast<_Args&&>(__args)...); } - template - using __1st_env_t = decltype(env::__get_1st<_Query, _Args...>(__declval())); - - // NOT TO SPEC: a static query memfn for those envs that have a static query memfn. - // This is useful for constexpr evaluation of queries. - template - requires __statically_queryable_with<__1st_env_t<_Query, _Args...>, _Query, _Args...> - STDEXEC_ATTRIBUTE(nodiscard, always_inline) - static constexpr auto query(_Query, _Args&&... __args) - noexcept(__nothrow_queryable_with<__1st_env_t<_Query, _Args...>, _Query, _Args...>) - -> decltype(auto) { - return std::remove_reference_t<__1st_env_t<_Query, _Args...>>::query( - _Query(), static_cast<_Args&&>(__args)...); - } - - template - requires(!__statically_queryable_with<__1st_env_t<_Query, _Args...>, _Query, _Args...>) - && __queryable_with<__1st_env_t<_Query, _Args...>, _Query, _Args...> - STDEXEC_ATTRIBUTE(nodiscard, always_inline) - constexpr auto query(_Query, _Args&&... __args) const - noexcept(__nothrow_queryable_with<__1st_env_t<_Query, _Args...>, _Query, _Args...>) - -> decltype(auto) { - return __query<_Query>()( - env::__get_1st<_Query, _Args...>(*this), static_cast<_Args&&>(__args)...); - } + Env& env_; }; - template - STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE env(_Envs...) -> env...>; - - template - struct __with { - using __t = __with; - using __id = __with; - STDEXEC_ATTRIBUTE(no_unique_address) _Value __value_; + template + using __env_base = __if_c, env, Env>; - __with() = default; + template + struct env : __env_base { + using __t = env; + using __id = env; - constexpr explicit __with(_Value __value) noexcept(__nothrow_decay_copyable<_Value>) - : __value_(static_cast<_Value&&>(__value)) { - } + using __env_base::query; - constexpr explicit __with(_Value __value, _Tag, _Tags...) - noexcept(__nothrow_decay_copyable<_Value>) - : __value_(static_cast<_Value&&>(__value)) { + template + requires(!__queryable_with) + && __queryable_with + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto query(Query, _Args&&... __args) const + noexcept(__nothrow_queryable_with) + -> __query_result_t { + return __query()(env2_, static_cast<_Args&&>(__args)...); } - template <__one_of<_Tag, _Tags...> _Key> - auto query(_Key) const noexcept -> const _Value& { - return __value_; - } + STDEXEC_ATTRIBUTE(no_unique_address) Env2 env2_; }; - template - __with(_Value, _Tag, _Tags...) -> __with<_Value, _Tag, _Tags...>; - - template - struct __fwd_base { - using __fwd_env_t = _Env; + template + struct env : env, Envs...> { + using __t = env; + using __id = env; }; + template + STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE env(_Envs...) -> env...>; + template struct __fwd { using _Env = __cvref_t<_EnvId>; static_assert(__nothrow_move_constructible<_Env>); - struct __t : __fwd_base<_Env> { + struct __t { using __id = __fwd; - STDEXEC_ATTRIBUTE(no_unique_address) _Env __env_; - -#if STDEXEC_GCC() && STDEXEC_GCC_VERSION < 12'00 - using __cvref_env_t = std::add_const_t<_Env>&; -#else - using __cvref_env_t = const _Env&; -#endif - - template <__forwarding_query _Tag> - requires __queryable_with<__cvref_env_t, _Tag> - auto query(_Tag) const noexcept(__nothrow_queryable_with<__cvref_env_t, _Tag>) - -> __query_result_t<__cvref_env_t, _Tag> { - return __query<_Tag>()(__env_); + using __fwd_env_t = __t; + + template <__forwarding_query _Query, class... _Args> + requires __queryable_with<_Env, _Query, _Args...> + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto query(_Query, _Args&&... __args) const + noexcept(__nothrow_queryable_with<_Env, _Query, _Args...>) + -> __query_result_t<_Env, _Query, _Args...> { + return __query<_Query>()(__env_, static_cast<_Args&&>(__args)...); } + + STDEXEC_ATTRIBUTE(no_unique_address) + _Env __env_; }; }; template - concept __is_fwd_env = same_as<_Env, typename _Env::__fwd_env_t>; + concept __is_fwd_env = __same_as<_Env, typename _Env::__fwd_env_t>; struct __fwd_fn { template - auto operator()(_Env&& __env) const -> decltype(auto) { - if constexpr (__is_fwd_env<__decay_t<_Env>>) { + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto operator()(_Env&& __env) const -> decltype(auto) { + if constexpr (__decays_to<_Env, env<>> || __is_fwd_env<__decay_t<_Env>>) { return static_cast<_Env>(static_cast<_Env&&>(__env)); } else { - return __t<__fwd<__cvref_id<_Env>>>{{}, static_cast<_Env&&>(__env)}; + return __t<__fwd<__cvref_id<_Env>>>{static_cast<_Env&&>(__env)}; } } - - auto operator()(env<>) const -> env<> { - return {}; - } }; template using __fwd_env_t = __call_result_t<__fwd_fn, _Env>; - template + template struct __without_ { using _Env = __cvref_t<_EnvId>; static_assert(__nothrow_move_constructible<_Env>); - struct __t { + struct __t : __env_base<_Env> { using __id = __without_; - _Env __env_; - -#if STDEXEC_GCC() && STDEXEC_GCC_VERSION < 12'00 - using __cvref_env_t = std::add_const_t<_Env>&; -#else - using __cvref_env_t = const _Env&; -#endif - - auto query(_Tag) const noexcept = delete; + using __env_base<_Env>::query; - template - requires __queryable_with<__cvref_env_t, _Key> - STDEXEC_ATTRIBUTE(always_inline) - auto query(_Key) const noexcept(__nothrow_queryable_with<__cvref_env_t, _Key>) - -> decltype(auto) { - return __query<_Key>()(__env_); - } + STDEXEC_ATTRIBUTE(nodiscard, host, device) + auto query(_Query) const noexcept = delete; }; }; struct __without_fn { - template - constexpr auto operator()(_Env&& __env, _Tag) const noexcept -> decltype(auto) { - if constexpr (__queryable_with<_Env, _Tag>) { - using _Without = __t<__without_<__cvref_id<_Env>, _Tag>>; + template + constexpr auto operator()(_Env&& __env, _Query) const noexcept -> auto { + if constexpr (__queryable_with<_Env, _Query>) { + using _Without = __t<__without_<__cvref_id<_Env>, _Query>>; return _Without{static_cast<_Env&&>(__env)}; } else { - return static_cast<_Env>(static_cast<_Env&&>(__env)); + return static_cast<_Env&&>(__env); } } }; inline constexpr __without_fn __without{}; - template - using __without_t = __result_of<__without, _Env, _Tag, _Tags...>; + template + using __without_t = __result_of<__without, _Env, _Query, _Tags...>; template <__nothrow_move_constructible _Fun> struct __from { @@ -697,36 +640,31 @@ namespace stdexec { using __id = __from; STDEXEC_ATTRIBUTE(no_unique_address) _Fun __fun_; - template - requires __callable - auto query(_Tag) const noexcept(__nothrow_callable) - -> __call_result_t { - return __fun_(_Tag()); + template + requires __callable + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + auto query(_Query, _Args&&... __args) const + noexcept(__nothrow_callable) + -> __call_result_t { + return __fun_(_Query(), static_cast<_Args&&>(__args)...); } }; template - __from(_Fun) -> __from<_Fun>; + STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE __from(_Fun) -> __from<_Fun>; struct __join_fn { - auto operator()(env<>, env<>) const noexcept -> env<> { - return {}; - } - - template - auto operator()(_Env&& __env, env<> = {}) const noexcept -> _Env { - return static_cast<_Env&&>(__env); - } - - template - auto operator()(env<>, _Env&& __env) const noexcept -> decltype(auto) { - return __fwd_fn()(static_cast<_Env&&>(__env)); - } - - template - auto operator()(_First&& __first, _Second&& __second) const noexcept - -> env<_First, __fwd_env_t<_Second>> { - return {static_cast<_First&&>(__first), __fwd_fn()(static_cast<_Second&&>(__second))}; + template + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto operator()(_Env1&& __env1, _Env2&& __env2) const noexcept -> decltype(auto) { + if constexpr (__decays_to<_Env1, env<>>) { + return __fwd_fn()(static_cast<_Env2&&>(__env2)); + } else if constexpr (__decays_to<_Env2, env<>>) { + return static_cast<_Env1>(static_cast<_Env1&&>(__env1)); + } else { + return env<_Env1, __fwd_env_t<_Env2>>{ + {static_cast<_Env1&&>(__env1)}, __fwd_fn()(static_cast<_Env2&&>(__env2))}; + } } }; @@ -737,6 +675,7 @@ namespace stdexec { struct __as_root_env_fn { template + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) constexpr auto operator()(_Env __env) const noexcept -> __join_env_t<__root_env, std::unwrap_reference_t<_Env>> { return __join(__root_env{}, static_cast&&>(__env)); @@ -763,7 +702,7 @@ namespace stdexec { struct get_env_t { template requires __has_get_env<_EnvProvider> - STDEXEC_ATTRIBUTE(always_inline) + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) constexpr auto operator()(const _EnvProvider& __env_provider) const noexcept -> decltype(__env_provider.get_env()) { static_assert(queryable); @@ -773,7 +712,7 @@ namespace stdexec { template requires(!__has_get_env<_EnvProvider>) && tag_invocable - STDEXEC_ATTRIBUTE(always_inline) + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) constexpr auto operator()(const _EnvProvider& __env_provider) const noexcept -> tag_invoke_result_t { static_assert(queryable>); @@ -782,6 +721,7 @@ namespace stdexec { } template + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) constexpr auto operator()(const _EnvProvider&) const noexcept -> env<> { return {}; } @@ -803,26 +743,30 @@ namespace stdexec { using __scheduler_t = __decay_t<_Scheduler>; using __sched_domain_t = __query_result_or_t; - _Scheduler __sched_; - STDEXEC_ATTRIBUTE(no_unique_address) _LateDomain __late_domain_ { }; - auto query(get_completion_scheduler_t) const noexcept -> __scheduler_t { + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto query(get_completion_scheduler_t) const noexcept -> __scheduler_t { return __sched_; } + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) constexpr auto query(get_domain_t) const noexcept -> __sched_domain_t { return {}; } + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) constexpr auto query(get_domain_override_t) const noexcept -> _LateDomain requires(!same_as<_LateDomain, __none_such>) { return {}; } + + _Scheduler __sched_; + STDEXEC_ATTRIBUTE(no_unique_address) _LateDomain __late_domain_ { }; }; template - __sched_attrs(_Scheduler, _LateDomain = {}) + STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE __sched_attrs(_Scheduler, _LateDomain = {}) -> __sched_attrs, _LateDomain>; template @@ -832,19 +776,23 @@ namespace stdexec { using __scheduler_t = __decay_t<_Scheduler>; using __sched_domain_t = __query_result_or_t; - _Scheduler __sched_; - auto query(get_scheduler_t) const noexcept -> __scheduler_t { + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto query(get_scheduler_t) const noexcept -> __scheduler_t { return __sched_; } - auto query(get_domain_t) const noexcept -> __sched_domain_t { + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto query(get_domain_t) const noexcept -> __sched_domain_t { return {}; } + + _Scheduler __sched_; }; template - __sched_env(_Scheduler) -> __sched_env>; + STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE + __sched_env(_Scheduler) -> __sched_env>; using __env::__as_root_env_t; using __env::__as_root_env; diff --git a/include/stdexec/__detail/__read_env.hpp b/include/stdexec/__detail/__read_env.hpp index bc1acad95..a687e1e4a 100644 --- a/include/stdexec/__detail/__read_env.hpp +++ b/include/stdexec/__detail/__read_env.hpp @@ -141,22 +141,26 @@ namespace stdexec { namespace __queries { template - inline auto get_scheduler_t::operator()() const noexcept { + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto get_scheduler_t::operator()() const noexcept { return read_env(get_scheduler); } template - inline auto get_delegation_scheduler_t::operator()() const noexcept { + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto get_delegation_scheduler_t::operator()() const noexcept { return read_env(get_delegation_scheduler); } template - inline auto get_allocator_t::operator()() const noexcept { + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto get_allocator_t::operator()() const noexcept { return read_env(get_allocator); } template - inline auto get_stop_token_t::operator()() const noexcept { + //STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto get_stop_token_t::operator()() const noexcept { return read_env(get_stop_token); } } // namespace __queries diff --git a/include/stdexec/__detail/__schedulers.hpp b/include/stdexec/__detail/__schedulers.hpp index df0a12192..e1d141371 100644 --- a/include/stdexec/__detail/__schedulers.hpp +++ b/include/stdexec/__detail/__schedulers.hpp @@ -95,20 +95,23 @@ namespace stdexec { namespace __queries { template - inline constexpr void get_scheduler_t::__validate() noexcept { + STDEXEC_ATTRIBUTE(always_inline, host, device) + constexpr void get_scheduler_t::__validate() noexcept { static_assert(__nothrow_callable); static_assert(scheduler<__call_result_t>); } template - inline constexpr void get_delegation_scheduler_t::__validate() noexcept { + STDEXEC_ATTRIBUTE(always_inline, host, device) + constexpr void get_delegation_scheduler_t::__validate() noexcept { static_assert(__nothrow_callable); static_assert(scheduler<__call_result_t>); } template <__completion_tag _Tag> template - inline constexpr void get_completion_scheduler_t<_Tag>::__validate() noexcept { + STDEXEC_ATTRIBUTE(always_inline, host, device) + constexpr void get_completion_scheduler_t<_Tag>::__validate() noexcept { static_assert(__nothrow_callable, const _Env&>); static_assert(scheduler<__call_result_t, const _Env&>>); } diff --git a/test/stdexec/concepts/test_concepts_sender.cpp b/test/stdexec/concepts/test_concepts_sender.cpp index 954641c14..197d15e01 100644 --- a/test/stdexec/concepts/test_concepts_sender.cpp +++ b/test/stdexec/concepts/test_concepts_sender.cpp @@ -255,7 +255,7 @@ namespace { }; TEST_CASE("r5 sender emits deprecated diagnostics", "[concepts][sender]") { - ex::get_env(my_r5_sender0{}); + void(ex::get_env(my_r5_sender0{})); static_assert(ex::sender); static_assert(std::same_as< decltype(ex::get_completion_signatures(my_r5_sender0{}, ex::env<>{})),