diff --git a/include/stdexec/__detail/__completion_behavior.hpp b/include/stdexec/__detail/__completion_behavior.hpp new file mode 100644 index 000000000..11db2e5c6 --- /dev/null +++ b/include/stdexec/__detail/__completion_behavior.hpp @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2025 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://llvm.org/LICENSE.txt + * + * 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 +#include +#include + +#include "__config.hpp" +#include "__concepts.hpp" +#include "__query.hpp" +#include "__meta.hpp" +#include "__execution_fwd.hpp" +#include "__type_traits.hpp" + +namespace stdexec { + ////////////////////////////////////////////////////////////////////////////////////////// + // get_completion_behavior + namespace __completion_behavior { + enum class completion_behavior : int { + unknown, ///< The completion behavior is unknown. + asynchronous, ///< The operation's completion will not happen on the calling thread before `start()` + ///< returns. + synchronous, ///< The operation's completion happens-before the return of `start()`. + inline_completion ///< The operation completes synchronously within `start()` on the same thread that called + ///< `start()`. + }; + + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto operator<=>(completion_behavior __a, completion_behavior __b) noexcept + -> std::strong_ordering { + return static_cast(__a) <=> static_cast(__b); + } + + template + using __constant_t = std::integral_constant; + + using __unknown_t = __constant_t; + using __asynchronous_t = __constant_t; + using __synchronous_t = __constant_t; + using __inline_completion_t = __constant_t; + } // namespace __completion_behavior + + struct min_t; + + struct completion_behavior { + private: + template <__completion_behavior::completion_behavior _CB> + using __constant_t = std::integral_constant<__completion_behavior::completion_behavior, _CB>; + + friend struct min_t; + + public: + struct unknown_t : __completion_behavior::__unknown_t { }; + struct asynchronous_t : __completion_behavior::__asynchronous_t { }; + struct synchronous_t : __completion_behavior::__synchronous_t { }; + struct inline_completion_t : __completion_behavior::__inline_completion_t { }; + + static constexpr unknown_t unknown{}; + static constexpr asynchronous_t asynchronous{}; + static constexpr synchronous_t synchronous{}; + static constexpr inline_completion_t inline_completion{}; + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // get_completion_behavior: A sender can define this attribute to describe the sender's + // completion behavior + struct get_completion_behavior_t + : __query> { + template + STDEXEC_ATTRIBUTE(always_inline, host, device) + static constexpr void __validate() noexcept { + static_assert( + __nothrow_queryable_with<_Attrs, get_completion_behavior_t, _Env...>, + "The get_completion_behavior query must be noexcept."); + static_assert( + convertible_to< + __query_result_t<_Attrs, get_completion_behavior_t, _Env...>, + __completion_behavior::completion_behavior + >, + "The get_completion_behavior query must return one of the static member variables in " + "execution::completion_behavior."); + } + + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + static constexpr auto query(forwarding_query_t) noexcept -> bool { + return true; + } + }; + + struct min_t { + using __completion_behavior_t = __completion_behavior::completion_behavior; + + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + static constexpr auto __minimum(std::initializer_list<__completion_behavior_t> __cbs) noexcept + -> __completion_behavior_t { + auto __result = __completion_behavior::completion_behavior::inline_completion; + for (auto __cb: __cbs) { + if (__cb < __result) { + __result = __cb; + } + } + return __result; + } + + template <__completion_behavior_t... _CBs> + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto operator()(completion_behavior::__constant_t<_CBs>...) const noexcept { + constexpr auto __behavior = __minimum({_CBs...}); + + if constexpr (__behavior == completion_behavior::unknown) { + return completion_behavior::unknown; + } else if constexpr (__behavior == completion_behavior::asynchronous) { + return completion_behavior::asynchronous; + } else if constexpr (__behavior == completion_behavior::synchronous) { + return completion_behavior::synchronous; + } else if constexpr (__behavior == completion_behavior::inline_completion) { + return completion_behavior::inline_completion; + } + STDEXEC_UNREACHABLE(); + } + }; + + constexpr min_t min{}; + + template + concept __completes_inline = + (__call_result_t{} + == completion_behavior::inline_completion); + + template + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + consteval auto get_completion_behavior() noexcept { + using __behavior_t = + __call_result_t, const _Env&...>; + return __behavior_t{}; + } + +} // namespace stdexec diff --git a/include/stdexec/__detail/__env.hpp b/include/stdexec/__detail/__env.hpp index 7214907df..45ecf68d4 100644 --- a/include/stdexec/__detail/__env.hpp +++ b/include/stdexec/__detail/__env.hpp @@ -18,7 +18,9 @@ #include "__execution_fwd.hpp" #include "__concepts.hpp" +#include "__completion_behavior.hpp" #include "__meta.hpp" +#include "__query.hpp" #include "__stop_token.hpp" #include "__tag_invoke.hpp" // #include "__tuple.hpp" @@ -33,132 +35,9 @@ STDEXEC_PRAGMA_IGNORE_EDG(probable_guiding_friend) STDEXEC_PRAGMA_IGNORE_EDG(type_qualifiers_ignored_on_reference) namespace stdexec { - // [exec.queries.queryable] - template - concept queryable = destructible; - - template - concept __member_queryable_with = - queryable<_Env> - && requires(const _Env& __env, const _Query& __query, __declfn<_Args&&>... __args) { - { __env.query(__query, __args()...) }; - }; - - template - concept __nothrow_member_queryable_with = - __member_queryable_with<_Env, _Query, _Args...> - && requires(const _Env& __env, const _Query& __query, __declfn<_Args&&>... __args) { - { __env.query(__query, __args()...) } noexcept; - }; - - template - using __member_query_result_t = - decltype(__declval().query(__declval(), __declval<_Args>()...)); - - constexpr __none_such __no_default{}; - - template - concept __has_validation = requires { _Query::template __validate<_Env, _Args...>(); }; - - template > - struct __query // NOLINT(bugprone-crtp-constructor-accessibility) - : __query<_Query, __no_default, _Transform> { - using __query<_Query, __no_default, _Transform>::operator(); - - template - STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - constexpr auto operator()(__ignore, _Args&&...) const noexcept // - -> __mcall1<_Transform, __mtypeof<_Default>> { - return _Default; - } - }; - - template - struct __query<_Query, __no_default, _Transform> { - template - static inline constexpr _Query (*signature)(Sig) = nullptr; - - // Query with a .query member function: - 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, _Qy, _Args...>) - -> __mcall1<_Transform, __member_query_result_t<_Env, _Qy, _Args...>> { - if constexpr (__has_validation<_Query, _Env, _Args...>) { - _Query::template __validate<_Env, _Args...>(); - } - return __env.query(_Query(), static_cast<_Args&&>(__args)...); - } - - // Query with tag_invoke (legacy): - 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<_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...>(); - } - return tag_invoke(_Query(), __env, static_cast<_Args&&>(__args)...); - } - }; - - template - concept __queryable_with = __callable<__query<_Query>, _Env&, _Args...>; - - template - concept __nothrow_queryable_with = __nothrow_callable<__query<_Query>, _Env&, _Args...>; - - template - using __query_result_t = __call_result_t<__query<_Query>, _Env&, _Args...>; - ////////////////////////////////////////////////////////////////////////////////////////////////// // [exec.queries] namespace __queries { - template - concept __is_bool_constant = requires { typename __mbool<_Tp::value>; }; - - 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()); - } else if constexpr (tag_invocable) { - using __result_t = tag_invoke_result_t; - // If this a integral type wrapper, unpack it and return the value. Otherwise, return the - // result of the tag_invoke call expression. - if constexpr (__is_bool_constant<__result_t>) { - return __result_t::value; - } else { - return tag_invoke(*this, _Query()); - } - } else { - return derived_from<_Query, forwarding_query_t>; - } - } - }; - - 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); - } - - 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)); - } - }; - struct execute_may_block_caller_t : __query { template STDEXEC_ATTRIBUTE(always_inline, host, device) @@ -200,7 +79,7 @@ namespace stdexec { 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 { + static consteval auto query(forwarding_query_t) noexcept -> bool { return true; } }; @@ -220,7 +99,7 @@ namespace stdexec { 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 { + static consteval auto query(forwarding_query_t) noexcept -> bool { return true; } }; @@ -240,7 +119,7 @@ namespace stdexec { } STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - static constexpr auto query(forwarding_query_t) noexcept -> bool { + static consteval auto query(forwarding_query_t) noexcept -> bool { return true; } }; @@ -262,7 +141,7 @@ namespace stdexec { } STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - static constexpr auto query(forwarding_query_t) noexcept -> bool { + static consteval auto query(forwarding_query_t) noexcept -> bool { return true; } }; @@ -274,7 +153,7 @@ namespace stdexec { 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 { + static consteval auto query(forwarding_query_t) noexcept -> bool { return true; } }; @@ -292,7 +171,7 @@ namespace stdexec { } STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - static constexpr auto query(forwarding_query_t) noexcept -> bool { + static consteval auto query(forwarding_query_t) noexcept -> bool { return true; } }; @@ -310,33 +189,66 @@ namespace stdexec { } STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - static constexpr auto query(forwarding_query_t) noexcept -> bool { + static consteval auto query(forwarding_query_t) noexcept -> bool { return false; } }; struct __is_scheduler_affine_t { - template + template + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + static consteval auto __ensure_bool_constant() noexcept { + if constexpr (__is_bool_constant<_Result>) { + return static_cast(_Result::value); + } else { + static_assert( + __is_bool_constant<_Result>, + "The __is_scheduler_affine query must be one of the following forms:\n" + " static constexpr bool query(__is_scheduler_affine_t) noexcept;\n" + " bool_constant query(__is_scheduler_affine_t) const noexcept;\n" + " bool_constant query(__is_scheduler_affine_t, const Env&) const noexcept;\n"); + } + } + + template + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + consteval auto operator()() const noexcept -> bool { + return __completes_inline<_Attrs, const _Env&...>; + } + + template <__queryable_with<__is_scheduler_affine_t> _Attrs, class... _Env> 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); - } else if constexpr (requires { __env.query(*this); }) { - return __env.query(*this); + consteval auto operator()() const noexcept -> bool { + if constexpr (__statically_queryable_with<_Attrs, __is_scheduler_affine_t>) { + return _Attrs::query(__is_scheduler_affine_t()); } else { - return false; + return __ensure_bool_constant<__query_result_t<_Attrs, __is_scheduler_affine_t>>(); } } + template + requires __queryable_with<_Attrs, __is_scheduler_affine_t, const _Env&> STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) - static constexpr auto query(forwarding_query_t) noexcept -> bool { + consteval auto operator()() const noexcept -> bool { + using __result_t = __query_result_t<_Attrs, __is_scheduler_affine_t, const _Env&>; + return __ensure_bool_constant<__result_t>(); + } + + template + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + consteval auto operator()(const _Attrs&, const _Env&...) const noexcept -> bool { + return operator()<_Attrs, _Env...>(); + } + + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + static consteval 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 { + static consteval auto query(forwarding_query_t) noexcept -> bool { return false; } }; @@ -352,8 +264,6 @@ namespace stdexec { }; } // namespace __queries - using __queries::forwarding_query_t; - using __queries::query_or_t; using __queries::execute_may_block_caller_t; using __queries::get_forward_progress_guarantee_t; using __queries::get_allocator_t; @@ -370,8 +280,6 @@ namespace stdexec { using __queries::__root_t; using __queries::__root_env; - inline constexpr forwarding_query_t forwarding_query{}; - inline constexpr query_or_t query_or{}; // NOT TO SPEC inline constexpr execute_may_block_caller_t execute_may_block_caller{}; inline constexpr get_forward_progress_guarantee_t get_forward_progress_guarantee{}; inline constexpr get_scheduler_t get_scheduler{}; @@ -394,9 +302,6 @@ namespace stdexec { get_completion_scheduler{}; #endif - template - concept __forwarding_query = forwarding_query(_Query{}); - inline constexpr get_domain_t get_domain{}; inline constexpr get_domain_override_t get_domain_override{}; @@ -443,15 +348,6 @@ namespace stdexec { auto get_env() const noexcept -> const _Env&; }; - template - concept __statically_queryable_with_i = requires(_Query __q, _Args&&... __args) { - std::remove_reference_t<_Env>::query(__q, static_cast<_Args &&>(__args)...); - }; - - template - concept __statically_queryable_with = __member_queryable_with<_Env, _Query, _Args...> - && __statically_queryable_with_i<_Env, _Query, _Args...>; - template struct __prop_like { template @@ -802,10 +698,43 @@ namespace stdexec { { __root_t{}(__env) } -> same_as; }; - template + template concept __is_scheduler_affine = requires { - requires std::remove_reference_t>::query(__is_scheduler_affine_t{}); + requires __is_scheduler_affine_t().operator(), _Env...>(); }; + + // The attributes of a sender adaptor that does not introduce asynchrony. + template + struct __sync_attrs { + using __t = __sync_attrs; + using __id = __sync_attrs; + + [[nodiscard]] + constexpr auto query(__is_scheduler_affine_t) const noexcept { + return __mbool<__is_scheduler_affine<_Sender>>(); + } + + template + [[nodiscard]] + constexpr auto query(get_completion_behavior_t, const _Env&...) const noexcept { + return get_completion_behavior<_Sender, _Env...>(); + } + + template <__forwarding_query _Query, class... _Args> + requires __queryable_with, _Query, _Args...> + [[nodiscard]] + constexpr auto query(_Query, _Args&&... __args) const + noexcept(__nothrow_queryable_with, _Query, _Args...>) + -> __query_result_t, _Query, _Args...> { + return __query<_Query>()(get_env(__sndr_), static_cast<_Args&&>(__args)...); + } + + const _Sender& __sndr_; + }; + + template + STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE __sync_attrs(const _Sender&) -> __sync_attrs<_Sender>; + } // namespace stdexec STDEXEC_PRAGMA_POP() diff --git a/include/stdexec/__detail/__inline_scheduler.hpp b/include/stdexec/__detail/__inline_scheduler.hpp index e9d3463fe..28e6943c4 100644 --- a/include/stdexec/__detail/__inline_scheduler.hpp +++ b/include/stdexec/__detail/__inline_scheduler.hpp @@ -41,8 +41,8 @@ namespace stdexec { struct __attrs { STDEXEC_ATTRIBUTE(nodiscard, host, device) - static constexpr auto query(__is_scheduler_affine_t) noexcept -> bool { - return true; + static constexpr auto query(get_completion_behavior_t) noexcept { + return completion_behavior::inline_completion; } STDEXEC_ATTRIBUTE(nodiscard, host, device) diff --git a/include/stdexec/__detail/__just.hpp b/include/stdexec/__detail/__just.hpp index 1edff558c..d51014b78 100644 --- a/include/stdexec/__detail/__just.hpp +++ b/include/stdexec/__detail/__just.hpp @@ -34,8 +34,8 @@ namespace stdexec { struct __impl : __sexpr_defaults { using __tag_t = typename _JustTag::__tag_t; - static constexpr auto get_attrs = - [](__ignore) noexcept -> cprop<__is_scheduler_affine_t, true> { + static constexpr auto get_attrs = [](__ignore) noexcept + -> cprop { return {}; }; diff --git a/include/stdexec/__detail/__query.hpp b/include/stdexec/__detail/__query.hpp new file mode 100644 index 000000000..8476bd71f --- /dev/null +++ b/include/stdexec/__detail/__query.hpp @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2025 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://llvm.org/LICENSE.txt + * + * 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 + +#include "__config.hpp" +#include "__concepts.hpp" +#include "__execution_fwd.hpp" +#include "__tag_invoke.hpp" +#include "__type_traits.hpp" + +namespace stdexec { + // [exec.queries.queryable] + template + concept queryable = destructible; + + template + concept __member_queryable_with = + queryable<_Env> + && requires(const _Env& __env, const _Query& __query, __declfn<_Args&&>... __args) { + { __env.query(__query, __args()...) }; + }; + + template + concept __nothrow_member_queryable_with = + __member_queryable_with<_Env, _Query, _Args...> + && requires(const _Env& __env, const _Query& __query, __declfn<_Args&&>... __args) { + { __env.query(__query, __args()...) } noexcept; + }; + + template + using __member_query_result_t = + decltype(__declval().query(__declval(), __declval<_Args>()...)); + + constexpr __none_such __no_default{}; + + template + concept __has_validation = requires { _Query::template __validate<_Env, _Args...>(); }; + + template > + struct __query // NOLINT(bugprone-crtp-constructor-accessibility) + : __query<_Query, __no_default, _Transform> { + using __query<_Query, __no_default, _Transform>::operator(); + + template + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto operator()(__ignore, _Args&&...) const noexcept // + -> __mcall1<_Transform, __mtypeof<_Default>> { + return _Default; + } + }; + + template + struct __query<_Query, __no_default, _Transform> { + template + static inline constexpr _Query (*signature)(Sig) = nullptr; + + // Query with a .query member function: + 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, _Qy, _Args...>) + -> __mcall1<_Transform, __member_query_result_t<_Env, _Qy, _Args...>> { + if constexpr (__has_validation<_Query, _Env, _Args...>) { + _Query::template __validate<_Env, _Args...>(); + } + return __env.query(_Query(), static_cast<_Args&&>(__args)...); + } + + // Query with tag_invoke (legacy): + 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<_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...>(); + } + return tag_invoke(_Query(), __env, static_cast<_Args&&>(__args)...); + } + }; + + template + concept __queryable_with = __callable<__query<_Query>, _Env&, _Args...>; + + template + concept __nothrow_queryable_with = __nothrow_callable<__query<_Query>, _Env&, _Args...>; + + template + using __query_result_t = __call_result_t<__query<_Query>, _Env&, _Args...>; + + template + concept __statically_queryable_with_impl = requires(_Query __q, _Args&&... __args) { + std::remove_reference_t<_Env>::query(__q, static_cast<_Args&&>(__args)...); + }; + + template + concept __statically_queryable_with = __queryable_with<_Env, _Query, _Args...> + && __statically_queryable_with_impl<_Env, _Query, _Args...>; + + ////////////////////////////////////////////////////////////////////////////////////////////////// + // [exec.queries] + namespace __queries { + template + concept __is_bool_constant = requires { typename __mbool<_Tp::value>; }; + + struct forwarding_query_t { + template + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + consteval auto operator()(_Query) const noexcept -> bool { + if constexpr (__queryable_with<_Query, forwarding_query_t>) { + return __query()(_Query()); + } else { + return derived_from<_Query, forwarding_query_t>; + } + } + }; + + struct query_or_t { + template + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto operator()(_Query, _Queryable&&, _Default&& __default, _Args&&...) const + noexcept(__nothrow_move_constructible<_Default>) -> _Default { + return static_cast<_Default&&>(__default); + } + + template + requires __callable<_Query, _Queryable, _Args...> + STDEXEC_ATTRIBUTE(nodiscard, always_inline, host, device) + constexpr auto + operator()(_Query __query, _Queryable&& __queryable, _Default&&, _Args&&... __args) const + noexcept(__nothrow_callable<_Query, _Queryable, _Args...>) + -> __call_result_t<_Query, _Queryable, _Args...> { + return static_cast<_Query&&>( + __query)(static_cast<_Queryable&&>(__queryable), static_cast<_Args&&>(__args)...); + } + }; + } // namespace __queries + + using __queries::forwarding_query_t; + using __queries::query_or_t; + + inline constexpr forwarding_query_t forwarding_query{}; + inline constexpr query_or_t query_or{}; // NOT TO SPEC + + template + concept __forwarding_query = forwarding_query(_Tag{}); +} // namespace stdexec diff --git a/include/stdexec/__detail/__read_env.hpp b/include/stdexec/__detail/__read_env.hpp index a687e1e4a..bbae7b9ae 100644 --- a/include/stdexec/__detail/__read_env.hpp +++ b/include/stdexec/__detail/__read_env.hpp @@ -82,8 +82,8 @@ namespace stdexec { using __completions_t = __minvoke<__mtry_catch_q<__read::__completions_t, __q<__query_failed_error>>, _Tag, _Env>; - static constexpr auto get_attrs = - [](__ignore) noexcept -> cprop<__is_scheduler_affine_t, true> { + static constexpr auto get_attrs = [](__ignore) noexcept + -> cprop { return {}; }; diff --git a/include/stdexec/__detail/__then.hpp b/include/stdexec/__detail/__then.hpp index a035a110d..f35e69c6a 100644 --- a/include/stdexec/__detail/__then.hpp +++ b/include/stdexec/__detail/__then.hpp @@ -61,9 +61,7 @@ namespace stdexec { struct __then_impl : __sexpr_defaults { static constexpr auto get_attrs = [](__ignore, const _Child& __child) noexcept { - return __env::__join( - cprop<__is_scheduler_affine_t, __is_scheduler_affine<_Child>>{}, - stdexec::get_env(__child)); + return __sync_attrs{__child}; }; static constexpr auto get_completion_signatures = diff --git a/include/stdexec/__detail/__write_env.hpp b/include/stdexec/__detail/__write_env.hpp index 8e7614cbc..740088b9e 100644 --- a/include/stdexec/__detail/__write_env.hpp +++ b/include/stdexec/__detail/__write_env.hpp @@ -56,9 +56,7 @@ namespace stdexec { struct __write_env_impl : __sexpr_defaults { static constexpr auto get_attrs = [](__ignore, const _Child& __child) noexcept { - return __env::__join( - cprop<__is_scheduler_affine_t, __is_scheduler_affine<_Child>>{}, - stdexec::get_env(__child)); + return __sync_attrs{__child}; }; static constexpr auto get_env = [](__ignore, const auto& __state, const auto& __rcvr) noexcept diff --git a/test/stdexec/algos/adaptors/test_then.cpp b/test/stdexec/algos/adaptors/test_then.cpp index d1d1c5ad0..5a0223972 100644 --- a/test/stdexec/algos/adaptors/test_then.cpp +++ b/test/stdexec/algos/adaptors/test_then.cpp @@ -25,6 +25,7 @@ namespace ex = stdexec; // Check that the `then` algorithm is correctly forwarding the __is_scheduler_affine query static_assert(ex::__is_scheduler_affine); +static_assert(ex::__completes_inline); namespace { TEST_CASE("then returns a sender", "[adaptors][then]") { diff --git a/test/stdexec/cpos/test_cpo_upon_error.cpp b/test/stdexec/cpos/test_cpo_upon_error.cpp index e3aa9c185..7f5e766c4 100644 --- a/test/stdexec/cpos/test_cpo_upon_error.cpp +++ b/test/stdexec/cpos/test_cpo_upon_error.cpp @@ -46,7 +46,7 @@ namespace { } { - ex::get_completion_scheduler(ex::get_env(snd)); + void(ex::get_completion_scheduler(ex::get_env(snd))); constexpr scope_t scope = decltype(ex::upon_error(snd, f))::scope; STATIC_REQUIRE(scope == scope_t::scheduler); }