From 482c2605918d3aadbd472960e207126a21809140 Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Wed, 4 Mar 2026 15:55:48 -0800 Subject: [PATCH] `affine_on(sndr)` should use `sndr`'s attrs for non-completion queries --- include/stdexec/__detail/__affine_on.hpp | 24 +++++-- .../__detail/__completion_behavior.hpp | 28 ++++++--- include/stdexec/__detail/__continues_on.hpp | 3 +- include/stdexec/__detail/__query.hpp | 22 ++++--- include/stdexec/__detail/__read_env.hpp | 13 +--- test/stdexec/queries/test_env.cpp | 63 ++++++++++++------- 6 files changed, 96 insertions(+), 57 deletions(-) diff --git a/include/stdexec/__detail/__affine_on.hpp b/include/stdexec/__detail/__affine_on.hpp index 67e5ee658..97e10dff8 100644 --- a/include/stdexec/__detail/__affine_on.hpp +++ b/include/stdexec/__detail/__affine_on.hpp @@ -118,14 +118,14 @@ namespace STDEXEC namespace __affine_on { - template + template struct __attrs { template - requires __queryable_with<_Attrs, __get_completion_behavior_t<_Tag>, _Env const &...> + requires __callable<__get_completion_behavior_t<_Tag>, env_of_t<_Sender>, _Env const &...> constexpr auto query(__get_completion_behavior_t<_Tag>, _Env const &...) const noexcept { - constexpr auto __behavior = __completion_behavior_of_v<_Tag, _Attrs, _Env...>; + constexpr auto __behavior = __get_completion_behavior<_Tag, _Sender, _Env...>(); // When the child sender completes inline, we can return "inline" here instead of // "__asynchronous_affine". @@ -138,16 +138,30 @@ namespace STDEXEC return __completion_behavior::__asynchronous_affine; } } + + template <__forwarding_query _Tag, class... _Args> + requires(!__completion_query<_Tag>) + && __queryable_with, _Tag, _Args const &...> + constexpr auto query(_Tag, _Args const &...) const noexcept + -> __query_result_t, _Tag, _Args const &...> + { + return __query_result_t, _Tag, _Args const &...>{}; + } + + _Sender const &__sndr_; }; + + template + STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE __attrs(_Sender const &) -> __attrs<_Sender>; } // namespace __affine_on template <> struct __sexpr_impl : __sexpr_defaults { static constexpr auto __get_attrs = // - [](__ignore, __ignore, _Child const &) noexcept + [](affine_on_t, __ignore, _Child const &__child) noexcept { - return __affine_on::__attrs>{}; + return __affine_on::__attrs{__child}; }; }; } // namespace STDEXEC diff --git a/include/stdexec/__detail/__completion_behavior.hpp b/include/stdexec/__detail/__completion_behavior.hpp index 8fc7b61f8..1b7f50a99 100644 --- a/include/stdexec/__detail/__completion_behavior.hpp +++ b/include/stdexec/__detail/__completion_behavior.hpp @@ -156,17 +156,11 @@ namespace STDEXEC template inline static constexpr __get_completion_behavior_t (*signature)(_Sig) = nullptr; - template + template STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - constexpr auto operator()(_Attrs const &, _Env const &...) const noexcept + constexpr auto operator()(_Attrs const &) const noexcept { - if constexpr (__member_queryable_with<_Attrs const &, - __get_completion_behavior_t<_Tag>, - _Env...>) - { - return __validate<_Attrs, _Env...>(); - } - else if constexpr (__member_queryable_with<_Attrs const &, __get_completion_behavior_t<_Tag>>) + if constexpr (__member_queryable_with<_Attrs const &, __get_completion_behavior_t<_Tag>>) { return __validate<_Attrs>(); } @@ -176,6 +170,22 @@ namespace STDEXEC } } + template + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto operator()([[maybe_unused]] _Attrs const &__attrs, _Env const &) const noexcept + { + if constexpr (__member_queryable_with<_Attrs const &, + __get_completion_behavior_t<_Tag>, + _Env const &>) + { + return __validate<_Attrs, _Env>(); + } + else + { + return (*this)(__attrs); + } + } + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) static constexpr auto query(forwarding_query_t) noexcept -> bool { diff --git a/include/stdexec/__detail/__continues_on.hpp b/include/stdexec/__detail/__continues_on.hpp index e6c8e53ca..809ddcf5c 100644 --- a/include/stdexec/__detail/__continues_on.hpp +++ b/include/stdexec/__detail/__continues_on.hpp @@ -297,8 +297,7 @@ namespace STDEXEC //! @brief Forwards other queries to the underlying sender's environment. //! @pre @c _Tag is a forwarding query but not a completion query. template <__forwarding_query _Tag, class... _Args> - requires(!__is_completion_query<_Tag>) - && __queryable_with, _Tag, _Args...> + requires(!__completion_query<_Tag>) && __queryable_with, _Tag, _Args...> [[nodiscard]] constexpr auto query(_Tag, _Args&&... __args) const noexcept(__nothrow_queryable_with, _Tag, _Args...>) diff --git a/include/stdexec/__detail/__query.hpp b/include/stdexec/__detail/__query.hpp index 9a50df70f..fce5c0312 100644 --- a/include/stdexec/__detail/__query.hpp +++ b/include/stdexec/__detail/__query.hpp @@ -137,15 +137,21 @@ namespace STDEXEC concept __forwarding_query = forwarding_query(_Tag{}); ////////////////////////////////////////////////////////////////////////////////////////// - // __is_completion_query + // __completion_query + namespace __detail + { + template + inline constexpr bool __is_completion_query_v = false; + template + inline constexpr bool __is_completion_query_v> = true; + template + inline constexpr bool __is_completion_query_v> = true; + template + inline constexpr bool __is_completion_query_v<__get_completion_behavior_t<_Tag>> = true; + } // namespace __detail + template - inline constexpr bool __is_completion_query = false; - template - inline constexpr bool __is_completion_query> = true; - template - inline constexpr bool __is_completion_query> = true; - template - inline constexpr bool __is_completion_query<__get_completion_behavior_t<_Tag>> = true; + concept __completion_query = __detail::__is_completion_query_v<_Query>; } // namespace STDEXEC STDEXEC_P2300_NAMESPACE_BEGIN() diff --git a/include/stdexec/__detail/__read_env.hpp b/include/stdexec/__detail/__read_env.hpp index f25e1fe29..b36e8dfd1 100644 --- a/include/stdexec/__detail/__read_env.hpp +++ b/include/stdexec/__detail/__read_env.hpp @@ -78,18 +78,9 @@ namespace STDEXEC template struct __attrs { - template - requires __callable<_Query, _Env> + template STDEXEC_ATTRIBUTE(nodiscard) - constexpr auto query(__get_completion_behavior_t, _Env const &) const noexcept - { - return __completion_behavior::__inline_completion; - } - - template - requires __callable<_Query, _Env> && (!__nothrow_callable<_Query, _Env>) - STDEXEC_ATTRIBUTE(nodiscard) - constexpr auto query(__get_completion_behavior_t, _Env const &) const noexcept + constexpr auto query(__get_completion_behavior_t<_SetTag>) const noexcept { return __completion_behavior::__inline_completion; } diff --git a/test/stdexec/queries/test_env.cpp b/test/stdexec/queries/test_env.cpp index b5926f48a..95906e78e 100644 --- a/test/stdexec/queries/test_env.cpp +++ b/test/stdexec/queries/test_env.cpp @@ -19,72 +19,91 @@ #include #include +namespace ex = STDEXEC; + namespace { - template - concept can_get_domain = requires(T const & t) { t.query(::STDEXEC::get_domain); }; + concept can_get_domain = requires(T const & t) { t.query(::ex::get_domain); }; namespace zero { - using env = ::STDEXEC::env<>; - static_assert(std::is_same_v<::STDEXEC::never_stop_token, ::STDEXEC::stop_token_of_t>); + using env = ::ex::env<>; + static_assert(std::is_same_v<::ex::never_stop_token, ::ex::stop_token_of_t>); static_assert(!can_get_domain); } // namespace zero namespace one { - using env = ::STDEXEC::env<::STDEXEC::env<>>; - static_assert(std::is_same_v<::STDEXEC::never_stop_token, ::STDEXEC::stop_token_of_t>); + using env = ::ex::env<::ex::env<>>; + static_assert(std::is_same_v<::ex::never_stop_token, ::ex::stop_token_of_t>); static_assert(!can_get_domain); } // namespace one namespace two { - using env = ::STDEXEC::env<::STDEXEC::env<>, ::STDEXEC::env<>>; - static_assert(std::is_same_v<::STDEXEC::never_stop_token, ::STDEXEC::stop_token_of_t>); + using env = ::ex::env<::ex::env<>, ::ex::env<>>; + static_assert(std::is_same_v<::ex::never_stop_token, ::ex::stop_token_of_t>); static_assert(!can_get_domain); } // namespace two namespace three { - using env = ::STDEXEC::env<::STDEXEC::env<>, ::STDEXEC::env<>, ::STDEXEC::env<>>; - static_assert(std::is_same_v<::STDEXEC::never_stop_token, ::STDEXEC::stop_token_of_t>); + using env = ::ex::env<::ex::env<>, ::ex::env<>, ::ex::env<>>; + static_assert(std::is_same_v<::ex::never_stop_token, ::ex::stop_token_of_t>); static_assert(!can_get_domain); } // namespace three // https://github.com/NVIDIA/stdexec/issues/1840 constexpr struct FwdFoo - : STDEXEC::__query - , STDEXEC::forwarding_query_t + : ex::__query + , ex::forwarding_query_t { - using STDEXEC::__query::operator(); + using ex::__query::operator(); } fwd_foo{}; - constexpr struct Foo : STDEXEC::__query + constexpr struct Foo : ex::__query { } foo{}; - constexpr struct Bar : STDEXEC::__query + constexpr struct Bar : ex::__query { } bar{}; constexpr bool test() { - auto env = STDEXEC::env{ - STDEXEC::env{STDEXEC::prop{fwd_foo, 42.}, STDEXEC::prop{foo, 'F'}}, - STDEXEC::prop{ bar, 31415} + auto env = ex::env{ + ex::env{ex::prop{fwd_foo, 42.}, ex::prop{foo, 'F'}}, + ex::prop{ bar, 31415} }; - static_assert(STDEXEC::__queryable_with); + static_assert(ex::__queryable_with); return fwd_foo(env) == 42. && bar(env) == 31415; } static_assert(test()); struct EnvOfThree - : STDEXEC::env, STDEXEC::prop>, - STDEXEC::prop> + : ex::env, ex::prop>, ex::prop> {}; - static_assert(STDEXEC::__queryable_with); + static_assert(ex::__queryable_with); + + struct non_dependent_attrs + { + [[nodiscard]] + auto query(ex::get_completion_scheduler_t) const noexcept + { + return ex::inline_scheduler{}; + } + }; + + TEST_CASE("env forwards non-dependent queries to the root environment", "[queries][env]") + { + auto attrs = ex::env{ + ex::prop{fwd_foo, 'F'}, + non_dependent_attrs{} + }; + auto sch = ex::get_completion_scheduler(attrs, ex::env{}); + CHECK(std::same_as); + } } // namespace