From 0ce499b0b007b8a86a7f0ed25c1132614945bd8f Mon Sep 17 00:00:00 2001 From: Cra3z <3324654761@qq.com> Date: Tue, 27 Oct 2026 20:26:32 +0800 Subject: [PATCH 1/2] Forward non-completion queries through `affine` and `starts_on` attrs --- include/stdexec/__detail/__affine.hpp | 14 ++-- include/stdexec/__detail/__continues_on.hpp | 3 +- include/stdexec/__detail/__starts_on.hpp | 11 +++ .../algos/adaptors/test_continues_on.cpp | 7 ++ test/stdexec/algos/adaptors/test_on.cpp | 7 ++ .../stdexec/algos/adaptors/test_starts_on.cpp | 7 ++ test/test_common/non_completion_query.hpp | 68 +++++++++++++++++++ 7 files changed, 109 insertions(+), 8 deletions(-) create mode 100644 test/test_common/non_completion_query.hpp diff --git a/include/stdexec/__detail/__affine.hpp b/include/stdexec/__detail/__affine.hpp index f0f81f21a..be7c97be7 100644 --- a/include/stdexec/__detail/__affine.hpp +++ b/include/stdexec/__detail/__affine.hpp @@ -163,13 +163,15 @@ namespace STDEXEC } } - 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 &...> + template <__forwarding_query _Query, class... _Args> + requires(!__completion_query<_Query>) + && __queryable_with, _Query, _Args...> + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto query(_Query __query, _Args &&...__args) const + noexcept(__nothrow_queryable_with, _Query, _Args...>) + -> __query_result_t, _Query, _Args...> { - return __query_result_t, _Tag, _Args const &...>{}; + return __query(STDEXEC::get_env(__sndr_), static_cast<_Args &&>(__args)...); } _Sender const &__sndr_; diff --git a/include/stdexec/__detail/__continues_on.hpp b/include/stdexec/__detail/__continues_on.hpp index 54887a115..b5af696d0 100644 --- a/include/stdexec/__detail/__continues_on.hpp +++ b/include/stdexec/__detail/__continues_on.hpp @@ -360,8 +360,7 @@ namespace STDEXEC } STDEXEC_CATCH_ALL { - STDEXEC::set_error(static_cast<_State&&>(__state).__rcvr_, std::current_exception()); - return; + __state.__data_.__emplace_from(__mktuple, set_error, std::current_exception()); } } diff --git a/include/stdexec/__detail/__starts_on.hpp b/include/stdexec/__detail/__starts_on.hpp index 59b6d8aac..94eb08e07 100644 --- a/include/stdexec/__detail/__starts_on.hpp +++ b/include/stdexec/__detail/__starts_on.hpp @@ -87,6 +87,17 @@ namespace STDEXEC { return {}; } + + template <__forwarding_query _Query, class... _Args> + requires(!__completion_query<_Query>) + && __queryable_with, _Query, _Args...> + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto query(_Query __query, _Args&&... __args) const + noexcept(__nothrow_queryable_with, _Query, _Args...>) + -> __query_result_t, _Query, _Args...> + { + return __query(__attr_, static_cast<_Args&&>(__args)...); + } }; } // namespace __starts_on diff --git a/test/stdexec/algos/adaptors/test_continues_on.cpp b/test/stdexec/algos/adaptors/test_continues_on.cpp index bc0f0d79e..d0a3bb9e4 100644 --- a/test/stdexec/algos/adaptors/test_continues_on.cpp +++ b/test/stdexec/algos/adaptors/test_continues_on.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #if STDEXEC_USE_MODULES() import std; @@ -80,6 +81,12 @@ namespace (void) snd; } + TEST_CASE("continues_on forwards non-completion queries", "[adaptors][continues_on]") + { + auto snd = ex::continues_on(test_common::non_completion_query_sender{}, inline_scheduler{}); + CHECK(test_common::non_completion_query(ex::get_env(snd), 41) == 42); + } + TEST_CASE("continues_on with environment returns a sender", "[adaptors][continues_on]") { auto snd = ex::continues_on(ex::just(13), inline_scheduler{}); diff --git a/test/stdexec/algos/adaptors/test_on.cpp b/test/stdexec/algos/adaptors/test_on.cpp index eb896ebc8..f6d2eb3c5 100644 --- a/test/stdexec/algos/adaptors/test_on.cpp +++ b/test/stdexec/algos/adaptors/test_on.cpp @@ -21,6 +21,7 @@ #include #include #include +#include namespace ex = STDEXEC; @@ -47,6 +48,12 @@ namespace (void) snd; } + TEST_CASE("STDEXEC::on forwards non-completion queries", "[adaptors][on]") + { + auto snd = ex::on(inline_scheduler{}, test_common::non_completion_query_sender{}); + CHECK(test_common::non_completion_query(ex::get_env(snd), 41) == 42); + } + TEST_CASE("STDEXEC::on with environment returns a sender", "[adaptors][on]") { auto snd = ex::on(inline_scheduler{}, ex::just(13)); diff --git a/test/stdexec/algos/adaptors/test_starts_on.cpp b/test/stdexec/algos/adaptors/test_starts_on.cpp index 2b5891dd7..fe9b4faf5 100644 --- a/test/stdexec/algos/adaptors/test_starts_on.cpp +++ b/test/stdexec/algos/adaptors/test_starts_on.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #if STDEXEC_USE_MODULES() import std; @@ -48,6 +49,12 @@ namespace (void) snd; } + TEST_CASE("starts_on forwards non-completion queries", "[adaptors][starts_on]") + { + auto snd = ex::starts_on(inline_scheduler{}, test_common::non_completion_query_sender{}); + CHECK(test_common::non_completion_query(ex::get_env(snd), 41) == 42); + } + TEST_CASE("starts_on with environment returns a sender", "[adaptors][starts_on]") { auto snd = ex::starts_on(inline_scheduler{}, ex::just(13)); diff --git a/test/test_common/non_completion_query.hpp b/test/test_common/non_completion_query.hpp new file mode 100644 index 000000000..905c8df58 --- /dev/null +++ b/test/test_common/non_completion_query.hpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2026 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 with LLVM Exceptions + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +namespace test_common +{ + namespace ex = STDEXEC; + + struct non_completion_query_t : ex::__query, ex::forwarding_query_t + { + using ex::__query::operator(); + }; + + inline constexpr non_completion_query_t non_completion_query{}; + + struct non_completion_query_env + { + constexpr auto query(non_completion_query_t, int value) const noexcept -> int + { + return value + 1; + } + }; + + struct non_completion_query_sender + { + using sender_concept = ex::sender_tag; + using completion_signatures = ex::completion_signatures; + + template + struct operation + { + using operation_state_concept = ex::operation_state_tag; + + void start() & noexcept + { + ex::set_value(static_cast(receiver)); + } + + Receiver receiver; + }; + + template + auto connect(Receiver&& receiver) const -> operation> + { + return {static_cast(receiver)}; + } + + auto get_env() const noexcept -> non_completion_query_env + { + return {}; + } + }; +} // namespace test_common From 8e5091437e6e5c471077f8fee970fd7de382c4d3 Mon Sep 17 00:00:00 2001 From: Cra3z <3324654761@qq.com> Date: Sat, 31 Oct 2026 11:35:22 +0800 Subject: [PATCH 2/2] Make some sender-attributes SFINAE-friendly --- include/stdexec/__detail/__affine.hpp | 7 +++---- include/stdexec/__detail/__continues_on.hpp | 3 +-- include/stdexec/__detail/__on.hpp | 7 +++---- include/stdexec/__detail/__sequence.hpp | 6 ++---- include/stdexec/__detail/__starts_on.hpp | 7 +++---- test/stdexec/algos/adaptors/test_continues_on.cpp | 2 +- test/stdexec/algos/adaptors/test_on.cpp | 2 +- test/stdexec/algos/adaptors/test_starts_on.cpp | 2 +- test/test_common/non_completion_query.hpp | 6 ++++-- 9 files changed, 19 insertions(+), 23 deletions(-) diff --git a/include/stdexec/__detail/__affine.hpp b/include/stdexec/__detail/__affine.hpp index be7c97be7..839b06a84 100644 --- a/include/stdexec/__detail/__affine.hpp +++ b/include/stdexec/__detail/__affine.hpp @@ -167,11 +167,10 @@ namespace STDEXEC requires(!__completion_query<_Query>) && __queryable_with, _Query, _Args...> STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - constexpr auto query(_Query __query, _Args &&...__args) const - noexcept(__nothrow_queryable_with, _Query, _Args...>) - -> __query_result_t, _Query, _Args...> + constexpr auto query(_Query, _Args &&...__args) const + noexcept(__nothrow_queryable_with, _Query, _Args...>) -> decltype(auto) { - return __query(STDEXEC::get_env(__sndr_), static_cast<_Args &&>(__args)...); + return STDEXEC::get_env(__sndr_).query(_Query(), static_cast<_Args &&>(__args)...); } _Sender const &__sndr_; diff --git a/include/stdexec/__detail/__continues_on.hpp b/include/stdexec/__detail/__continues_on.hpp index b5af696d0..cf96b8c60 100644 --- a/include/stdexec/__detail/__continues_on.hpp +++ b/include/stdexec/__detail/__continues_on.hpp @@ -268,8 +268,7 @@ namespace STDEXEC && __queryable_with, _Query, _Args...> [[nodiscard]] constexpr auto query(_Query, _Args&&... __args) const - noexcept(__nothrow_queryable_with, _Query, _Args...>) - -> __query_result_t, _Query, _Args...> + noexcept(__nothrow_queryable_with, _Query, _Args...>) -> decltype(auto) { return __attrs_.query(_Query(), static_cast<_Args&&>(__args)...); } diff --git a/include/stdexec/__detail/__on.hpp b/include/stdexec/__detail/__on.hpp index 258fc24e3..83fbd1d1e 100644 --- a/include/stdexec/__detail/__on.hpp +++ b/include/stdexec/__detail/__on.hpp @@ -110,11 +110,10 @@ namespace STDEXEC requires(!__completion_query<_Query>) && __queryable_with, _Query, _Args...> STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - constexpr auto query(_Query __query, _Args&&... __args) const - noexcept(__nothrow_queryable_with, _Query, _Args...>) - -> __query_result_t, _Query, _Args...> + constexpr auto query(_Query, _Args&&... __args) const + noexcept(__nothrow_queryable_with, _Query, _Args...>) -> decltype(auto) { - return __query(STDEXEC::get_env(__child_), static_cast<_Args&&>(__args)...); + return STDEXEC::get_env(__child_).query(_Query(), static_cast<_Args&&>(__args)...); } _Child const & __child_; diff --git a/include/stdexec/__detail/__sequence.hpp b/include/stdexec/__detail/__sequence.hpp index ce7626748..73e16379a 100644 --- a/include/stdexec/__detail/__sequence.hpp +++ b/include/stdexec/__detail/__sequence.hpp @@ -79,8 +79,7 @@ namespace STDEXEC requires __queryable_with<_env_t, _Query, _Args...> STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) constexpr auto query(_Query, _Args &&...__args) const - noexcept(__nothrow_queryable_with<_env_t, _Query, _Args...>) - -> __query_result_t<_env_t, _Query, _Args...> + noexcept(__nothrow_queryable_with<_env_t, _Query, _Args...>) -> decltype(auto) { return STDEXEC::get_env(__sndr_).query(_Query(), static_cast<_Args &&>(__args)...); } @@ -174,8 +173,7 @@ namespace STDEXEC requires(!__completion_query<_Query>) && __queryable_with<__joined_t, _Query, _Args...> [[nodiscard]] constexpr auto operator()(_Query, _Args &&...__args) const - noexcept(__nothrow_queryable_with<__joined_t, _Query, _Args...>) - -> __query_result_t<__joined_t, _Query, _Args...> + noexcept(__nothrow_queryable_with<__joined_t, _Query, _Args...>) -> decltype(auto) { return __joined_t{STDEXEC::get_env(__sndr2_), STDEXEC::get_env(__sndr1_)} .query(_Query(), static_cast<_Args &&>(__args)...); diff --git a/include/stdexec/__detail/__starts_on.hpp b/include/stdexec/__detail/__starts_on.hpp index 94eb08e07..4f0166149 100644 --- a/include/stdexec/__detail/__starts_on.hpp +++ b/include/stdexec/__detail/__starts_on.hpp @@ -92,11 +92,10 @@ namespace STDEXEC requires(!__completion_query<_Query>) && __queryable_with, _Query, _Args...> STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - constexpr auto query(_Query __query, _Args&&... __args) const - noexcept(__nothrow_queryable_with, _Query, _Args...>) - -> __query_result_t, _Query, _Args...> + constexpr auto query(_Query, _Args&&... __args) const + noexcept(__nothrow_queryable_with, _Query, _Args...>) -> decltype(auto) { - return __query(__attr_, static_cast<_Args&&>(__args)...); + return __attr_.query(_Query(), static_cast<_Args&&>(__args)...); } }; } // namespace __starts_on diff --git a/test/stdexec/algos/adaptors/test_continues_on.cpp b/test/stdexec/algos/adaptors/test_continues_on.cpp index d0a3bb9e4..e1481b7ef 100644 --- a/test/stdexec/algos/adaptors/test_continues_on.cpp +++ b/test/stdexec/algos/adaptors/test_continues_on.cpp @@ -20,10 +20,10 @@ #include #include #include +#include #include #include #include -#include #if STDEXEC_USE_MODULES() import std; diff --git a/test/stdexec/algos/adaptors/test_on.cpp b/test/stdexec/algos/adaptors/test_on.cpp index f6d2eb3c5..d6359c795 100644 --- a/test/stdexec/algos/adaptors/test_on.cpp +++ b/test/stdexec/algos/adaptors/test_on.cpp @@ -19,9 +19,9 @@ #include #include +#include #include #include -#include namespace ex = STDEXEC; diff --git a/test/stdexec/algos/adaptors/test_starts_on.cpp b/test/stdexec/algos/adaptors/test_starts_on.cpp index fe9b4faf5..b6229da6f 100644 --- a/test/stdexec/algos/adaptors/test_starts_on.cpp +++ b/test/stdexec/algos/adaptors/test_starts_on.cpp @@ -20,10 +20,10 @@ #include #include #include +#include #include #include #include -#include #if STDEXEC_USE_MODULES() import std; diff --git a/test/test_common/non_completion_query.hpp b/test/test_common/non_completion_query.hpp index 905c8df58..a68ff94cf 100644 --- a/test/test_common/non_completion_query.hpp +++ b/test/test_common/non_completion_query.hpp @@ -21,7 +21,9 @@ namespace test_common { namespace ex = STDEXEC; - struct non_completion_query_t : ex::__query, ex::forwarding_query_t + struct non_completion_query_t + : ex::__query + , ex::forwarding_query_t { using ex::__query::operator(); }; @@ -38,7 +40,7 @@ namespace test_common struct non_completion_query_sender { - using sender_concept = ex::sender_tag; + using sender_concept = ex::sender_tag; using completion_signatures = ex::completion_signatures; template