From f99b1fee95fb01eb008c9266b876559fa912f01a Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Sun, 11 Jan 2026 15:25:21 -0800 Subject: [PATCH 1/4] migrate most senders to use `static consteval` version of `get_completion_signatures` --- examples/algorithms/retry.hpp | 7 +- examples/algorithms/then.hpp | 11 +- examples/benchmark/static_thread_pool_old.hpp | 4 +- include/exec/__detail/__basic_sequence.hpp | 2 +- include/exec/any_sender_of.hpp | 4 +- include/exec/async_scope.hpp | 10 +- include/exec/at_coroutine_exit.hpp | 4 +- include/exec/completion_signatures.hpp | 15 +- include/exec/env.hpp | 8 +- include/exec/finally.hpp | 190 +++++++++--------- include/exec/fork_join.hpp | 7 +- include/exec/libdispatch_queue.hpp | 4 +- include/exec/materialize.hpp | 8 +- include/exec/on_coro_disposition.hpp | 2 +- include/exec/repeat_n.hpp | 10 +- include/exec/reschedule.hpp | 4 +- include/exec/sequence.hpp | 3 +- include/exec/sequence/transform_each.hpp | 5 +- include/exec/sequence_senders.hpp | 43 ++-- include/exec/start_now.hpp | 3 +- include/exec/static_thread_pool.hpp | 4 +- include/exec/system_context.hpp | 4 +- include/exec/variant_sender.hpp | 3 +- include/exec/when_any.hpp | 12 +- include/execpools/thread_pool_base.hpp | 3 +- include/nvexec/nvtx.cuh | 4 +- include/nvexec/stream/algorithm_base.cuh | 4 +- include/nvexec/stream/bulk.cuh | 8 +- include/nvexec/stream/continues_on.cuh | 17 +- include/nvexec/stream/ensure_started.cuh | 4 +- include/nvexec/stream/launch.cuh | 4 +- include/nvexec/stream/let_xxx.cuh | 3 +- include/nvexec/stream/schedule_from.cuh | 3 +- include/nvexec/stream/then.cuh | 4 +- include/nvexec/stream/upon_error.cuh | 4 +- include/nvexec/stream/upon_stopped.cuh | 4 +- include/nvexec/stream/when_all.cuh | 4 +- include/stdexec/__detail/__basic_sender.hpp | 24 ++- .../__detail/__completion_signatures.hpp | 23 +-- include/stdexec/__detail/__debug.hpp | 2 - include/stdexec/__detail/__diagnostics.hpp | 29 ++- include/stdexec/__detail/__ensure_started.hpp | 27 +-- include/stdexec/__detail/__execution_fwd.hpp | 34 ++-- .../__detail/__get_completion_signatures.hpp | 120 +++++------ include/stdexec/__detail/__let.hpp | 63 +++--- include/stdexec/__detail/__on.hpp | 20 +- include/stdexec/__detail/__read_env.hpp | 66 +++--- include/stdexec/__detail/__run_loop.hpp | 9 +- include/stdexec/__detail/__shared.hpp | 41 ++-- include/stdexec/__detail/__split.hpp | 39 ++-- include/stdexec/__detail/__starts_on.hpp | 19 +- include/stdexec/__detail/__transfer_just.hpp | 18 +- .../stdexec/__detail/__transform_sender.hpp | 10 +- include/stdexec/__detail/__when_all.hpp | 47 +++-- test/nvexec/common.cuh | 8 +- .../stdexec/algos/adaptors/test_let_value.cpp | 22 +- test/stdexec/algos/adaptors/test_on3.cpp | 3 +- test/stdexec/algos/factories/test_read.cpp | 6 +- test/test_common/retry.hpp | 15 +- test/test_common/senders.hpp | 6 +- 60 files changed, 546 insertions(+), 538 deletions(-) diff --git a/examples/algorithms/retry.hpp b/examples/algorithms/retry.hpp index 5ff36eb89..26b672908 100644 --- a/examples/algorithms/retry.hpp +++ b/examples/algorithms/retry.hpp @@ -119,10 +119,9 @@ struct _retry_sender { template using _value = stdexec::completion_signatures; - template - auto get_completion_signatures(Env&&) const -> stdexec::transform_completion_signatures_of< - S&, - Env, + template + static consteval auto get_completion_signatures() -> stdexec::transform_completion_signatures< + stdexec::completion_signatures_of_t, stdexec::completion_signatures, _value, _error diff --git a/examples/algorithms/then.hpp b/examples/algorithms/then.hpp index a793dcacb..94ed91643 100644 --- a/examples/algorithms/then.hpp +++ b/examples/algorithms/then.hpp @@ -64,16 +64,15 @@ struct _then_sender { using _set_value_t = stdexec::completion_signatures)>; - template - using _completions_t = stdexec::transform_completion_signatures_of< - S, - Env, + template + using _completions_t = stdexec::transform_completion_signatures< + stdexec::completion_signatures_of_t, stdexec::completion_signatures, _set_value_t >; - template - auto get_completion_signatures(Env&&) && -> _completions_t { + template + static consteval auto get_completion_signatures() -> _completions_t { return {}; } diff --git a/examples/benchmark/static_thread_pool_old.hpp b/examples/benchmark/static_thread_pool_old.hpp index ef8165794..35076f3bc 100644 --- a/examples/benchmark/static_thread_pool_old.hpp +++ b/examples/benchmark/static_thread_pool_old.hpp @@ -494,11 +494,9 @@ namespace exec_old { STDEXEC_EXPLICIT_THIS_END(connect) template Self, class Env> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this Self&&, Env&&) - -> completion_signatures { + static consteval auto get_completion_signatures() -> completion_signatures { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) auto get_env() const noexcept -> stdexec::env_of_t { return stdexec::get_env(sndr_); diff --git a/include/exec/__detail/__basic_sequence.hpp b/include/exec/__detail/__basic_sequence.hpp index 04f006cc3..e22d77bf6 100644 --- a/include/exec/__detail/__basic_sequence.hpp +++ b/include/exec/__detail/__basic_sequence.hpp @@ -74,7 +74,7 @@ namespace exec { }; template - STDEXEC_ATTRIBUTE(host, device) + STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE __seqexpr(_Tag, _Data, _Child...) -> __seqexpr; } // namespace diff --git a/include/exec/any_sender_of.hpp b/include/exec/any_sender_of.hpp index d597cd8a5..572b2df4f 100644 --- a/include/exec/any_sender_of.hpp +++ b/include/exec/any_sender_of.hpp @@ -1233,11 +1233,9 @@ namespace exec { template _Self, class... _Env> requires(__any::__satisfies_receiver_query && ...) - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this _Self&&, _Env&&...) noexcept - -> __sender_base::completion_signatures { + static consteval auto get_completion_signatures() -> __sender_base::completion_signatures { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) template _Receiver> auto connect(_Receiver __rcvr) && -> STDEXEC::connect_result_t<__sender_base, _Receiver> { diff --git a/include/exec/async_scope.hpp b/include/exec/async_scope.hpp index d04c3a6bc..7991369bc 100644 --- a/include/exec/async_scope.hpp +++ b/include/exec/async_scope.hpp @@ -131,11 +131,10 @@ namespace exec { STDEXEC_EXPLICIT_THIS_END(connect) template <__decays_to<__t> _Self, class... _Env> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this _Self&&, _Env&&...) + static consteval auto get_completion_signatures() -> __completion_signatures_of_t<__copy_cvref_t<_Self, _Constrained>, __env_t<_Env>...> { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) const __impl* __scope_; STDEXEC_ATTRIBUTE(no_unique_address) _Constrained __c_; @@ -278,11 +277,10 @@ namespace exec { STDEXEC_EXPLICIT_THIS_END(connect) template <__decays_to<__t> _Self, class... _Env> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this _Self&&, _Env&&...) + static consteval auto get_completion_signatures() -> __completion_signatures_of_t<__copy_cvref_t<_Self, _Constrained>, __env_t<_Env>...> { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) }; }; @@ -684,11 +682,9 @@ namespace exec { STDEXEC_EXPLICIT_THIS_END(connect) template <__decays_to<__t> _Self, class... _OtherEnv> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this _Self&&, _OtherEnv&&...) - -> __completions_t<_Self> { + static consteval auto get_completion_signatures() -> __completions_t<_Self> { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) private: friend struct async_scope; diff --git a/include/exec/at_coroutine_exit.hpp b/include/exec/at_coroutine_exit.hpp index 7bebd700b..5d37f976b 100644 --- a/include/exec/at_coroutine_exit.hpp +++ b/include/exec/at_coroutine_exit.hpp @@ -91,11 +91,9 @@ namespace exec { } template <__same_as<__t> _Self, class... _Env> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this _Self&&, _Env&&...) - -> __completions_t<_Env...> { + static consteval auto get_completion_signatures() -> __completions_t<_Env...> { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) auto get_env() const noexcept -> env_of_t<_Sender> { return STDEXEC::get_env(__sender_); diff --git a/include/exec/completion_signatures.hpp b/include/exec/completion_signatures.hpp index 4263d61dd..1efb82d9d 100644 --- a/include/exec/completion_signatures.hpp +++ b/include/exec/completion_signatures.hpp @@ -39,6 +39,17 @@ namespace exec { detail::normalize(static_cast(nullptr))...)); } // namespace detail + /////////////////////////////////////////////////////////////////////////////////////////////////// + // get_child_completion_signatures + template + [[nodiscard]] + consteval auto get_child_completion_signatures() { + return STDEXEC::get_completion_signatures< + STDEXEC::__copy_cvref_t<_Parent, _Child>, + STDEXEC::__fwd_env_t<_Env>... + >(); + } + //! Creates a compile-time completion signatures type from explicit and deduced signature types. //! //! This function is a compile-time helper that constructs a completion signatures type @@ -213,8 +224,8 @@ namespace exec { ErrorFn error_fn = {}, StoppedFn stopped_fn = {}, ExtraSigs = {}) { - STDEXEC_COMPLSIGS_LET(auto(completions) = Completions{}) { - STDEXEC_COMPLSIGS_LET(auto(extra_sigs) = ExtraSigs{}) { + STDEXEC_COMPLSIGS_LET(completions, Completions{}) { + STDEXEC_COMPLSIGS_LET(extra_sigs, ExtraSigs{}) { detail::_transform_one tfx1{value_fn, error_fn, stopped_fn}; return concat_completion_signatures( completions.__apply(detail::_transform_all_fn{tfx1}), extra_sigs); diff --git a/include/exec/env.hpp b/include/exec/env.hpp index d08fa1449..4a8a472fb 100644 --- a/include/exec/env.hpp +++ b/include/exec/env.hpp @@ -126,8 +126,8 @@ namespace exec { } STDEXEC_EXPLICIT_THIS_END(connect) - template - constexpr auto get_completion_signatures(_Env&&) -> __completions_t<_Env> { + template + static consteval auto get_completion_signatures() -> __completions_t<_Env> { return {}; } }; @@ -166,12 +166,10 @@ namespace exec { } template <__decays_to<__t> _Self, class... _Env> - constexpr STDEXEC_EXPLICIT_THIS_BEGIN( - auto get_completion_signatures)(this _Self&&, _Env&&...) + static consteval auto get_completion_signatures() -> __completion_signatures_of_t<__copy_cvref_t<_Self, _Sender>, _Env...> { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) template <__decays_to<__t> _Self, class _Receiver> requires sender_in<__copy_cvref_t<_Self, _Sender>, env_of_t<_Receiver>> diff --git a/include/exec/finally.hpp b/include/exec/finally.hpp index 621491105..48080e499 100644 --- a/include/exec/finally.hpp +++ b/include/exec/finally.hpp @@ -16,58 +16,42 @@ */ #pragma once -#include "../stdexec/__detail/__manual_lifetime.hpp" #include "../stdexec/execution.hpp" +// include these after execution.hpp +#include "../stdexec/__detail/__manual_lifetime.hpp" + +#include "completion_signatures.hpp" + namespace exec { namespace __final { using namespace STDEXEC; template - using __result_variant = - __for_each_completion_signature<_Sigs, __decayed_std_tuple, __std_variant>; + using __result_variant = __for_each_completion_signature<_Sigs, __decayed_tuple, __variant_for>; template struct __final_operation_base { using _Receiver = __t<_ReceiverId>; _Receiver __receiver_{}; - STDEXEC::__manual_lifetime<_ResultType> __result_{}; + __manual_lifetime<_ResultType> __result_{}; }; - template - using __as_rvalues = completion_signatures...)>; - - template - using __completion_signatures_t = transform_completion_signatures< - __completion_signatures_of_t<_InitialSender, _Env...>, - transform_completion_signatures< - __completion_signatures_of_t<_FinalSender, _Env...>, - completion_signatures, - __mconst>::__f - >, // swallow the final sender's value completions - __as_rvalues - >; - - template struct __applier { - _Receiver __receiver_; - - template - void operator()(_Tag __tag, _Args&&... __args) noexcept { - __tag(static_cast<_Receiver&&>(__receiver_), static_cast<_Args&&>(__args)...); + // We intentially take the arguments by value so they will still be valid + // after we clear the result storage. + template + void operator()(_OpState* __op, _Tag __tag, _Args... __args) noexcept { + __op->__result_.__destroy(); + __tag(std::move(__op->__receiver_), static_cast<_Args&&>(__args)...); } }; - template struct __visitor { - _Receiver __receiver_; - - template - void operator()(_Tuple&& __tuple) noexcept { - std::apply( - __applier<_Receiver>{static_cast<_Receiver&&>(__receiver_)}, - static_cast<_Tuple&&>(__tuple)); + template + void operator()(_OpState* __op, _Tuple&& __tuple) noexcept { + STDEXEC::__apply(__applier{}, static_cast<_Tuple&&>(__tuple), __op); } }; @@ -78,7 +62,7 @@ namespace exec { class __t { public: using __id = __final_receiver; - using receiver_concept = STDEXEC::receiver_t; + using receiver_concept = receiver_t; explicit __t(__final_operation_base<_ResultType, _ReceiverId>* __op) noexcept : __op_{__op} { @@ -89,21 +73,12 @@ namespace exec { } void set_value() noexcept { - if constexpr (std::is_nothrow_move_constructible_v<_ResultType>) { - _ResultType __result = static_cast<_ResultType&&>(__op_->__result_.__get()); - __op_->__result_.__destroy(); - std::visit( - __visitor<_Receiver>{static_cast<_Receiver&&>(__op_->__receiver_)}, - static_cast<_ResultType&&>(__result)); - } else { - STDEXEC_TRY { - _ResultType __result = static_cast<_ResultType&&>(__op_->__result_.__get()); - __op_->__result_.__destroy(); - std::visit( - __visitor<_Receiver>{static_cast<_Receiver&&>(__op_->__receiver_)}, - static_cast<_ResultType&&>(__result)); - } - STDEXEC_CATCH_ALL { + STDEXEC_TRY { + auto& __result = __op_->__result_.__get(); + __result.visit(__visitor{}, static_cast<_ResultType&&>(__result), __op_); + } + STDEXEC_CATCH_ALL { + if constexpr (!__mapply_q<__nothrow_decay_copyable_t, _ResultType>::value) { STDEXEC::set_error( static_cast<_Receiver&&>(__op_->__receiver_), std::current_exception()); } @@ -151,7 +126,7 @@ namespace exec { class __t { public: using __id = __initial_receiver; - using receiver_concept = STDEXEC::receiver_t; + using receiver_concept = receiver_t; explicit __t(__base_op_t* __op) noexcept : __op_(__op) { @@ -206,44 +181,48 @@ namespace exec { STDEXEC::__t<__initial_receiver<_InitialSenderId, _FinalSenderId, _ReceiverId>>; struct __initial_op_t { + explicit __initial_op_t( + _InitialSender&& __sndr, + _FinalSender&& __final, + __initial_receiver_t __rcvr) + : __sndr_{static_cast<_FinalSender&&>(__final)} + , __initial_operation_{STDEXEC::connect( + static_cast<_InitialSender&&>(__sndr), + static_cast<__initial_receiver_t&&>(__rcvr))} { + } + _FinalSender __sndr_; connect_result_t<_InitialSender, __initial_receiver_t> __initial_operation_; }; - std::variant<__initial_op_t, __final_op_t> __op_; + __variant_for<__initial_op_t, __final_op_t> __op_; public: using __id = __operation_state; template - requires __std::constructible_from< - __result_variant<__signatures>, - __decayed_std_tuple<_Args...> - > void __store_result_and_start_next_op(_Args&&... __args) { - this->__result_.__construct( - std::in_place_type<__decayed_std_tuple<_Args...>>, static_cast<_Args&&>(__args)...); + this->__result_.__construct() + .template emplace<__decayed_tuple<_Args...>>(static_cast<_Args&&>(__args)...); STDEXEC_ASSERT(__op_.index() == 0); - auto __final = static_cast<_FinalSender&&>(std::get_if<0>(&__op_)->__sndr_); - __final_op_t& __final_op = __op_.template emplace<1>(__emplace_from{[&] { - return STDEXEC::connect(static_cast<_FinalSender&&>(__final), __final_receiver_t{this}); - }}); + auto __final = static_cast<_FinalSender&&>(__op_.template get<0>().__sndr_); + __final_op_t& __final_op = __op_.template emplace_from_at<1>( + STDEXEC::connect, static_cast<_FinalSender&&>(__final), __final_receiver_t{this}); STDEXEC::start(__final_op); } - __t(_InitialSender&& __initial, _FinalSender __final, _Receiver __receiver) + explicit __t(_InitialSender&& __initial, _FinalSender __final, _Receiver __receiver) : __base_t{{static_cast<_Receiver&&>(__receiver)}} - , __op_(std::in_place_index<0>, __emplace_from{[&] { - return __initial_op_t{ - static_cast<_FinalSender&&>(__final), - STDEXEC::connect( - static_cast<_InitialSender&&>(__initial), __initial_receiver_t{this})}; - }}) { + , __op_() { + __op_.template emplace<0>( + static_cast<_InitialSender&&>(__initial), + static_cast<_FinalSender&&>(__final), + __initial_receiver_t{this}); } void start() & noexcept { STDEXEC_ASSERT(__op_.index() == 0); - STDEXEC::start(std::get_if<0>(&__op_)->__initial_operation_); + STDEXEC::start(__op_.template get<0>().__initial_operation_); } }; @@ -257,25 +236,14 @@ namespace exec { __operation_state<__cvref_id<_Self, _InitialSender>, __id<_FinalSender>, __id<_Receiver>> >; - class __t { - _InitialSender __initial_sndr_; - _FinalSender __final_sndr_; - - public: + struct __t { using __id = __sender; - using sender_concept = STDEXEC::sender_t; - - template <__decays_to<_InitialSender> _Initial, __decays_to<_FinalSender> _Final> - __t(_Initial&& __initial, _Final&& __final) - noexcept(__nothrow_decay_copyable<_Initial> && __nothrow_decay_copyable<_Final>) - : __initial_sndr_{static_cast<_Initial&&>(__initial)} - , __final_sndr_{static_cast<_Final&&>(__final)} { - } + using sender_concept = sender_t; template <__decays_to<__t> _Self, class _Rec> STDEXEC_EXPLICIT_THIS_BEGIN(auto connect)(this _Self&& __self, _Rec&& __receiver) noexcept -> __op_t<_Self, _Rec> { - return { + return __op_t<_Self, _Rec>{ static_cast<_Self&&>(__self).__initial_sndr_, static_cast<_Self&&>(__self).__final_sndr_, static_cast<_Rec&&>(__receiver)}; @@ -283,21 +251,45 @@ namespace exec { STDEXEC_EXPLICIT_THIS_END(connect) template <__decays_to<__t> _Self, class... _Env> - STDEXEC_EXPLICIT_THIS_BEGIN( - auto get_completion_signatures)(this _Self&&, _Env&&...) noexcept - -> __completion_signatures_t<__copy_cvref_t<_Self, _InitialSender>, _FinalSender, _Env...> { - return {}; + requires __decay_copyable<__copy_cvref_t<_Self, _FinalSender>> + static consteval auto get_completion_signatures() { + STDEXEC_COMPLSIGS_LET( + __initial_completions, + exec::get_child_completion_signatures<_Self, _InitialSender, _Env...>()) { + STDEXEC_COMPLSIGS_LET( + __final_completions, + STDEXEC::get_completion_signatures<_FinalSender, __fwd_env_t<_Env>...>()) { + // The finally sender's completion signatures are ... + return exec::concat_completion_signatures( + // ... the initial sender's completions with value types decayed ... + exec::transform_completion_signatures( + __initial_completions, exec::decay_arguments()), + // ... and the finally sender's error and stopped completions ... + exec::transform_completion_signatures( + __final_completions, exec::ignore_completion()), + // ... and a set_error(exception_ptr) (TODO: only needed if the + // values of the initial sender are not nothrow decay copyable). + completion_signatures()); + } + } } + template <__decays_to<__t> _Self, class... _Env> - requires(!__decay_copyable<__copy_cvref_t<_Self, _FinalSender>>) - STDEXEC_EXPLICIT_THIS_BEGIN( - auto get_completion_signatures)(this _Self&&, _Env&&...) noexcept { - return _ERROR_<_SENDER_TYPE_IS_NOT_COPYABLE_, _WITH_SENDER_<_FinalSender>>{}; + static consteval auto get_completion_signatures() { + return exec::invalid_completion_signature< + _SENDER_TYPE_IS_NOT_COPYABLE_, + _WITH_SENDER_<_FinalSender> + >(); } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) + + _InitialSender __initial_sndr_; + _FinalSender __final_sndr_; }; }; + template + using __sender_t = __t<__sender<__id<_InitialSender>, __id<_FinalSender>>>; + struct finally_t { template auto operator()(_Initial&& __initial, _Final&& __final) const { @@ -312,13 +304,11 @@ namespace exec { } template - static auto transform_sender(STDEXEC::set_value_t, _Sender&& __sndr, __ignore) { + static auto transform_sender(set_value_t, _Sender&& __sndr, __ignore) { return __apply( []( __ignore, __ignore, _Initial&& __initial, _Final&& __final) { - using __result_sndr_t = - __t<__sender<__id<__decay_t<_Initial>>, __id<__decay_t<_Final>>>>; - return __result_sndr_t{ + return __sender_t<_Initial, _Final>{ static_cast<_Initial&&>(__initial), static_cast<_Final&&>(__final)}; }, static_cast<_Sender&&>(__sndr)); @@ -333,9 +323,11 @@ namespace exec { namespace STDEXEC { template <> struct __sexpr_impl : __sexpr_defaults { - static constexpr auto get_completion_signatures = - [](_Sender&&, const _Env&...) noexcept - -> __completion_signatures_of_t, _Env...> { - }; + template + static consteval auto get_completion_signatures() { + using __sndr_t = + __detail::__transform_sender_result_t>; + return STDEXEC::get_completion_signatures<__sndr_t, _Env...>(); + } }; } // namespace STDEXEC diff --git a/include/exec/fork_join.hpp b/include/exec/fork_join.hpp index dd639da63..83d390fb9 100644 --- a/include/exec/fork_join.hpp +++ b/include/exec/fork_join.hpp @@ -102,9 +102,9 @@ namespace exec { const Variant* _results_; }; - template + template STDEXEC_ATTRIBUTE(host, device) - auto get_completion_signatures(_Env&&...) const noexcept { + static consteval auto get_completion_signatures() { return STDEXEC::__mapply, Variant>{}; } @@ -254,7 +254,7 @@ namespace exec { template STDEXEC_ATTRIBUTE(host, device) - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this Self&&, Env&&...) noexcept { + static consteval auto get_completion_signatures() { using namespace STDEXEC; using _domain_t = STDEXEC::__completion_domain_of_t; using _child_t = __copy_cvref_t; @@ -273,7 +273,6 @@ namespace exec { return __completion_signatures_of_t<_sndr_t, __fwd_env_t...>{}; } } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) template STDEXEC_ATTRIBUTE(host, device) diff --git a/include/exec/libdispatch_queue.hpp b/include/exec/libdispatch_queue.hpp index a755343e6..4ff803c21 100644 --- a/include/exec/libdispatch_queue.hpp +++ b/include/exec/libdispatch_queue.hpp @@ -302,11 +302,9 @@ namespace exec { STDEXEC_EXPLICIT_THIS_END(connect) template Self, class... Env> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this Self &&, Env &&...) - -> __completions_t { + static consteval auto get_completion_signatures() -> __completions_t { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) auto get_env() const noexcept -> STDEXEC::env_of_t { return STDEXEC::get_env(sndr_); diff --git a/include/exec/materialize.hpp b/include/exec/materialize.hpp index cccb62e5e..61e7c643e 100644 --- a/include/exec/materialize.hpp +++ b/include/exec/materialize.hpp @@ -104,11 +104,9 @@ namespace exec { >; template <__decays_to<__t> _Self, class... _Env> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this _Self&&, _Env&&...) - -> __completions_t<_Self, _Env...> { + static consteval auto get_completion_signatures() -> __completions_t<_Self, _Env...> { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) private: _Sender __sndr_; @@ -211,11 +209,9 @@ namespace exec { >; template <__decays_to<__t> _Self, class... _Env> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this _Self&&, _Env&&...) - -> __completions_t<_Self, _Env...> { + static consteval auto get_completion_signatures() -> __completions_t<_Self, _Env...> { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) private: _Sender __sndr_; diff --git a/include/exec/on_coro_disposition.hpp b/include/exec/on_coro_disposition.hpp index 41345b6a7..141dba66a 100644 --- a/include/exec/on_coro_disposition.hpp +++ b/include/exec/on_coro_disposition.hpp @@ -16,7 +16,7 @@ */ #pragma once -// The original idea is taken from libunifex and adapted to stdexec. +// The original idea is taken from libunifex and adapted to STDEXEC. #include "../stdexec/coroutine.hpp" #include "../stdexec/execution.hpp" diff --git a/include/exec/repeat_n.hpp b/include/exec/repeat_n.hpp index ad657851c..688e39079 100644 --- a/include/exec/repeat_n.hpp +++ b/include/exec/repeat_n.hpp @@ -207,7 +207,7 @@ namespace exec { } template > - auto transform_sender(set_value_t, _Sender &&__sndr, __ignore) noexcept(_NoThrow) { + static auto transform_sender(set_value_t, _Sender &&__sndr, __ignore) noexcept(_NoThrow) { return __apply( [](__ignore, std::size_t __count, _Child __child) noexcept(_NoThrow) { return __make_sexpr<__repeat_n_tag>(__child_count_pair{std::move(__child), __count}); @@ -227,9 +227,11 @@ namespace STDEXEC { template <> struct __sexpr_impl : __sexpr_defaults { - static constexpr auto get_completion_signatures = - [](_Sender &&, const _Env &...) noexcept - -> __completion_signatures_of_t, _Env...> { + template + static consteval auto get_completion_signatures() { + using __sndr_t = + __detail::__transform_sender_result_t>; + return STDEXEC::get_completion_signatures<__sndr_t, _Env...>(); }; }; } // namespace STDEXEC diff --git a/include/exec/reschedule.hpp b/include/exec/reschedule.hpp index 44dbff50d..9cd449fba 100644 --- a/include/exec/reschedule.hpp +++ b/include/exec/reschedule.hpp @@ -48,8 +48,8 @@ namespace exec { struct __sender { using sender_concept = sender_t; - template - auto get_completion_signatures(_Env&&) noexcept -> __completions<_Env> { + template + static consteval auto get_completion_signatures() -> __completions<_Env> { return {}; } diff --git a/include/exec/sequence.hpp b/include/exec/sequence.hpp index 543079a14..4d99649e7 100644 --- a/include/exec/sequence.hpp +++ b/include/exec/sequence.hpp @@ -191,10 +191,9 @@ namespace exec { template STDEXEC_ATTRIBUTE(host, device) - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this Self&&, Env&&...) { + static consteval auto get_completion_signatures() { return _completions_t{}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) template requires STDEXEC::__decay_copyable diff --git a/include/exec/sequence/transform_each.hpp b/include/exec/sequence/transform_each.hpp index 429b6f796..14eb82c77 100644 --- a/include/exec/sequence/transform_each.hpp +++ b/include/exec/sequence/transform_each.hpp @@ -167,8 +167,9 @@ namespace exec { template using __completion_sigs_t = __sequence_completion_signatures_of_t<__child_of<_Self>, _Env...>; - template _Self, class... _Env> - static consteval auto get_completion_signatures() noexcept { + template + static consteval auto get_completion_signatures() { + static_assert(sender_expr_for<_Self, transform_each_t>); using __result_t = __completion_sigs_t<_Self, _Env...>; if constexpr (__ok<__result_t>) { return __result_t(); diff --git a/include/exec/sequence_senders.hpp b/include/exec/sequence_senders.hpp index 618702efc..db3f3881d 100644 --- a/include/exec/sequence_senders.hpp +++ b/include/exec/sequence_senders.hpp @@ -314,8 +314,7 @@ namespace exec { __tfx_sequence_t)::template get_item_types<__tfx_sequence_t, _Env>()>; return __debug::__item_types(); } else { - using __result_t = __unrecognized_sequence_error_t<_Sequence, _Env>; - return __declfn<__result_t>(); + return __unrecognized_sequence_error_t<_Sequence, _Env>(); } } }; @@ -567,21 +566,29 @@ namespace exec { >; template - concept sequence_receiver_from = STDEXEC::receiver<_Receiver> - && STDEXEC::sender_in<_Sequence, STDEXEC::env_of_t<_Receiver>> - && sequence_receiver_of< - _Receiver, - __item_types_of_t<_Sequence, STDEXEC::env_of_t<_Receiver>> - > - && ((sequence_sender_in<_Sequence, STDEXEC::env_of_t<_Receiver>> - && STDEXEC::receiver_of< - _Receiver, - STDEXEC::completion_signatures_of_t< - _Sequence, - STDEXEC::env_of_t<_Receiver> - > - >) - || (!sequence_sender_in<_Sequence, STDEXEC::env_of_t<_Receiver>> && STDEXEC::__receiver_from<__sequence_sndr::__stopped_means_break_t<_Receiver>, next_sender_of_t<_Receiver, _Sequence>>) ); + concept __sequence_receiver_from = // + sequence_sender_in<_Sequence, STDEXEC::env_of_t<_Receiver>> // + && STDEXEC::receiver_of< + _Receiver, + STDEXEC::completion_signatures_of_t<_Sequence, STDEXEC::env_of_t<_Receiver>> + >; + + template + concept __stopped_means_break_receiver_from = // + !sequence_sender_in<_Sequence, STDEXEC::env_of_t<_Receiver>> + && STDEXEC::__receiver_from< + __sequence_sndr::__stopped_means_break_t<_Receiver>, + next_sender_of_t<_Receiver, _Sequence> + >; + + template + concept sequence_receiver_from = // + STDEXEC::receiver<_Receiver> // + && STDEXEC::sender_in<_Sequence, STDEXEC::env_of_t<_Receiver>> // + && sequence_receiver_of<_Receiver, __item_types_of_t<_Sequence, STDEXEC::env_of_t<_Receiver>>> // + && bool( // cast to bool to hide the disjunction + __sequence_receiver_from<_Receiver, _Sequence> // + || __stopped_means_break_receiver_from<_Receiver, _Sequence>); namespace __sequence_sndr { struct subscribe_t; @@ -654,7 +661,7 @@ namespace exec { template static consteval auto __get_declfn() noexcept { constexpr bool __nothrow_tfx_sequence = - __nothrow_callable>; + __noexcept_of>; using __tfx_sequence_t = __transform_sender_result_t<_Sequence, _Receiver>; static_assert( diff --git a/include/exec/start_now.hpp b/include/exec/start_now.hpp index 0863d5510..493f0b460 100644 --- a/include/exec/start_now.hpp +++ b/include/exec/start_now.hpp @@ -154,7 +154,8 @@ namespace exec { return {__stg_, static_cast<_Receiver&&>(__rcvr)}; } - auto get_completion_signatures(STDEXEC::__ignore = {}) -> __completions_t { + template + static consteval auto get_completion_signatures() -> __completions_t { return {}; } }; diff --git a/include/exec/static_thread_pool.hpp b/include/exec/static_thread_pool.hpp index 7cc8e9311..9804f49bf 100644 --- a/include/exec/static_thread_pool.hpp +++ b/include/exec/static_thread_pool.hpp @@ -1196,11 +1196,9 @@ namespace exec { STDEXEC_EXPLICIT_THIS_END(connect) template <__decays_to<__t> Self, class... Env> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this Self&&, Env&&...) - -> _completions_t { + static consteval auto get_completion_signatures() -> _completions_t { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) auto get_env() const noexcept -> env_of_t { return STDEXEC::get_env(sndr_); diff --git a/include/exec/system_context.hpp b/include/exec/system_context.hpp index 133ba9287..d6bcc09e8 100644 --- a/include/exec/system_context.hpp +++ b/include/exec/system_context.hpp @@ -628,11 +628,9 @@ namespace exec { /// Gets the completion signatures for this sender. template _Self, class... _Env> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this _Self&&, _Env&&...) - -> __completions_t<_Self, _Env...> { + static consteval auto get_completion_signatures() -> __completions_t<_Self, _Env...> { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) private: /// The underlying implementation of the scheduler we are using. diff --git a/include/exec/variant_sender.hpp b/include/exec/variant_sender.hpp index eb4be68fe..986cb585d 100644 --- a/include/exec/variant_sender.hpp +++ b/include/exec/variant_sender.hpp @@ -114,11 +114,10 @@ namespace exec { STDEXEC_EXPLICIT_THIS_END(connect) template <__decays_to<__t> _Self, class _Env> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this _Self&&, _Env&&) + static consteval auto get_completion_signatures() // -> __completion_signatures_t<_Self, _Env> { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) }; }; } // namespace __variant diff --git a/include/exec/when_any.hpp b/include/exec/when_any.hpp index 5ab5af32e..aa3255535 100644 --- a/include/exec/when_any.hpp +++ b/include/exec/when_any.hpp @@ -267,19 +267,17 @@ namespace exec { STDEXEC_EXPLICIT_THIS_END(connect) template <__decay_copyable _Self, class... _Env> - STDEXEC_EXPLICIT_THIS_BEGIN( - auto get_completion_signatures)(this _Self&&, const _Env&...) noexcept { + static consteval auto get_completion_signatures() { return __completions_t<_Self, _Env...>{}; } + template - STDEXEC_EXPLICIT_THIS_BEGIN( - auto get_completion_signatures)(this _Self&&, const _Env&...) noexcept { - return __mexception< + static consteval auto get_completion_signatures() { + return STDEXEC::__invalid_completion_signature< _SENDER_TYPE_IS_NOT_COPYABLE_, _WITH_SENDERS_>... - >{}; + >(); } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) private: __senders_tuple __senders_; diff --git a/include/execpools/thread_pool_base.hpp b/include/execpools/thread_pool_base.hpp index d1da6163e..80d430154 100644 --- a/include/execpools/thread_pool_base.hpp +++ b/include/execpools/thread_pool_base.hpp @@ -391,11 +391,10 @@ namespace execpools { STDEXEC_EXPLICIT_THIS_END(connect) template Self, class... Env> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this Self&&, Env&&...) + static consteval auto get_completion_signatures() // -> _completion_signatures_t { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) template requires STDEXEC::__queryable_with, Tag, As...> diff --git a/include/nvexec/nvtx.cuh b/include/nvexec/nvtx.cuh index f31446466..e9228b634 100644 --- a/include/nvexec/nvtx.cuh +++ b/include/nvexec/nvtx.cuh @@ -117,11 +117,9 @@ namespace nvexec { STDEXEC_EXPLICIT_THIS_END(connect) template <__decays_to<__t> Self, class Env> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this Self&&, Env&&) - -> _completion_signatures_t { + static consteval auto get_completion_signatures() -> _completion_signatures_t { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) auto get_env() const noexcept -> stream_sender_attrs { return {&sndr_}; diff --git a/include/nvexec/stream/algorithm_base.cuh b/include/nvexec/stream/algorithm_base.cuh index a48e268c6..a5cf6e9c2 100644 --- a/include/nvexec/stream/algorithm_base.cuh +++ b/include/nvexec/stream/algorithm_base.cuh @@ -138,11 +138,9 @@ namespace nvexec::_strm::__algo_range_init_fun { STDEXEC_EXPLICIT_THIS_END(connect) template <__decays_to<__t> Self, class... Env> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this Self&&, Env&&...) - -> completion_signatures { + static consteval auto get_completion_signatures() -> completion_signatures { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) auto get_env() const noexcept -> env_of_t { return STDEXEC::get_env(sndr_); diff --git a/include/nvexec/stream/bulk.cuh b/include/nvexec/stream/bulk.cuh index 8c528584f..c11146681 100644 --- a/include/nvexec/stream/bulk.cuh +++ b/include/nvexec/stream/bulk.cuh @@ -145,11 +145,9 @@ namespace nvexec::_strm { STDEXEC_EXPLICIT_THIS_END(connect) template <__decays_to<__t> Self, class... Env> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this Self&&, Env&&...) - -> _completion_signatures_t { + static consteval auto get_completion_signatures() -> _completion_signatures_t { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) auto get_env() const noexcept -> stream_sender_attrs { return {&sndr_}; @@ -392,11 +390,9 @@ namespace nvexec::_strm { STDEXEC_EXPLICIT_THIS_END(connect) template <__decays_to<__t> Self, class... Env> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this Self&&, Env&&...) - -> _completion_signatures_t { + static consteval auto get_completion_signatures() -> _completion_signatures_t { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) auto get_env() const noexcept -> stream_sender_attrs { return {&sndr_}; diff --git a/include/nvexec/stream/continues_on.cuh b/include/nvexec/stream/continues_on.cuh index 510c8d22e..22394e6ed 100644 --- a/include/nvexec/stream/continues_on.cuh +++ b/include/nvexec/stream/continues_on.cuh @@ -169,11 +169,10 @@ namespace nvexec::_strm { STDEXEC_EXPLICIT_THIS_END(connect) template <__decay_copyable _Self, class... _Env> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this _Self&&, _Env&&...) + static consteval auto get_completion_signatures() -> __completion_signatures_of_t<__copy_cvref_t<_Self, Sender>, _Env...> { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) [[nodiscard]] auto get_env() const noexcept -> env_of_t { @@ -232,16 +231,14 @@ namespace nvexec::_strm { STDEXEC_EXPLICIT_THIS_END(connect) template <__decays_to<__t> _Self, class... _Env> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this _Self&&, _Env&&...) - -> transform_completion_signatures< - __completion_signatures_of_t<__copy_cvref_t<_Self, Sender>, _Env...>, - completion_signatures, - _trnsfr::value_completions_t, - _trnsfr::error_completions_t - > { + static consteval auto get_completion_signatures() -> transform_completion_signatures< + __completion_signatures_of_t<__copy_cvref_t<_Self, Sender>, _Env...>, + completion_signatures, + _trnsfr::value_completions_t, + _trnsfr::error_completions_t + > { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) [[nodiscard]] auto get_env() const noexcept -> __sched_attrs { diff --git a/include/nvexec/stream/ensure_started.cuh b/include/nvexec/stream/ensure_started.cuh index 0bac3f3cc..aa148509a 100644 --- a/include/nvexec/stream/ensure_started.cuh +++ b/include/nvexec/stream/ensure_started.cuh @@ -357,8 +357,8 @@ namespace nvexec::_strm { template using _set_error_t = completion_signatures)>; - template - auto get_completion_signatures(Env&&) && -> __try_make_completion_signatures< + template + static consteval auto get_completion_signatures() -> __try_make_completion_signatures< Sender, _ensure_started::env_t, completion_signatures, diff --git a/include/nvexec/stream/launch.cuh b/include/nvexec/stream/launch.cuh index 3a3c284b5..4f07b1bbb 100644 --- a/include/nvexec/stream/launch.cuh +++ b/include/nvexec/stream/launch.cuh @@ -150,11 +150,9 @@ namespace nvexec { STDEXEC_EXPLICIT_THIS_END(connect) template <__decays_to<__t> Self, class... Env> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this Self&&, Env&&...) - -> completions_t { + static consteval auto get_completion_signatures() -> completions_t { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) auto get_env() const noexcept -> stream_sender_attrs { return {&sndr_}; diff --git a/include/nvexec/stream/let_xxx.cuh b/include/nvexec/stream/let_xxx.cuh index a7bd37683..39eaff1a7 100644 --- a/include/nvexec/stream/let_xxx.cuh +++ b/include/nvexec/stream/let_xxx.cuh @@ -264,11 +264,10 @@ namespace nvexec::_strm { } template <__decays_to<__t> Self, class... Env> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this Self&&, Env&&...) + static consteval auto get_completion_signatures() -> __completions<__copy_cvref_t, stream_env...> { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) Sender sndr_; Fun fun_; diff --git a/include/nvexec/stream/schedule_from.cuh b/include/nvexec/stream/schedule_from.cuh index 2a837815a..2769a21ff 100644 --- a/include/nvexec/stream/schedule_from.cuh +++ b/include/nvexec/stream/schedule_from.cuh @@ -164,11 +164,10 @@ namespace nvexec { STDEXEC_EXPLICIT_THIS_END(connect) template <__decays_to<__t> Self, class... Env> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this Self&&, Env&&...) + static consteval auto get_completion_signatures() // -> _completion_signatures_t { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) auto get_env() const noexcept -> STDEXEC::__fwd_env_t> { return STDEXEC::__fwd_env(STDEXEC::get_env(sndr_)); diff --git a/include/nvexec/stream/then.cuh b/include/nvexec/stream/then.cuh index 3e274741d..97fbbd3ec 100644 --- a/include/nvexec/stream/then.cuh +++ b/include/nvexec/stream/then.cuh @@ -206,11 +206,9 @@ namespace nvexec::_strm { STDEXEC_EXPLICIT_THIS_END(connect) template <__decays_to<__t> Self, class... Env> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this Self&&, Env&&...) - -> _completion_signatures_t { + static consteval auto get_completion_signatures() -> _completion_signatures_t { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) auto get_env() const noexcept -> stream_sender_attrs { return {&sndr_}; diff --git a/include/nvexec/stream/upon_error.cuh b/include/nvexec/stream/upon_error.cuh index e9d38bf4e..952276b6c 100644 --- a/include/nvexec/stream/upon_error.cuh +++ b/include/nvexec/stream/upon_error.cuh @@ -188,11 +188,9 @@ namespace nvexec::_strm { STDEXEC_EXPLICIT_THIS_END(connect) template <__decays_to<__t> Self, class... Env> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this Self&&, Env&&...) - -> completion_signatures { + static consteval auto get_completion_signatures() -> completion_signatures { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) auto get_env() const noexcept -> stream_sender_attrs { return {&sndr_}; diff --git a/include/nvexec/stream/upon_stopped.cuh b/include/nvexec/stream/upon_stopped.cuh index 7e960ac88..3bf030d20 100644 --- a/include/nvexec/stream/upon_stopped.cuh +++ b/include/nvexec/stream/upon_stopped.cuh @@ -158,11 +158,9 @@ namespace nvexec::_strm { STDEXEC_EXPLICIT_THIS_END(connect) template <__decays_to<__t> Self, class... Env> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this Self&&, Env&&...) - -> completion_signatures { + static consteval auto get_completion_signatures() -> completion_signatures { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) auto get_env() const noexcept -> stream_sender_attrs { return {&sndr_}; diff --git a/include/nvexec/stream/when_all.cuh b/include/nvexec/stream/when_all.cuh index 52c7d7530..457ad1a38 100644 --- a/include/nvexec/stream/when_all.cuh +++ b/include/nvexec/stream/when_all.cuh @@ -487,11 +487,9 @@ namespace nvexec::_strm { STDEXEC_EXPLICIT_THIS_END(connect) template <__decays_to Self, class... Env> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this Self&&, Env&&...) - -> completion_sigs { + static consteval auto get_completion_signatures() -> completion_sigs { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) [[nodiscard]] auto get_env() const noexcept -> const attrs& { diff --git a/include/stdexec/__detail/__basic_sender.hpp b/include/stdexec/__detail/__basic_sender.hpp index a8a22cfa8..396a41d53 100644 --- a/include/stdexec/__detail/__basic_sender.hpp +++ b/include/stdexec/__detail/__basic_sender.hpp @@ -331,8 +331,13 @@ namespace STDEXEC { concept __in_range = (_Idx < sizeof(__minvoke<_Descriptor, __q<__tuple_size_t>>)); template - concept __has_static_consteval_get_completion_signatures = requires { - _Tag::template get_completion_signatures<_Self, _Env...>(); + concept __has_new_get_completion_signatures = requires { + __sexpr_impl<_Tag>::template get_completion_signatures<_Self, _Env...>(); + }; + + template + concept __has_old_get_completion_signatures = requires(__declfn_t<_Self> __self) { + __sexpr_impl<_Tag>::get_completion_signatures(__self(), __declval<_Env>()...); }; } // namespace __detail @@ -468,14 +473,13 @@ namespace STDEXEC { template static consteval auto get_completion_signatures() { - static_assert(__decays_to_derived_from<_Self, __sexpr>); - using __impl_t = __mtypeof<__sexpr_impl<__tag_t>::get_completion_signatures>; - using __detail::__has_static_consteval_get_completion_signatures; - - if constexpr (__has_static_consteval_get_completion_signatures<__tag_t, _Self, _Env...>) { - return __impl_t::template get_completion_signatures<_Self, _Env...>(); - } else if constexpr (__callable<__impl_t, _Self, _Env...>) { - return __call_result_t<__impl_t, _Self, _Env...>(); + using namespace __detail; + if constexpr (__has_new_get_completion_signatures<__tag_t, _Self, _Env...>) { + return __sexpr_impl<__tag_t>::template get_completion_signatures<_Self, _Env...>(); + } else if constexpr (__has_new_get_completion_signatures<__tag_t, _Self>) { + return __sexpr_impl<__tag_t>::template get_completion_signatures<_Self>(); + } else if constexpr (__has_old_get_completion_signatures<__tag_t, _Self, _Env...>) { + return __result_of<__sexpr_impl<__tag_t>::get_completion_signatures, _Self, _Env...>(); } else if constexpr (sizeof...(_Env) == 0) { return __dependent_sender<_Self>(); } else { diff --git a/include/stdexec/__detail/__completion_signatures.hpp b/include/stdexec/__detail/__completion_signatures.hpp index 1e19fa891..8e83d4024 100644 --- a/include/stdexec/__detail/__completion_signatures.hpp +++ b/include/stdexec/__detail/__completion_signatures.hpp @@ -380,7 +380,7 @@ namespace STDEXEC { // // USAGE: // - // STDEXEC_COMPLSIGS_LET(auto(__cs) = ) + // STDEXEC_COMPLSIGS_LET(__cs, ) // { // // __cs is guaranteed to be a specialization of completion_signatures. // } @@ -402,16 +402,11 @@ namespace STDEXEC { #if STDEXEC_NO_STD_CONSTEXPR_EXCEPTIONS() -# define STDEXEC_PP_EAT_AUTO_auto(_ID) _ID STDEXEC_PP_EAT STDEXEC_PP_LPAREN -# define STDEXEC_PP_EXPAND_AUTO_auto(_ID) auto _ID -# define STDEXEC_COMPLSIGS_LET_ID(...) \ - STDEXEC_PP_EXPAND(STDEXEC_PP_CAT(STDEXEC_PP_EAT_AUTO_, __VA_ARGS__) STDEXEC_PP_RPAREN) - -# define STDEXEC_COMPLSIGS_LET(...) \ - if constexpr (STDEXEC_PP_CAT(STDEXEC_PP_EXPAND_AUTO_, __VA_ARGS__); \ - !STDEXEC::__valid_completion_signatures) { \ - return STDEXEC_COMPLSIGS_LET_ID(__VA_ARGS__); \ +# define STDEXEC_COMPLSIGS_LET(_ID, ...) \ + if constexpr ([[maybe_unused]] \ + auto _ID = __VA_ARGS__; \ + !STDEXEC::__valid_completion_signatures) { \ + return _ID; \ } else template @@ -422,8 +417,10 @@ namespace STDEXEC { #else // ^^^ no constexpr exceptions ^^^ / vvv constexpr exceptions vvv -# define STDEXEC_COMPLSIGS_LET(...) \ - if constexpr ([[maybe_unused]] __VA_ARGS__; false) { \ +# define STDEXEC_COMPLSIGS_LET(_ID, ...) \ + if constexpr ([[maybe_unused]] \ + auto _ID = __VA_ARGS__; \ + false) { \ } else template diff --git a/include/stdexec/__detail/__debug.hpp b/include/stdexec/__detail/__debug.hpp index c02d4e0aa..55219fac8 100644 --- a/include/stdexec/__detail/__debug.hpp +++ b/include/stdexec/__detail/__debug.hpp @@ -44,8 +44,6 @@ namespace STDEXEC { template using __debug_env_t = env, _Env>; - struct __completion_signatures { }; - template struct __valid_completions { template diff --git a/include/stdexec/__detail/__diagnostics.hpp b/include/stdexec/__detail/__diagnostics.hpp index 618e1fbf8..a335a4fd9 100644 --- a/include/stdexec/__detail/__diagnostics.hpp +++ b/include/stdexec/__detail/__diagnostics.hpp @@ -15,6 +15,9 @@ */ #pragma once +#include "__execution_fwd.hpp" + +// include these after __execution_fwd.hpp #include "__meta.hpp" #include // IWYU pragma: keep for std::exception @@ -182,6 +185,8 @@ namespace STDEXEC { char const * what_; }; + // A specialization of _ERROR_ to be used to report dependent sender. It inherits + // from dependent_sender_error. template struct _ERROR_ : dependent_sender_error { constexpr _ERROR_() noexcept @@ -212,25 +217,27 @@ namespace STDEXEC { using __stopped_types = _ERROR_; }; + // By making __dependent_sender_error an alias for _ERROR_<...>, we ensure that + // it will get propagated correctly through various metafunctions. template using __dependent_sender_error = _ERROR_>; - template + template struct __not_a_sender { using sender_concept = sender_t; - template - constexpr auto get_completion_signatures(Self&&) const noexcept { - return __mexception<_What, _With...>(); + template + static consteval auto get_completion_signatures() { + return STDEXEC::__invalid_completion_signature<_What...>(); } }; - template + template struct __not_a_scheduler { using scheduler_concept = scheduler_t; auto schedule() noexcept { - return __not_a_sender<_What, _With...>{}; + return __not_a_sender<_What...>{}; } constexpr bool operator==(const __not_a_scheduler&) const noexcept = default; @@ -300,8 +307,8 @@ namespace STDEXEC { " public:\n" \ " using sender_concept = STDEXEC::sender_t;\n" \ "\n" \ - " template \n" \ - " auto get_completion_signatures(_Env&&...) -> STDEXEC::completion_signatures<\n" \ + " template \n" \ + " static consteval auto get_completion_signatures() -> STDEXEC::completion_signatures<\n" \ " // This sender can complete successfully with an int and a float...\n" \ " STDEXEC::set_value_t(int, float),\n" \ " // ... or in error with a std::exception_ptr.\n" \ @@ -333,14 +340,14 @@ namespace STDEXEC { " public:\n" \ " using sender_concept = STDEXEC::sender_t;\n" \ "\n" \ - " template \n" \ - " auto get_completion_signatures(_Env&&...) -> STDEXEC::completion_signatures<\n" \ + " template \n" \ + " static consteval auto get_completion_signatures() -> STDEXEC::completion_signatures<\n" \ " // This sender can complete successfully with an int and a float...\n" \ " STDEXEC::set_value_t(int, float),\n" \ " // ... or in error with a std::exception_ptr.\n" \ " STDEXEC::set_error_t(std::exception_ptr)>\n" \ " {\n" \ - " return {};\n" \ + " return {};\n" \ " }\n" \ " ...\n" \ " };\n" diff --git a/include/stdexec/__detail/__ensure_started.hpp b/include/stdexec/__detail/__ensure_started.hpp index c19b61135..d11ec2ab7 100644 --- a/include/stdexec/__detail/__ensure_started.hpp +++ b/include/stdexec/__detail/__ensure_started.hpp @@ -79,7 +79,7 @@ namespace STDEXEC { template static auto transform_sender(set_value_t, _Sender&&, const _Env&) { - return __mexception<_SENDER_TYPE_IS_NOT_COPYABLE_, _WITH_SENDER_<_Sender>>(); + return __not_a_sender<_SENDER_TYPE_IS_NOT_COPYABLE_, _WITH_SENDER_<_Sender>>(); } }; } // namespace __ensure_started @@ -93,16 +93,19 @@ namespace STDEXEC { template <> struct __sexpr_impl : __sexpr_defaults { - static constexpr auto get_completion_signatures = - [](_Sender&&, const _Env&...) noexcept { - // Use the senders decay-copyability as a proxy for whether it is lvalue-connectable. - if constexpr (__decay_copyable<_Sender>) { - using __result_t = - __completion_signatures_of_t, _Env...>; - return __result_t{}; - } else { - return __mexception<_SENDER_TYPE_IS_NOT_COPYABLE_, _WITH_SENDER_<_Sender>>(); - } - }; + template + static consteval auto get_completion_signatures() { + // Use the senders decay-copyability as a proxy for whether it is lvalue-connectable. + if constexpr (__decay_copyable<_Sender>) { + using __sndr_t = + __detail::__transform_sender_result_t>; + return STDEXEC::get_completion_signatures<__sndr_t, _Env...>(); + } else { + return STDEXEC::__invalid_completion_signature< + _SENDER_TYPE_IS_NOT_COPYABLE_, + _WITH_SENDER_<_Sender> + >(); + } + } }; } // namespace STDEXEC diff --git a/include/stdexec/__detail/__execution_fwd.hpp b/include/stdexec/__detail/__execution_fwd.hpp index 02450171a..ed61fc6f0 100644 --- a/include/stdexec/__detail/__execution_fwd.hpp +++ b/include/stdexec/__detail/__execution_fwd.hpp @@ -166,7 +166,7 @@ namespace STDEXEC { concept __is_debug_env = __callable<__queries::__debug_env_t, _Env>; namespace __debug { - struct __completion_signatures; + struct __completion_signatures { }; } // namespace __debug template @@ -224,17 +224,34 @@ namespace STDEXEC { concept __well_formed_completions = bool( __cmplsigs::__well_formed_completions_helper<_Completions>); - // Legacy interface: template requires(sizeof...(_Env) <= 1) [[nodiscard]] - constexpr auto get_completion_signatures(_Sender&&, _Env&&...) noexcept // - -> __well_formed_completions auto; + consteval auto get_completion_signatures() -> __well_formed_completions auto; + // Legacy interface: template requires(sizeof...(_Env) <= 1) [[nodiscard]] - consteval auto get_completion_signatures() -> __well_formed_completions auto; + constexpr auto get_completion_signatures(_Sender&&, const _Env&...) noexcept // + -> __well_formed_completions auto; + + #if STDEXEC_NO_STD_CONSTEXPR_EXCEPTIONS() + + template + consteval auto __invalid_completion_signature(_Values...) -> __mexception<_What...>; + + #else // ^^^ no constexpr exceptions ^^^ / vvv constexpr exceptions vvv + + // C++26, https://wg21.link/p3068 + template + consteval auto __invalid_completion_signature(_Values...) + -> completion_signatures<>; + + #endif // ^^^ constexpr exceptions ^^^ + + template + consteval auto __invalid_completion_signature(__mexception<_What...>); ////////////////////////////////////////////////////////////////////////////////////////////////// namespace __connect { @@ -393,13 +410,6 @@ namespace STDEXEC { using __on::on_t; extern const on_t on; - - // namespace __detail { - // struct __sexpr_apply_t; - // } // namespace __detail - - // using __detail::__sexpr_apply_t; - // extern const __sexpr_apply_t __sexpr_apply; } // namespace STDEXEC template diff --git a/include/stdexec/__detail/__get_completion_signatures.hpp b/include/stdexec/__detail/__get_completion_signatures.hpp index 0e0858645..f2d767066 100644 --- a/include/stdexec/__detail/__get_completion_signatures.hpp +++ b/include/stdexec/__detail/__get_completion_signatures.hpp @@ -74,8 +74,8 @@ namespace STDEXEC { template [[nodiscard]] - consteval auto __invalid_completion_signature(_Values...) { - return __mexception<_What...>(); + consteval auto __invalid_completion_signature(_Values...) -> __mexception<_What...> { + return {}; } #else // ^^^ no constexpr exceptions ^^^ / vvv constexpr exceptions vvv @@ -102,6 +102,12 @@ namespace STDEXEC { return STDEXEC::__invalid_completion_signature<_What...>(); } + // Returns _Sender if sizeof...(_Env) == 0, otherwise it is the result of applying + // transform_sender_result_t to _Sender with _Env. + template + using __maybe_transform_sender_t = + __mmemoize<__mfold_right<_Sender, __q>, _Env...>; + //////////////////////////////////////////////////////////////////////////////////////////////////// // get_completion_signatures STDEXEC_PRAGMA_PUSH() @@ -114,10 +120,10 @@ namespace STDEXEC { STDEXEC_REMOVE_REFERENCE( \ STDEXEC_PP_FRONT(__VA_ARGS__))::template get_completion_signatures<__VA_ARGS__>() -#define STDEXEC_CHECKED_COMPLSIGS(...) \ +#define STDEXEC_CHECKED_COMPLSIGS(_SENDER, _ENV, ...) \ (static_cast(__VA_ARGS__), \ - void(), \ - STDEXEC::__cmplsigs::__checked_complsigs()) + STDEXEC::__cmplsigs::__checked_complsigs( \ + static_cast<__types<_SENDER, _ENV...>*>(nullptr))) struct _A_GET_COMPLETION_SIGNATURES_CUSTOMIZATION_RETURNED_A_TYPE_THAT_IS_NOT_A_COMPLETION_SIGNATURES_SPECIALIZATION { }; @@ -126,20 +132,23 @@ namespace STDEXEC { concept __non_sender = !enable_sender<__decay_t<_Ty>>; template <__valid_completion_signatures _Completions> - consteval auto __checked_complsigs() { + consteval auto __checked_complsigs(void*) { return _Completions(); } - template - consteval auto __checked_complsigs() { + template + requires(!__valid_completion_signatures<_Completions>) + consteval auto __checked_complsigs(__types<_Sender, _Env...>*) { if constexpr (__merror<_Completions>) { // NOLINT(bugprone-branch-clone) - return _Completions(); + return STDEXEC::__invalid_completion_signature(_Completions()); } else if constexpr (STDEXEC_IS_BASE_OF(dependent_sender_error, _Completions)) { return _Completions(); } else { return __invalid_completion_signature< _A_GET_COMPLETION_SIGNATURES_CUSTOMIZATION_RETURNED_A_TYPE_THAT_IS_NOT_A_COMPLETION_SIGNATURES_SPECIALIZATION, - _WITH_COMPLETION_SIGNATURES_(_Completions) + _WITH_COMPLETION_SIGNATURES_(_Completions), + _WITH_SENDER_<_Sender>, + _WITH_ENVIRONMENT_<_Env>... >(); } } @@ -156,15 +165,21 @@ namespace STDEXEC { template concept __with_member = __mvalid<__member_result_t, _Sender, _Env...>; + template + using __member_alias_t = STDEXEC_REMOVE_REFERENCE(_Sender)::completion_signatures; + template concept __with_static_member = __mvalid<__static_member_result_t, _Sender, _Env...>; template - concept __with_consteval_static_member = (__non_sender<_Env> && ...) - && requires { STDEXEC_GET_COMPLSIGS(_Sender, _Env...); }; + concept __with_consteval_static_member = // + (__non_sender<_Env> && ...) // + && requires { STDEXEC_GET_COMPLSIGS(_Sender, _Env...); }; + // [WAR]: see nvbugs#5813160 template - using __member_alias_t = STDEXEC_REMOVE_REFERENCE(_Sender)::completion_signatures; + concept __with_non_dependent_consteval_static_member = // + requires { STDEXEC_GET_COMPLSIGS(_Sender); }; template concept __with_tag_invoke = tag_invocable; @@ -181,22 +196,26 @@ namespace STDEXEC { consteval auto __get_completion_signatures_helper() { using namespace __cmplsigs; if constexpr (__with_static_member<_Sender, _Env...>) { - return STDEXEC_CHECKED_COMPLSIGS(__static_member_result_t<_Sender, _Env...>()); + using _Result = __static_member_result_t<_Sender, _Env...>; + return STDEXEC_CHECKED_COMPLSIGS(_Sender, _Env, _Result()); } else if constexpr (__with_member<_Sender, _Env...>) { - return STDEXEC_CHECKED_COMPLSIGS(__member_result_t<_Sender, _Env...>()); + return STDEXEC_CHECKED_COMPLSIGS(_Sender, _Env, __member_result_t<_Sender, _Env...>()); } else if constexpr (__with_member_alias<_Sender>) { - return STDEXEC_CHECKED_COMPLSIGS(__member_alias_t<_Sender>()); + return STDEXEC_CHECKED_COMPLSIGS(_Sender, _Env, __member_alias_t<_Sender>()); } else if constexpr (__with_consteval_static_member<_Sender, _Env...>) { - return STDEXEC_CHECKED_COMPLSIGS(STDEXEC_GET_COMPLSIGS(_Sender, _Env...)); - } else if constexpr (__with_consteval_static_member<_Sender>) { - return STDEXEC_CHECKED_COMPLSIGS(STDEXEC_GET_COMPLSIGS(_Sender)); + return STDEXEC_CHECKED_COMPLSIGS(_Sender, _Env, STDEXEC_GET_COMPLSIGS(_Sender, _Env...)); + } else if constexpr (__with_non_dependent_consteval_static_member<_Sender>) { + return STDEXEC_CHECKED_COMPLSIGS(_Sender, _Env, STDEXEC_GET_COMPLSIGS(_Sender)); } else if constexpr (__with_tag_invoke<_Sender, _Env...>) { + static_assert(!__with_tag_invoke<_Sender, _Env...>); + //__deprecated_tag_invoke_completion_signatures_warning(); // NOLINT(deprecated-declarations) using _Result = tag_invoke_result_t; - return STDEXEC_CHECKED_COMPLSIGS(_Result()); + return STDEXEC_CHECKED_COMPLSIGS(_Sender, _Env, _Result()); } else if constexpr (__with_legacy_tag_invoke<_Sender, _Env...>) { // This branch is strictly for backwards compatibility + //__deprecated_tag_invoke_completion_signatures_warning(); // NOLINT(deprecated-declarations) using _Result = tag_invoke_result_t>; - return STDEXEC_CHECKED_COMPLSIGS(_Result()); + return STDEXEC_CHECKED_COMPLSIGS(_Sender, _Env, _Result()); } else if constexpr (bool(__awaitable<_Sender, __detail::__promise<_Env>...>)) { // [WAR] The explicit cast to bool above is to work around a bug in nvc++ (nvbug#4707793) using _Result = __await_result_t<_Sender, __detail::__promise<_Env>...>; @@ -207,7 +226,7 @@ namespace STDEXEC { } else if constexpr ((__is_debug_env<_Env> || ...)) { // This ought to cause a hard error that indicates where the problem is. using _Completions [[maybe_unused]] = decltype(STDEXEC_GET_COMPLSIGS(_Sender, _Env...)); - return static_cast<__debug::__completion_signatures (*)()>(nullptr); + return __debug::__completion_signatures(); } else { return __unrecognized_sender_error<_Sender, _Env...>(); } @@ -215,19 +234,13 @@ namespace STDEXEC { // For backwards compatibility struct get_completion_signatures_t { - template - constexpr auto operator()(_Sender&&) const noexcept - -> decltype(__get_completion_signatures_helper<_Sender>()) { - return {}; - } - - template - constexpr auto operator()(_Sender&&, _Env&&) const noexcept - -> decltype(__get_completion_signatures_helper< - transform_sender_result_t<_Sender, _Env>, - _Env - >()) { - return {}; + template + constexpr auto operator()(_Sender&&, const _Env&...) const noexcept { + // _Sender2 is _Sender if sizeof...(_Env) == 0, otherwise it is the result of applying + // transform_sender_result_t to _Sender with _Env. + using _NewSender = __maybe_transform_sender_t<_Sender, _Env...>; + using _Completions = decltype(__get_completion_signatures_helper<_NewSender, _Env...>()); + return _Completions{}; } }; } // namespace __cmplsigs @@ -238,48 +251,23 @@ namespace STDEXEC { requires(sizeof...(_Env) <= 1) [[nodiscard]] consteval auto get_completion_signatures() -> __well_formed_completions auto { - if constexpr (0 == sizeof...(_Env)) { - return __cmplsigs::__get_completion_signatures_helper<_Sender>(); + using _NewSender = __maybe_transform_sender_t<_Sender, _Env...>; + if constexpr (__merror<_NewSender>) { + // Computing the type of the transformed sender returned an error type. Propagate it. + return STDEXEC::__invalid_completion_signature(_NewSender()); } else { - // Apply a lazy sender transform if one exists before computing the completion signatures: - using _NewSender = transform_sender_result_t<_Sender, _Env...>; - if constexpr (__merror<_NewSender>) { - // Computing the type of the transformed sender returned an error type. Propagate it. - return STDEXEC::__invalid_completion_signature(_NewSender()); - } else { - return __cmplsigs::__get_completion_signatures_helper<_NewSender, _Env...>(); - } + return __cmplsigs::__get_completion_signatures_helper<_NewSender, _Env...>(); } } // Legacy interface: template requires(sizeof...(_Env) <= 1) - constexpr auto get_completion_signatures(_Sender&&, _Env&&...) noexcept // + constexpr auto get_completion_signatures(_Sender&&, const _Env&...) noexcept // -> __well_formed_completions auto { return STDEXEC::get_completion_signatures<_Sender, _Env...>(); } - template - requires __cmplsigs::__with_legacy_tag_invoke<_Sender> - [[deprecated("the use of tag_invoke for get_completion_signatures is deprecated")]] - constexpr auto get_completion_signatures(_Sender&&) noexcept // - -> __well_formed_completions auto { - return tag_invoke_result_t(); - } - - template - requires __cmplsigs::__with_legacy_tag_invoke, _Env> - [[deprecated("the use of tag_invoke for get_completion_signatures is deprecated")]] - constexpr auto get_completion_signatures(_Sender&&, _Env&&...) noexcept // - -> __well_formed_completions auto { - return tag_invoke_result_t< - get_completion_signatures_t, - transform_sender_result_t<_Sender, _Env>, - _Env - >(); - } - // An minimally constrained alias for get_completion_signatures: template requires enable_sender<__decay_t<_Sender>> diff --git a/include/stdexec/__detail/__let.hpp b/include/stdexec/__detail/__let.hpp index ade781d8e..49f5bf794 100644 --- a/include/stdexec/__detail/__let.hpp +++ b/include/stdexec/__detail/__let.hpp @@ -284,7 +284,7 @@ namespace STDEXEC { using __env2_t = __let::__env2_t<_SetTag, env_of_t, env_of_t>; using __second_rcvr_t = __receiver_with_env_t<_Receiver, __env2_t>; - template + template constexpr void __impl(_Receiver& __rcvr, _Tag __tag, _Args&&... __args) noexcept { if constexpr (std::is_same_v<_SetTag, _Tag>) { using __sender_t = __call_result_t<_Fun, __decay_t<_Args>&...>; @@ -316,15 +316,15 @@ namespace STDEXEC { using receiver_concept = ::STDEXEC::receiver_t; __let_state& __state; _Receiver& __rcvr; - template + template constexpr void set_value(_Args&&... __args) noexcept { __state.__impl(__rcvr, ::STDEXEC::set_value, static_cast<_Args&&>(__args)...); } - template + template constexpr void set_error(_Args&&... __args) noexcept { __state.__impl(__rcvr, ::STDEXEC::set_error, static_cast<_Args&&>(__args)...); } - template + template constexpr void set_stopped(_Args&&... __args) noexcept { __state.__impl(__rcvr, ::STDEXEC::set_stopped, static_cast<_Args&&>(__args)...); } @@ -564,9 +564,9 @@ namespace STDEXEC { template STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE __data_t(_Sender, _Fun) -> __data_t<_Sender, _Fun>; - template + template using __sender_of = decltype((__declval<__data_of<_Sender>>().__sndr)); - template + template using __fun_of = decltype((__declval<__data_of<_Sender>>().__fun)); //! Implementation of the `let_*_t` types, where `_SetTag` is, e.g., `set_value_t` for `let_value`. @@ -585,16 +585,16 @@ namespace STDEXEC { } template - auto transform_sender(set_value_t, _Sender&& __sndr, __ignore) { - if constexpr (!__movable_value<_Sender>) { - return __mexception<_SENDER_TYPE_IS_NOT_COPYABLE_, _WITH_SENDER_<_Sender>>{}; - } else { + static auto transform_sender(set_value_t, _Sender&& __sndr, __ignore) { + if constexpr (__decay_copyable<_Sender>) { return __apply( - [](__ignore, _Fun&& __fun, _Child&& __child) { + [](__let_t<_SetTag>, _Fun&& __fun, _Child&& __child) { return __make_sexpr<__let_tag<_SetTag>>( __data_t{static_cast<_Child&&>(__child), static_cast<_Fun&&>(__fun)}); }, static_cast<_Sender&&>(__sndr)); + } else { + return __not_a_sender<_SENDER_TYPE_IS_NOT_COPYABLE_, _WITH_SENDER_<_Sender>>(); } } }; @@ -607,18 +607,22 @@ namespace STDEXEC { return STDEXEC::get_env(__data.__sndr); }; - static constexpr auto get_completion_signatures = - [](_Self&&, _Env&&...) noexcept { - static_assert(sender_expr_for<_Self, __let_tag<_SetTag>>); - if constexpr (__decay_copyable<_Self>) { - using __fn_t = __decay_t<__fun_of<_Self>>; - using __result_t = - __completions_t<__let_tag<_SetTag>, __fn_t, __sender_of<_Self>, _Env>; - return __result_t{}; - } else { - return __mexception<_SENDER_TYPE_IS_NOT_COPYABLE_, _WITH_SENDER_<_Self>>{}; - } - }; + template + static consteval auto get_completion_signatures() { + static_assert(sender_expr_for<_Sender, __let_tag<_SetTag>>); + if constexpr (__decay_copyable<_Sender>) { + using __fn_t = __decay_t<__fun_of<_Sender>>; + // TODO: update this to use constant evaluation + using __result_t = + __completions_t<__let_tag<_SetTag>, __fn_t, __sender_of<_Sender>, _Env>; + return __result_t{}; + } else { + return STDEXEC::__invalid_completion_signature< + _SENDER_TYPE_IS_NOT_COPYABLE_, + _WITH_SENDER_<_Sender> + >(); + } + } static constexpr auto get_state = [](_Sender&& __sndr, _Receiver& __rcvr) @@ -641,7 +645,7 @@ namespace STDEXEC { }; static constexpr auto start = - [](_State& __state, _Receiver&) noexcept { + [](_State& __state, _Receiver&) noexcept { ::STDEXEC::start(__state.__storage_.template get<1>()); }; }; @@ -656,9 +660,12 @@ namespace STDEXEC { template struct __sexpr_impl<__let::__let_t<_SetTag>> : __sexpr_defaults { - static constexpr auto get_completion_signatures = - [](_Sender&&, const _Env&...) noexcept - -> __completion_signatures_of_t, _Env...> { - }; + template + static consteval auto get_completion_signatures() { + static_assert(sender_expr_for<_Sender, __let::__let_t<_SetTag>>); + using __sndr_t = + __detail::__transform_sender_result_t<__let::__let_t<_SetTag>, set_value_t, _Sender, env<>>; + return STDEXEC::get_completion_signatures<__sndr_t, _Env...>(); + } }; } // namespace STDEXEC diff --git a/include/stdexec/__detail/__on.hpp b/include/stdexec/__detail/__on.hpp index a4001721a..eb7829ce4 100644 --- a/include/stdexec/__detail/__on.hpp +++ b/include/stdexec/__detail/__on.hpp @@ -53,16 +53,14 @@ namespace STDEXEC { struct __no_scheduler_in_environment { using sender_concept = sender_t; - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)( - this const __no_scheduler_in_environment&, - const auto&) noexcept { - return __mexception< + template + static consteval auto get_completion_signatures() { + return STDEXEC::__invalid_completion_signature< _CANNOT_RESTORE_EXECUTION_CONTEXT_AFTER_ON_<>, _WITH_SENDER_<_Sender>, _WITH_ENVIRONMENT_<_Env> - >{}; + >(); } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) }; template @@ -167,10 +165,10 @@ namespace STDEXEC { template <> struct __sexpr_impl : __sexpr_defaults { - static constexpr auto get_completion_signatures = - [](_Sender&&, const _Env&) noexcept - -> __completion_signatures_of_t, _Env> { - return {}; - }; + template + static constexpr auto get_completion_signatures() { + using __sndr_t = __detail::__transform_sender_result_t; + return STDEXEC::get_completion_signatures<__sndr_t, _Env>(); + } }; } // namespace STDEXEC diff --git a/include/stdexec/__detail/__read_env.hpp b/include/stdexec/__detail/__read_env.hpp index 668a15cc2..e36153d72 100644 --- a/include/stdexec/__detail/__read_env.hpp +++ b/include/stdexec/__detail/__read_env.hpp @@ -45,21 +45,6 @@ namespace STDEXEC { inline constexpr __mstring __query_failed_diag = "The current execution environment doesn't have a value for the given query."_mstr; - template - using __query_failed_error = __mexception< - _NOT_CALLABLE_<"In STDEXEC::read_env()..."_mstr, __query_failed_diag>, - _WITH_QUERY_<_Tag>, - _WITH_ENVIRONMENT_<_Env> - >; - - template - requires __callable<_Tag, _Env> - using __completions_t = __if_c< - __nothrow_callable<_Tag, _Env>, - completion_signatures)>, - completion_signatures), set_error_t(std::exception_ptr)> - >; - template struct __state { using __query = _Tag; @@ -74,6 +59,23 @@ namespace STDEXEC { using __result = _Ty; }; + template + struct __attrs { + template + requires __callable<_Tag, _Env> + STDEXEC_ATTRIBUTE(nodiscard) + constexpr auto query(get_completion_behavior_t, const _Env&) const noexcept { + return completion_behavior::inline_completion; + } + + template + requires __callable<_Tag, _Env> && (!__nothrow_callable<_Tag, _Env>) + STDEXEC_ATTRIBUTE(nodiscard) + constexpr auto query(get_completion_behavior_t, const _Env&) const noexcept { + return completion_behavior::inline_completion; + } + }; + struct read_env_t { template constexpr auto operator()(_Tag) const noexcept { @@ -82,23 +84,27 @@ namespace STDEXEC { }; struct __read_env_impl : __sexpr_defaults { - template - using __completions_t = - __minvoke<__mtry_catch_q<__read::__completions_t, __q<__query_failed_error>>, _Tag, _Env>; - - static constexpr auto get_attrs = [](__ignore) noexcept - -> env< - cprop, completion_behavior::inline_completion>, - cprop, completion_behavior::inline_completion>, - cprop, completion_behavior::inline_completion> - > { - return {}; + static constexpr auto get_attrs = [](_Tag) noexcept { + return __attrs<_Tag>{}; }; - static constexpr auto get_completion_signatures = - [](const _Self&, _Env&&) noexcept - -> __completions_t<__data_of<_Self>, _Env> { - return {}; + template + static consteval auto get_completion_signatures() { + using __query_t = __data_of<_Self>; + if constexpr (__callable<__query_t, _Env>) { + using __result_t = __call_result_t<__query_t, _Env>; + if constexpr (__nothrow_callable<__query_t, _Env>) { + return completion_signatures(); + } else { + return completion_signatures(); + } + } else { + return STDEXEC::__invalid_completion_signature< + _NOT_CALLABLE_<"In STDEXEC::read_env()..."_mstr, __query_failed_diag>, + _WITH_QUERY_<__query_t>, + _WITH_ENVIRONMENT_<_Env> + >(); + } }; static constexpr auto get_state = diff --git a/include/stdexec/__detail/__run_loop.hpp b/include/stdexec/__detail/__run_loop.hpp index 6763bab8e..8cbf1ff8e 100644 --- a/include/stdexec/__detail/__run_loop.hpp +++ b/include/stdexec/__detail/__run_loop.hpp @@ -18,15 +18,15 @@ #include "__execution_fwd.hpp" // include these after __execution_fwd.hpp +#include "__atomic.hpp" #include "__atomic_intrusive_queue.hpp" #include "__completion_signatures.hpp" #include "__domain.hpp" #include "__env.hpp" #include "__receivers.hpp" #include "__schedulers.hpp" +#include "__stop_token.hpp" -#include "__atomic.hpp" -#include "__config.hpp" #include namespace STDEXEC { @@ -99,7 +99,10 @@ namespace STDEXEC { static_assert(noexcept(get_stop_token(__declval>()).stop_requested())); auto& __rcvr = static_cast<__opstate_t*>(__p)->__rcvr_; - if (get_stop_token(get_env(__rcvr)).stop_requested()) { + // NOLINTNEXTLINE(bugprone-branch-clone) + if constexpr (unstoppable_token>>) { + set_value(static_cast<_Rcvr&&>(__rcvr)); + } else if (get_stop_token(get_env(__rcvr)).stop_requested()) { set_stopped(static_cast<_Rcvr&&>(__rcvr)); } else { set_value(static_cast<_Rcvr&&>(__rcvr)); diff --git a/include/stdexec/__detail/__shared.hpp b/include/stdexec/__detail/__shared.hpp index 80b464e33..5831f98e4 100644 --- a/include/stdexec/__detail/__shared.hpp +++ b/include/stdexec/__detail/__shared.hpp @@ -18,6 +18,8 @@ #include "__execution_fwd.hpp" // include these after __execution_fwd.hpp +#include "../stop_token.hpp" +#include "__atomic.hpp" #include "__basic_sender.hpp" #include "__env.hpp" #include "__intrusive_slist.hpp" @@ -28,9 +30,6 @@ #include "__tuple.hpp" #include "__variant.hpp" // IWYU pragma: keep -#include "../stop_token.hpp" - -#include "__atomic.hpp" #include #include #include @@ -63,22 +62,19 @@ namespace STDEXEC::__shared { _BaseEnv >; // BUGBUG NOT TO SPEC - template struct __notify_fn { - template - void operator()(_Tag __tag, _Args&&... __args) const noexcept { - __tag(static_cast<_Receiver&&>(__rcvr_), static_cast<_Args&&>(__args)...); + template + void operator()(_Receiver& __rcvr, _Tag __tag, _Args&&... __args) const noexcept { + __tag(static_cast<_Receiver&&>(__rcvr), static_cast<_Args&&>(__args)...); } - - _Receiver& __rcvr_; }; - template - auto __make_notify_visitor(_Receiver& __rcvr) noexcept { - return [&](_Tuple&& __tupl) noexcept -> void { - STDEXEC::__apply(__notify_fn<_Receiver>{__rcvr}, static_cast<_Tuple&&>(__tupl)); + struct __notify_visitor { + template + void operator()(_Receiver& __rcvr, _Tuple&& __tupl) const noexcept { + STDEXEC::__apply(__notify_fn(), static_cast<_Tuple&&>(__tupl), __rcvr); }; - } + }; struct __local_state_base : __immovable { using __notify_fn = void(__local_state_base*) noexcept; @@ -157,8 +153,10 @@ namespace STDEXEC::__shared { __self->__on_stop_.reset(); - auto __visitor = __make_notify_visitor(__self->__receiver()); - __variant_t::visit(__visitor, static_cast<__cv_variant_t&&>(__self->__sh_state_->__results_)); + __variant_t::visit( + __notify_visitor(), + static_cast<__cv_variant_t&&>(__self->__sh_state_->__results_), + __self->__receiver()); } static auto __get_sh_state(_CvrefSender& __sndr) noexcept { @@ -440,11 +438,12 @@ namespace STDEXEC::__shared { return __local_state<_CvrefSender, _Receiver>{static_cast<_CvrefSender&&>(__sndr)}; }; - static constexpr auto get_completion_signatures = - [](const _Self&, auto&&...) noexcept - -> __completions_t<_Tag, typename __data_of<_Self>::__sh_state_t> { - static_assert(sender_expr_for<_Self, _Tag>); - return {}; + template + static consteval auto get_completion_signatures() { + static_assert(sender_expr_for<_Sender, _Tag>); + // TODO: update this for constant evaluation + using __shared_state_t = STDEXEC_REMOVE_REFERENCE(__data_of<_Sender>)::__sh_state_t; + return __completions_t<_Tag, __shared_state_t>{}; }; static constexpr auto start = []( diff --git a/include/stdexec/__detail/__split.hpp b/include/stdexec/__detail/__split.hpp index c98180bd5..b0a963df1 100644 --- a/include/stdexec/__detail/__split.hpp +++ b/include/stdexec/__detail/__split.hpp @@ -51,25 +51,25 @@ namespace STDEXEC { template using __receiver_t = __t<__meval<__receiver, __cvref_id<_CvrefSender>, __id<_Env>>>; - template <__decay_copyable _Sender, class _Env> - static auto transform_sender(set_value_t, _Sender&& __sndr, const _Env&) { + template <__decay_copyable _Sender> + static auto transform_sender(set_value_t, _Sender&& __sndr, __ignore) { using _Receiver = __receiver_t<__child_of<_Sender>, __decay_t<__data_of<_Sender>>>; static_assert(sender_to<__child_of<_Sender>, _Receiver>); return __apply( - [&](__ignore, _Env2&& __env, _Child&& __child) { + [&](__ignore, _Env&& __env, _Child&& __child) { // The shared state starts life with a ref-count of one. auto* __sh_state = - new __shared_state{static_cast<_Child&&>(__child), static_cast<_Env2&&>(__env)}; + new __shared_state{static_cast<_Child&&>(__child), static_cast<_Env&&>(__env)}; return __make_sexpr<__split_t>(__box{__split_t(), __sh_state}); }, static_cast<_Sender&&>(__sndr)); } - template - static auto transform_sender(set_value_t, _Sender&&, const _Env&) { - return __mexception<_SENDER_TYPE_IS_NOT_COPYABLE_, _WITH_SENDER_<_Sender>>(); + template + static auto transform_sender(set_value_t, _Sender&&, __ignore) { + return __not_a_sender<_SENDER_TYPE_IS_NOT_COPYABLE_, _WITH_SENDER_<_Sender>>(); } }; } // namespace __split @@ -82,16 +82,19 @@ namespace STDEXEC { template <> struct __sexpr_impl : __sexpr_defaults { - static constexpr auto get_completion_signatures = - [](_Sender&&, const _Env&...) noexcept { - // Use the senders decay-copyability as a proxy for whether it is lvalue-connectable. - if constexpr (__decay_copyable<_Sender>) { - using __result_t = - __completion_signatures_of_t, _Env...>; - return __result_t{}; - } else { - return __mexception<_SENDER_TYPE_IS_NOT_COPYABLE_, _WITH_SENDER_<_Sender>>(); - } - }; + template + static consteval auto get_completion_signatures() { + // Use the senders decay-copyability as a proxy for whether it is lvalue-connectable. + if constexpr (__decay_copyable<_Sender>) { + using __sndr_t = + __detail::__transform_sender_result_t>; + return STDEXEC::get_completion_signatures<__sndr_t, _Env...>(); + } else { + return STDEXEC::__invalid_completion_signature< + _SENDER_TYPE_IS_NOT_COPYABLE_, + _WITH_SENDER_<_Sender> + >(); + } + } }; } // namespace STDEXEC diff --git a/include/stdexec/__detail/__starts_on.hpp b/include/stdexec/__detail/__starts_on.hpp index 1c71ec848..20a62f235 100644 --- a/include/stdexec/__detail/__starts_on.hpp +++ b/include/stdexec/__detail/__starts_on.hpp @@ -55,8 +55,8 @@ namespace STDEXEC { static_cast<_Scheduler&&>(__sched), static_cast<_Sender&&>(__sndr)); } - template <__decay_copyable _Sender, class _Env> - static auto transform_sender(set_value_t, _Sender&& __sndr, const _Env&) { + template <__decay_copyable _Sender> + static auto transform_sender(set_value_t, _Sender&& __sndr, __ignore) { return __apply( [](__ignore, _Data&& __data, _Child&& __child) -> auto { // This is the heart of starts_on: It uses `let_value` to schedule `__child` on the given scheduler: @@ -66,9 +66,9 @@ namespace STDEXEC { static_cast<_Sender&&>(__sndr)); } - template - static auto transform_sender(set_value_t, _Sender&&, const _Env&) { - return _ERROR_<_SENDER_TYPE_IS_NOT_COPYABLE_, _WITH_SENDER_<_Sender>>{}; + template + static auto transform_sender(set_value_t, _Sender&&, __ignore) { + return __not_a_sender<_SENDER_TYPE_IS_NOT_COPYABLE_, _WITH_SENDER_<_Sender>>{}; } }; } // namespace __starts_on_ns @@ -142,10 +142,11 @@ namespace STDEXEC { return __attrs<_Data, _Child>{__data, STDEXEC::get_env(__child)}; }; - static constexpr auto get_completion_signatures = - [](_Sender&&, const _Env&...) noexcept - -> __completion_signatures_of_t, _Env...> { - return {}; + template + static consteval auto get_completion_signatures() { + using __sndr_t = + __detail::__transform_sender_result_t>; + return STDEXEC::get_completion_signatures<__sndr_t, _Env...>(); }; }; } // namespace STDEXEC diff --git a/include/stdexec/__detail/__transfer_just.hpp b/include/stdexec/__detail/__transfer_just.hpp index 7f27a2b6e..21a7d5de9 100644 --- a/include/stdexec/__detail/__transfer_just.hpp +++ b/include/stdexec/__detail/__transfer_just.hpp @@ -55,12 +55,12 @@ namespace STDEXEC { __tuple{static_cast<_Scheduler&&>(__sched), static_cast<_Values&&>(__vals)...}); } - template - static auto transform_sender(set_value_t, _Sender&& __sndr, const _Env&) { - if constexpr (!__decay_copyable<_Sender>) { - return __mexception<_SENDER_TYPE_IS_NOT_COPYABLE_, _WITH_SENDER_<_Sender>>(); - } else { + template + static auto transform_sender(set_value_t, _Sender&& __sndr, __ignore) { + if constexpr (__decay_copyable<_Sender>) { return __apply(__transform_sender_fn(), static_cast<_Sender&&>(__sndr)); + } else { + return __not_a_sender<_SENDER_TYPE_IS_NOT_COPYABLE_, _WITH_SENDER_<_Sender>>(); } } }; @@ -77,9 +77,11 @@ namespace STDEXEC { return STDEXEC::__apply(__make_attrs_fn(), __data); }; - static constexpr auto get_completion_signatures = - [](_Sender&&, const _Env&...) noexcept - -> __completion_signatures_of_t, _Env...> { + template + static consteval auto get_completion_signatures() { + using __sndr_t = + __detail::__transform_sender_result_t>; + return STDEXEC::get_completion_signatures<__sndr_t, _Env...>(); }; }; } // namespace __transfer_just diff --git a/include/stdexec/__detail/__transform_sender.hpp b/include/stdexec/__detail/__transform_sender.hpp index 03e4e62c2..8c8ff59d0 100644 --- a/include/stdexec/__detail/__transform_sender.hpp +++ b/include/stdexec/__detail/__transform_sender.hpp @@ -45,7 +45,7 @@ namespace STDEXEC { constexpr bool __is_nothrow = __has_nothrow_transform_sender<__domain_t, _OpTag, _Sndr, _Env>; - if constexpr (!__ok<__result_t>) { + if constexpr (__merror<__result_t>) { return __declfn<__result_t>(); } else if constexpr (__same_as<__result_t, _Sndr>) { return __declfn<__result_t, __is_nothrow>(); @@ -115,6 +115,14 @@ namespace STDEXEC { >; public: + // NOT TO SPEC: + template + STDEXEC_ATTRIBUTE(nodiscard, host, device) + constexpr auto operator()(_Sndr&& __sndr) const noexcept(__nothrow_move_constructible<_Sndr>) // + -> _Sndr { + return static_cast<_Sndr&&>(__sndr); + } + template {}> STDEXEC_ATTRIBUTE(nodiscard, host, device) constexpr auto operator()(_Sndr && __sndr, const _Env & __env) const diff --git a/include/stdexec/__detail/__when_all.hpp b/include/stdexec/__detail/__when_all.hpp index 00672bef2..7d319a7dd 100644 --- a/include/stdexec/__detail/__when_all.hpp +++ b/include/stdexec/__detail/__when_all.hpp @@ -466,8 +466,8 @@ namespace STDEXEC { return __make_sexpr(__(), static_cast<_Senders&&>(__sndrs)...); } - template - static auto transform_sender(set_value_t, _Sender&& __sndr, const _Env&) { + template + static auto transform_sender(set_value_t, _Sender&& __sndr, __ignore) { // transform when_all_with_variant(sndrs...) into when_all(into_variant(sndrs)...). return __apply( [&](__ignore, __ignore, _Child&&... __child) { @@ -482,10 +482,15 @@ namespace STDEXEC { return __when_all::__attrs<_Child...>{}; }; - static constexpr auto get_completion_signatures = - [](_Sender&&, const _Env&...) noexcept - -> __completion_signatures_of_t, _Env...> { - return {}; + template + static consteval auto get_completion_signatures() { + using __sndr_t = __detail::__transform_sender_result_t< + when_all_with_variant_t, + set_value_t, + _Sender, + env<> + >; + return STDEXEC::get_completion_signatures<__sndr_t, _Env...>(); }; }; @@ -497,8 +502,8 @@ namespace STDEXEC { static_cast<_Scheduler&&>(__sched), static_cast<_Senders&&>(__sndrs)...); } - template - static auto transform_sender(set_value_t, _Sender&& __sndr, const _Env&) { + template + static auto transform_sender(set_value_t, _Sender&& __sndr, __ignore) { // transform transfer_when_all(sch, sndrs...) into // continues_on(when_all(sndrs...), sch). return __apply( @@ -518,10 +523,11 @@ namespace STDEXEC { return __sched_attrs{std::cref(__sched)}; }; - static constexpr auto get_completion_signatures = - [](_Sender&&, const _Env&...) noexcept - -> __completion_signatures_of_t, _Env...> { - return {}; + template + static consteval auto get_completion_signatures() { + using __sndr_t = + __detail::__transform_sender_result_t>; + return STDEXEC::get_completion_signatures<__sndr_t, _Env...>(); }; }; @@ -533,8 +539,8 @@ namespace STDEXEC { static_cast<_Scheduler&&>(__sched), static_cast<_Senders&&>(__sndrs)...); } - template - static auto transform_sender(set_value_t, _Sender&& __sndr, const _Env&) { + template + static auto transform_sender(set_value_t, _Sender&& __sndr, __ignore) { // transform the transfer_when_all_with_variant(sch, sndrs...) into // transfer_when_all(sch, into_variant(sndrs...)) return __apply( @@ -553,10 +559,15 @@ namespace STDEXEC { return __sched_attrs{std::cref(__sched)}; }; - static constexpr auto get_completion_signatures = - [](_Sender&&, const _Env&...) noexcept - -> __completion_signatures_of_t, _Env...> { - return {}; + template + static consteval auto get_completion_signatures() { + using __sndr_t = __detail::__transform_sender_result_t< + transfer_when_all_with_variant_t, + set_value_t, + _Sender, + env<> + >; + return STDEXEC::get_completion_signatures<__sndr_t, _Env...>(); }; }; } // namespace __when_all diff --git a/test/nvexec/common.cuh b/test/nvexec/common.cuh index 486afbe1b..1a142e8f0 100644 --- a/test/nvexec/common.cuh +++ b/test/nvexec/common.cuh @@ -184,11 +184,9 @@ namespace { STDEXEC_EXPLICIT_THIS_END(connect) template Self, class... Env> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this Self&&, Env&&...) - -> __completions_t { + static consteval auto get_completion_signatures() -> __completions_t { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) auto get_env() const noexcept -> STDEXEC::__fwd_env_t> { return STDEXEC::__fwd_env(STDEXEC::get_env(sndr_)); @@ -254,11 +252,9 @@ namespace { STDEXEC_EXPLICIT_THIS_END(connect) template Self, class... Env> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this Self&&, Env&&...) - -> _completions_t { + static consteval auto get_completion_signatures() -> _completions_t { return {}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) auto get_env() const noexcept -> STDEXEC::__fwd_env_t> { return STDEXEC::__fwd_env(STDEXEC::get_env(sndr_)); diff --git a/test/stdexec/algos/adaptors/test_let_value.cpp b/test/stdexec/algos/adaptors/test_let_value.cpp index 3ce10ea8e..80e480b57 100644 --- a/test/stdexec/algos/adaptors/test_let_value.cpp +++ b/test/stdexec/algos/adaptors/test_let_value.cpp @@ -363,12 +363,13 @@ namespace { struct throws_on_connect { using sender_concept = ::STDEXEC::sender_t; - template - static consteval ::STDEXEC::completion_signatures<::STDEXEC::set_value_t()> - get_completion_signatures(const Args&...) noexcept { - return {}; + + template + static consteval auto get_completion_signatures() noexcept { + return ::STDEXEC::completion_signatures<::STDEXEC::set_value_t()>{}; } - template + + template auto connect(Receiver) const -> ::STDEXEC::connect_result_t { throw std::logic_error("TEST"); @@ -418,14 +419,17 @@ namespace { struct immovable_sender { using sender_concept = ::STDEXEC::sender_t; - template - consteval auto get_completion_signatures(const Args&...) const & noexcept { - return ::STDEXEC::completion_signatures_of_t{}; + + template + static consteval auto get_completion_signatures() noexcept { + return ::STDEXEC::completion_signatures_of_t{}; } - template + + template auto connect(Receiver r) const & noexcept { return ::STDEXEC::connect(::STDEXEC::just(), std::move(r)); } + immovable_sender() = default; immovable_sender(const immovable_sender&) { throw std::logic_error("Unexpected copy"); diff --git a/test/stdexec/algos/adaptors/test_on3.cpp b/test/stdexec/algos/adaptors/test_on3.cpp index 525062426..fd931e4e2 100644 --- a/test/stdexec/algos/adaptors/test_on3.cpp +++ b/test/stdexec/algos/adaptors/test_on3.cpp @@ -62,7 +62,7 @@ namespace { STDEXEC_EXPLICIT_THIS_END(connect) template Self, class Env> - STDEXEC_EXPLICIT_THIS_BEGIN(auto get_completion_signatures)(this Self&&, const Env&) { + static consteval auto get_completion_signatures() { return ex::__try_make_completion_signatures< ex::__copy_cvref_t, Env, @@ -70,7 +70,6 @@ namespace { ex::__mconst> >{}; } - STDEXEC_EXPLICIT_THIS_END(get_completion_signatures) }; struct probe_env_t { diff --git a/test/stdexec/algos/factories/test_read.cpp b/test/stdexec/algos/factories/test_read.cpp index 1aa3dc960..700fb16f0 100644 --- a/test/stdexec/algos/factories/test_read.cpp +++ b/test/stdexec/algos/factories/test_read.cpp @@ -16,6 +16,8 @@ #include #include +#include + namespace ex = STDEXEC; namespace { @@ -23,8 +25,10 @@ namespace { TEST_CASE("read returns empty env", "[factories][read]") { auto sndr = ex::read_env(ex::get_allocator); using Sndr = decltype(sndr); + using Env = ex::prop>; static_assert(ex::sender); static_assert(!ex::sender_in); - static_assert(ex::__is_scheduler_affine); + static_assert(ex::sender_in); + static_assert(ex::__is_scheduler_affine); } } // namespace diff --git a/test/test_common/retry.hpp b/test/test_common/retry.hpp index 143a76fb6..85e60fde7 100644 --- a/test/test_common/retry.hpp +++ b/test/test_common/retry.hpp @@ -122,17 +122,16 @@ namespace { } template - using _error = STDEXEC::completion_signatures<>; + using _swallow_errors = STDEXEC::completion_signatures<>; template - using _value = STDEXEC::completion_signatures; + using _keep_values = STDEXEC::completion_signatures; - template - auto get_completion_signatures(Env&&) const -> STDEXEC::transform_completion_signatures_of< - S&, - Env, + template Self, class... Env> + static consteval auto get_completion_signatures() -> STDEXEC::transform_completion_signatures< + STDEXEC::completion_signatures_of_t, STDEXEC::completion_signatures, - _value, - _error + _keep_values, + _swallow_errors > { return {}; } diff --git a/test/test_common/senders.hpp b/test/test_common/senders.hpp index a78f64b08..470d4c45b 100644 --- a/test/test_common/senders.hpp +++ b/test/test_common/senders.hpp @@ -78,12 +78,12 @@ namespace { }; [[nodiscard]] - auto get_attrs() const noexcept -> attrs { + auto get_env() const noexcept -> attrs { return {}; } - template - constexpr auto get_completion_signatures(const Env&...) && noexcept { + template Self> + static consteval auto get_completion_signatures() noexcept { return ex::completion_signatures< ex::set_value_t(Values...), ex::set_error_t(std::exception_ptr) From d609c9be22e341a4ad01efd2add9495caa586c30 Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Thu, 15 Jan 2026 15:57:11 -0800 Subject: [PATCH 2/4] work around GCC bug --- include/exec/sequence_senders.hpp | 4 +-- include/stdexec/__detail/__basic_sender.hpp | 25 +++++++++++++------ include/stdexec/__detail/__connect.hpp | 2 +- include/stdexec/__detail/__debug.hpp | 4 +-- include/stdexec/__detail/__meta.hpp | 6 +++++ .../stdexec/__detail/__sender_concepts.hpp | 19 +++++++------- include/stdexec/__detail/__sync_wait.hpp | 2 +- 7 files changed, 39 insertions(+), 23 deletions(-) diff --git a/include/exec/sequence_senders.hpp b/include/exec/sequence_senders.hpp index db3f3881d..644a69675 100644 --- a/include/exec/sequence_senders.hpp +++ b/include/exec/sequence_senders.hpp @@ -916,8 +916,8 @@ namespace exec { static_assert( __well_formed_item_senders<_Sequence>, STDEXEC_ERROR_GET_ITEM_TYPES_HAS_INVALID_RETURN_TYPE); - //} else { - // STDEXEC::__diagnose_sender_concept_failure<_Sequence, _Env...>(); + } else { + STDEXEC::__diagnose_sender_concept_failure, _Env...>(); } } } diff --git a/include/stdexec/__detail/__basic_sender.hpp b/include/stdexec/__detail/__basic_sender.hpp index 396a41d53..3cfc1dc3d 100644 --- a/include/stdexec/__detail/__basic_sender.hpp +++ b/include/stdexec/__detail/__basic_sender.hpp @@ -135,7 +135,8 @@ namespace STDEXEC { static_cast<_Sender&&>(__sndr), static_cast<_Receiver&&>(__rcvr)}; }; - static constexpr auto submit = []{}; + static constexpr auto submit = [] { + }; static constexpr auto start = []( __ignore, @@ -330,11 +331,18 @@ namespace STDEXEC { template concept __in_range = (_Idx < sizeof(__minvoke<_Descriptor, __q<__tuple_size_t>>)); - template - concept __has_new_get_completion_signatures = requires { - __sexpr_impl<_Tag>::template get_completion_signatures<_Self, _Env...>(); + template + concept __has_get_completion_signatures_lambda = requires { + static_cast(&__sexpr_impl<_Tag>::get_completion_signatures); }; + template + concept __has_new_get_completion_signatures = + // [WAR]: GCC hard-errors in the requires clause if + // __sexpr_impl<_Tag>::get_completion_signatures is not a function template. + STDEXEC_PP_WHEN(STDEXEC_GCC(), !__has_get_completion_signatures_lambda<_Tag> &&) + requires { __sexpr_impl<_Tag>::template get_completion_signatures<_Self, _Env...>(); }; + template concept __has_old_get_completion_signatures = requires(__declfn_t<_Self> __self) { __sexpr_impl<_Tag>::get_completion_signatures(__self(), __declval<_Env>()...); @@ -442,13 +450,17 @@ namespace STDEXEC { using __detail::__enable_receiver_from_this; + template + using __sexpr_t = __sexpr; + //! A dummy type used only for diagnostic purposes. //! See `__sexpr` for the implementation of P2300's _`basic-sender`_. - template + template struct __basic_sender { // See MAINTAINERS.md#class-template-parameters for `__id` and `__t`. using __id = __basic_sender; using __t = __basic_sender; + using __mangled = __sexpr_t<_Tag, _Data, __remangle_t<_Child>...>; }; namespace { @@ -532,9 +544,6 @@ namespace STDEXEC { __sexpr(_Tag, _Data, _Child...) -> __sexpr; } // anonymous namespace - template - using __sexpr_t = __sexpr; - STDEXEC_PRAGMA_PUSH() STDEXEC_PRAGMA_IGNORE_GNU("-Wmissing-braces") diff --git a/include/stdexec/__detail/__connect.hpp b/include/stdexec/__detail/__connect.hpp index 6ccf3ab20..c33eb4f2f 100644 --- a/include/stdexec/__detail/__connect.hpp +++ b/include/stdexec/__detail/__connect.hpp @@ -70,7 +70,7 @@ namespace STDEXEC { using __checked_signatures [[maybe_unused]] = completion_signatures_of_t<_Sender, env_of_t<_Receiver>>; } else { - __diagnose_sender_concept_failure<_Sender, env_of_t<_Receiver>>(); + __diagnose_sender_concept_failure<__demangle_t<_Sender>, env_of_t<_Receiver>>(); } return true; } diff --git a/include/stdexec/__detail/__debug.hpp b/include/stdexec/__detail/__debug.hpp index 55219fac8..b457a6d78 100644 --- a/include/stdexec/__detail/__debug.hpp +++ b/include/stdexec/__detail/__debug.hpp @@ -195,7 +195,7 @@ namespace STDEXEC { } } } else { - STDEXEC::__diagnose_sender_concept_failure<_Sender, _Env>(); + STDEXEC::__diagnose_sender_concept_failure<__demangle_t<_Sender>, _Env>(); } } } @@ -217,7 +217,7 @@ namespace STDEXEC { } } } else { - STDEXEC::__diagnose_sender_concept_failure<_Sender, _Env>(); + STDEXEC::__diagnose_sender_concept_failure<__demangle_t<_Sender>, _Env>(); } } } diff --git a/include/stdexec/__detail/__meta.hpp b/include/stdexec/__detail/__meta.hpp index 5133ae30b..e41f62efd 100644 --- a/include/stdexec/__detail/__meta.hpp +++ b/include/stdexec/__detail/__meta.hpp @@ -788,12 +788,18 @@ namespace STDEXEC { template using __demangle_t = __minvoke<__demangle_fn<_Ty>, _Ty>; + + template + using __remangle_t = __copy_cvref_t<_Sender, typename __decay_t<_Sender>::__mangled>; } // namespace __detail // A utility for pretty-printing type names in diagnostics template using __demangle_t = __copy_cvref_t<_Ty, __detail::__demangle_t>>; + template + using __remangle_t = __minvoke_or_q<__detail::__remangle_t, _Sender, _Sender>; + namespace __detail { ////////////////////////////////////////////////////////////////////////////////////////// // __get_pretty_name diff --git a/include/stdexec/__detail/__sender_concepts.hpp b/include/stdexec/__detail/__sender_concepts.hpp index 849f873eb..fd69208f0 100644 --- a/include/stdexec/__detail/__sender_concepts.hpp +++ b/include/stdexec/__detail/__sender_concepts.hpp @@ -108,20 +108,21 @@ namespace STDEXEC { // Used to report a meaningful error message when the sender_in // concept check fails. template - static constexpr auto __diagnose_sender_concept_failure() noexcept { - if constexpr (!enable_sender<__decay_t<_Sender>>) { - static_assert(enable_sender<_Sender>, STDEXEC_ERROR_ENABLE_SENDER_IS_FALSE); - } else if constexpr (!__std::move_constructible<__decay_t<_Sender>>) { + constexpr auto __diagnose_sender_concept_failure() noexcept { + using __sndr_t = __remangle_t<_Sender>; + if constexpr (!enable_sender<__decay_t<__sndr_t>>) { + static_assert(enable_sender<__sndr_t>, STDEXEC_ERROR_ENABLE_SENDER_IS_FALSE); + } else if constexpr (!__std::move_constructible<__decay_t<__sndr_t>>) { static_assert( - __std::move_constructible<__decay_t<_Sender>>, + __std::move_constructible<__decay_t<__sndr_t>>, "The sender type is not move-constructible."); - } else if constexpr (!__std::constructible_from<__decay_t<_Sender>, _Sender>) { + } else if constexpr (!__std::constructible_from<__decay_t<__sndr_t>, __sndr_t>) { static_assert( - __decay_copyable<_Sender>, + __decay_copyable<__sndr_t>, "The sender cannot be decay-copied. Did you forget a std::move?"); } else { - using _Completions = __completion_signatures_of_t<_Sender, _Env...>; - if constexpr (__same_as<_Completions, __unrecognized_sender_error<_Sender, _Env...>>) { + using _Completions = __completion_signatures_of_t<__sndr_t, _Env...>; + if constexpr (__same_as<_Completions, __unrecognized_sender_error<__sndr_t, _Env...>>) { static_assert(__mnever<_Completions>, STDEXEC_ERROR_CANNOT_COMPUTE_COMPLETION_SIGNATURES); } else if constexpr (__merror<_Completions>) { static_assert( diff --git a/include/stdexec/__detail/__sync_wait.hpp b/include/stdexec/__detail/__sync_wait.hpp index 6ac474339..8467b035e 100644 --- a/include/stdexec/__detail/__sync_wait.hpp +++ b/include/stdexec/__detail/__sync_wait.hpp @@ -237,7 +237,7 @@ namespace STDEXEC { template auto operator()(_Sender&&) const { - STDEXEC::__diagnose_sender_concept_failure<_Sender, __env>(); + STDEXEC::__diagnose_sender_concept_failure<__demangle_t<_Sender>, __env>(); // dummy return type to silence follow-on errors return std::optional>{}; } From 03db3fd777c6c93ec560d72f78b5ea69d199610b Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Thu, 15 Jan 2026 17:53:11 -0800 Subject: [PATCH 3/4] more --- include/exec/into_tuple.hpp | 9 +- include/exec/repeat_effect_until.hpp | 36 +++++--- include/exec/repeat_n.hpp | 9 +- include/exec/sequence/ignore_all_values.hpp | 10 +-- include/exec/unless_stop_requested.hpp | 17 ++-- include/stdexec/__detail/__basic_sender.hpp | 12 +-- include/stdexec/__detail/__bulk.hpp | 21 ++--- include/stdexec/__detail/__continues_on.hpp | 9 +- include/stdexec/__detail/__diagnostics.hpp | 3 + .../__detail/__get_completion_signatures.hpp | 12 +++ include/stdexec/__detail/__into_variant.hpp | 7 +- include/stdexec/__detail/__just.hpp | 10 +-- include/stdexec/__detail/__schedule_from.hpp | 11 ++- .../__detail/__stopped_as_optional.hpp | 41 +++++---- include/stdexec/__detail/__then.hpp | 14 ++- include/stdexec/__detail/__transfer_just.hpp | 1 - include/stdexec/__detail/__type_traits.hpp | 87 +------------------ include/stdexec/__detail/__upon_error.hpp | 5 +- include/stdexec/__detail/__upon_stopped.hpp | 5 +- include/stdexec/__detail/__when_all.hpp | 32 +++---- include/stdexec/__detail/__write_env.hpp | 12 +++ test/test_common/schedulers.hpp | 12 ++- 22 files changed, 174 insertions(+), 201 deletions(-) diff --git a/include/exec/into_tuple.hpp b/include/exec/into_tuple.hpp index 9257ecc26..97678323b 100644 --- a/include/exec/into_tuple.hpp +++ b/include/exec/into_tuple.hpp @@ -62,10 +62,11 @@ namespace exec { >; struct __into_tuple_impl : __sexpr_defaults { - static constexpr auto get_completion_signatures = - [](_Sender &&, _Env &&...) noexcept { - return __completions_t<__child_of<_Sender>, _Env...>{}; - }; + template + static consteval auto get_completion_signatures() { + // TODO: port this to use constant evaluation + return __completions_t<__child_of<_Sender>, _Env...>{}; + } static constexpr auto get_state = [](_Sender &&, _Receiver &) { diff --git a/include/exec/repeat_effect_until.hpp b/include/exec/repeat_effect_until.hpp index d203335b0..fc0ff2ee3 100644 --- a/include/exec/repeat_effect_until.hpp +++ b/include/exec/repeat_effect_until.hpp @@ -197,10 +197,10 @@ namespace exec { struct __repeat_effect_until_tag { }; struct __repeat_effect_until_impl : __sexpr_defaults { - static constexpr auto get_completion_signatures = - [](_Sender &&, _Env &&...) noexcept - -> __completions_t<__data_of<_Sender>, _Env...> { - return {}; + template + static consteval auto get_completion_signatures() { + // TODO: port this to use constant evaluation + return __completions_t<__data_of<_Sender>, _Env...>{}; }; static constexpr auto get_state = @@ -225,7 +225,7 @@ namespace exec { } template - auto transform_sender(STDEXEC::set_value_t, _Sender &&__sndr, __ignore) { + static auto transform_sender(STDEXEC::set_value_t, _Sender &&__sndr, __ignore) { return STDEXEC::__apply( [](__ignore, __ignore, _Child __child) { return __make_sexpr<__repeat_effect_until_tag>(std::move(__child)); @@ -254,10 +254,12 @@ namespace exec { } template - auto transform_sender(STDEXEC::set_value_t, _Sender &&__sndr, __ignore) { - return STDEXEC::__apply([](__ignore, __ignore, auto __child) { - return repeat_effect_until_t{}(STDEXEC::then(std::move(__child), _never{})); - }, static_cast<_Sender &&>(__sndr)); + static auto transform_sender(STDEXEC::set_value_t, _Sender &&__sndr, __ignore) { + return STDEXEC::__apply( + [](__ignore, __ignore, auto __child) { + return repeat_effect_until_t{}(STDEXEC::then(std::move(__child), _never{})); + }, + static_cast<_Sender &&>(__sndr)); } }; } // namespace __repeat_effect @@ -276,10 +278,16 @@ namespace STDEXEC { template <> struct __sexpr_impl : __sexpr_defaults { - static constexpr auto get_completion_signatures = - []( - _Sender &&) noexcept -> exec::__repeat_effect::__completions_t<__data_of<_Sender>> { - return {}; - }; + template + static consteval auto get_completion_signatures() { + static_assert(sender_expr_for<_Sender, exec::repeat_effect_until_t>); + using __sndr_t = __detail::__transform_sender_result_t< + exec::repeat_effect_until_t, + set_value_t, + _Sender, + env<> + >; + return STDEXEC::get_completion_signatures<__sndr_t, _Env...>(); + } }; } // namespace STDEXEC diff --git a/include/exec/repeat_n.hpp b/include/exec/repeat_n.hpp index 688e39079..7589a6c5c 100644 --- a/include/exec/repeat_n.hpp +++ b/include/exec/repeat_n.hpp @@ -180,10 +180,11 @@ namespace exec { struct __repeat_n_tag { }; struct __repeat_n_impl : __sexpr_defaults { - static constexpr auto get_completion_signatures = - [](_Sender &&, _Env &&...) noexcept { - return __completions_t<__data_of<_Sender>, _Env...>{}; - }; + template + static consteval auto get_completion_signatures() { + // TODO: port this to use constant evaluation + return __completions_t<__data_of<_Sender>, _Env...>{}; + } static constexpr auto get_state = [](_Sender &&__sndr, _Receiver &__rcvr) { diff --git a/include/exec/sequence/ignore_all_values.hpp b/include/exec/sequence/ignore_all_values.hpp index d518472e3..0e6a3ce0f 100644 --- a/include/exec/sequence/ignore_all_values.hpp +++ b/include/exec/sequence/ignore_all_values.hpp @@ -317,12 +317,12 @@ namespace exec { template using __completion_sigs = __sequence_completion_signatures_of_t<_Sequence, _Env...>; - static constexpr auto get_completion_signatures = - [](_Sender&&, _Env&&...) - -> __completion_sigs<__child_of<_Sender>, _Env...> { + template + static consteval auto get_completion_signatures() { static_assert(sender_expr_for<_Sender, ignore_all_values_t>); - return {}; - }; + // TODO: port this to use constant evaluation + return __completion_sigs<__child_of<_Sender>, _Env...>{}; + } template using _ResultVariant = __result_variant_t<_Child, env_of_t<_Receiver>>; diff --git a/include/exec/unless_stop_requested.hpp b/include/exec/unless_stop_requested.hpp index 0c463e082..00d258084 100644 --- a/include/exec/unless_stop_requested.hpp +++ b/include/exec/unless_stop_requested.hpp @@ -72,17 +72,18 @@ namespace exec { }; struct __unless_stop_requested_impl : __sexpr_defaults { - static constexpr auto get_completion_signatures = - [](_Self&&, _Env&&) noexcept - -> __completions<__child_of<_Self>, _Env> { + template + static consteval auto get_completion_signatures() { static_assert(sender_expr_for<_Self, unless_stop_requested_t>); - return {}; + // TODO: port this to use constant evaluation + return __completions<__child_of<_Self>, _Env>{}; }; - static constexpr auto start = []( - _State&, - _Receiver& __rcvr, - _Operation& __child_op) noexcept -> void { + static constexpr auto start = // + []( + _State&, + _Receiver& __rcvr, + _Operation& __child_op) noexcept -> void { static_assert(!__unstoppable_receiver<_Receiver>); if (get_stop_token(STDEXEC::get_env(__rcvr)).stop_requested()) { STDEXEC::set_stopped((_Receiver&&) __rcvr); diff --git a/include/stdexec/__detail/__basic_sender.hpp b/include/stdexec/__detail/__basic_sender.hpp index 3cfc1dc3d..e25f8a504 100644 --- a/include/stdexec/__detail/__basic_sender.hpp +++ b/include/stdexec/__detail/__basic_sender.hpp @@ -156,12 +156,12 @@ namespace STDEXEC { _SetTag()(std::move(__rcvr), static_cast<_Args&&>(__args)...); }; - static constexpr auto get_completion_signatures = - [](_Sender&&, auto&&...) noexcept { - static_assert( - __mnever>, - "No customization of get_completion_signatures for this sender tag type."); - }; + template + static consteval auto get_completion_signatures() { + static_assert( + __mnever>, + "No customization of get_completion_signatures for this sender tag type."); + } }; template diff --git a/include/stdexec/__detail/__bulk.hpp b/include/stdexec/__detail/__bulk.hpp index d639d57b1..e90271b74 100644 --- a/include/stdexec/__detail/__bulk.hpp +++ b/include/stdexec/__detail/__bulk.hpp @@ -273,16 +273,17 @@ namespace STDEXEC { return __fwd_env(STDEXEC::get_env(__child)); }; - static constexpr auto get_completion_signatures = - [](_Sender&&, _Env&&...) noexcept -> __completion_signatures< - _AlgoTag, - __fun_t<_Sender>, - __shape_t<_Sender>, - __child_of<_Sender>, - _Env... - > { - static_assert(sender_expr_for<_Sender, bulk_t>); - return {}; + template + static consteval auto get_completion_signatures() { + static_assert(sender_expr_for<_Sender, _AlgoTag>); + // TODO: port this to use constant evaluation + return __completion_signatures< + _AlgoTag, + __fun_t<_Sender>, + __shape_t<_Sender>, + __child_of<_Sender>, + _Env... + >{}; }; }; diff --git a/include/stdexec/__detail/__continues_on.hpp b/include/stdexec/__detail/__continues_on.hpp index 625f790d3..64ecd8559 100644 --- a/include/stdexec/__detail/__continues_on.hpp +++ b/include/stdexec/__detail/__continues_on.hpp @@ -333,12 +333,13 @@ namespace STDEXEC { return __attrs{__data, __child}; }; - static constexpr auto get_completion_signatures = - [](_Sender&&, _Env&&...) noexcept - -> __completions_t<__scheduler_t<_Sender, _Env...>, __child_of<_Sender>, _Env...> { + template + static consteval auto get_completion_signatures() // + -> __completions_t<__scheduler_t<_Sender, _Env...>, __child_of<_Sender>, _Env...> { static_assert(sender_expr_for<_Sender, continues_on_t>); + // TODO: update this to use constant evaluation: return {}; - }; + } static constexpr auto get_state = [](_Sender&& __sndr, _Receiver& __rcvr) diff --git a/include/stdexec/__detail/__diagnostics.hpp b/include/stdexec/__detail/__diagnostics.hpp index a335a4fd9..3360fa167 100644 --- a/include/stdexec/__detail/__diagnostics.hpp +++ b/include/stdexec/__detail/__diagnostics.hpp @@ -205,6 +205,9 @@ namespace STDEXEC { STDEXEC_ATTRIBUTE(host, device) auto operator,(_ERROR_&) -> _ERROR_&; + using __t = _ERROR_; + using __id = _ERROR_; + using __partitioned = _ERROR_; template diff --git a/include/stdexec/__detail/__get_completion_signatures.hpp b/include/stdexec/__detail/__get_completion_signatures.hpp index f2d767066..0d64113b6 100644 --- a/include/stdexec/__detail/__get_completion_signatures.hpp +++ b/include/stdexec/__detail/__get_completion_signatures.hpp @@ -21,6 +21,7 @@ #include "__awaitable.hpp" #include "__completion_signatures.hpp" // IWYU pragma: export #include "__diagnostics.hpp" +#include "__env.hpp" #include "__meta.hpp" #include "__tag_invoke.hpp" #include "__tuple.hpp" // IWYU pragma: keep for __tuple @@ -274,6 +275,17 @@ namespace STDEXEC { using __completion_signatures_of_t = decltype(STDEXEC::get_completion_signatures<_Sender, _Env...>()); + /////////////////////////////////////////////////////////////////////////////////////////////////// + // __get_child_completion_signatures + template + [[nodiscard]] + consteval auto __get_child_completion_signatures() { + return STDEXEC::get_completion_signatures< + __copy_cvref_t<_Parent, _Child>, + __fwd_env_t<_Env>... + >(); + } + #if STDEXEC_NO_STD_CONSTEXPR_EXCEPTIONS() template concept __is_dependent_sender = diff --git a/include/stdexec/__detail/__into_variant.hpp b/include/stdexec/__detail/__into_variant.hpp index 9e67fb19d..fe412dba6 100644 --- a/include/stdexec/__detail/__into_variant.hpp +++ b/include/stdexec/__detail/__into_variant.hpp @@ -97,11 +97,10 @@ namespace STDEXEC { } }; - static constexpr auto get_completion_signatures = - [](_Self&&, _Env&&...) noexcept - -> __completions<__child_of<_Self>, _Env...> { + template + static consteval auto get_completion_signatures() { static_assert(sender_expr_for<_Self, into_variant_t>); - return {}; + return __completions<__child_of<_Self>, _Env...>{}; }; }; } // namespace __into_variant diff --git a/include/stdexec/__detail/__just.hpp b/include/stdexec/__detail/__just.hpp index ac42e443f..4c78d65ca 100644 --- a/include/stdexec/__detail/__just.hpp +++ b/include/stdexec/__detail/__just.hpp @@ -40,11 +40,11 @@ namespace STDEXEC { return {}; }; - static constexpr auto get_completion_signatures = - [](_Sender&&, auto&&...) noexcept { - static_assert(sender_expr_for<_Sender, _JustTag>); - return completion_signatures<__mapply<__qf<__tag_t>, __decay_t<__data_of<_Sender>>>>{}; - }; + template + static consteval auto get_completion_signatures() { + static_assert(sender_expr_for<_Sender, _JustTag>); + return completion_signatures<__mapply<__qf<__tag_t>, __decay_t<__data_of<_Sender>>>>{}; + } static constexpr auto start = [](_State& __state, _Receiver& __rcvr) noexcept -> void { diff --git a/include/stdexec/__detail/__schedule_from.hpp b/include/stdexec/__detail/__schedule_from.hpp index 9c96df1a5..9dd59523d 100644 --- a/include/stdexec/__detail/__schedule_from.hpp +++ b/include/stdexec/__detail/__schedule_from.hpp @@ -21,7 +21,6 @@ #include "__basic_sender.hpp" #include "__completion_signatures_of.hpp" #include "__sender_introspection.hpp" -#include "__type_traits.hpp" namespace STDEXEC { ///////////////////////////////////////////////////////////////////////////// @@ -35,11 +34,11 @@ namespace STDEXEC { }; struct __schedule_from_impl : __sexpr_defaults { - static constexpr auto get_completion_signatures = - [](_Sender&&, _Env&&...) noexcept - -> __completion_signatures_of_t<__copy_cvref_t<_Sender, __child_of<_Sender>>, _Env...> { - return {}; - }; + template + static consteval auto get_completion_signatures() { + static_assert(sender_expr_for<_Sender, schedule_from_t>); + return STDEXEC::get_completion_signatures<__child_of<_Sender>, _Env...>(); + } }; } // namespace __schfr diff --git a/include/stdexec/__detail/__stopped_as_optional.hpp b/include/stdexec/__detail/__stopped_as_optional.hpp index 60e9e8af8..b9cefdf78 100644 --- a/include/stdexec/__detail/__stopped_as_optional.hpp +++ b/include/stdexec/__detail/__stopped_as_optional.hpp @@ -58,28 +58,27 @@ namespace STDEXEC { template using __set_error_t = completion_signatures; - static constexpr auto get_completion_signatures = - [](_Self&&, _Env&&...) noexcept - requires __mvalid<__completion_signatures_of_t, __child_of<_Self>, _Env...> - { + template + static constexpr auto get_completion_signatures() { static_assert(sender_expr_for<_Self, stopped_as_optional_t>); - using _Completions = __completion_signatures_of_t<__child_of<_Self>, _Env...>; - if constexpr (!__valid_completion_signatures<_Completions>) { - return _Completions(); - } else if constexpr (__single_value_sender<__child_of<_Self>, _Env...>) { - return transform_completion_signatures< - _Completions, - completion_signatures, - __set_value_t, - __set_error_t, - completion_signatures<> - >(); - } else { - return _ERROR_< - _WHAT_<>(_SENDER_MUST_HAVE_EXACTLY_ONE_VALUE_COMPLETION_WITH_ONE_ARGUMENT_), - _IN_ALGORITHM_(stopped_as_optional_t), - _WITH_SENDER_<__child_of<_Self>> - >(); + STDEXEC_COMPLSIGS_LET( + __completions, STDEXEC::get_completion_signatures<__child_of<_Self>, _Env...>()) { + using _Completions = decltype(__completions); + if constexpr (__single_value_sender<__child_of<_Self>, _Env...>) { + return transform_completion_signatures< + _Completions, + completion_signatures, + __set_value_t, + __set_error_t, + completion_signatures<> + >(); + } else { + return STDEXEC::__invalid_completion_signature< + _WHAT_<>(_SENDER_MUST_HAVE_EXACTLY_ONE_VALUE_COMPLETION_WITH_ONE_ARGUMENT_), + _IN_ALGORITHM_(stopped_as_optional_t), + _WITH_SENDER_<__child_of<_Self>> + >(); + } } }; diff --git a/include/stdexec/__detail/__then.hpp b/include/stdexec/__detail/__then.hpp index bb3090414..5d88ee6c4 100644 --- a/include/stdexec/__detail/__then.hpp +++ b/include/stdexec/__detail/__then.hpp @@ -59,13 +59,21 @@ namespace STDEXEC { return __sync_attrs{__child}; }; - static constexpr auto get_completion_signatures = - [](_Sender&&, _Env&&...) noexcept - -> __completions_t<__decay_t<__data_of<_Sender>>, __child_of<_Sender>, _Env...> { + template + static consteval auto get_completion_signatures() // + -> __completions_t<__decay_t<__data_of<_Sender>>, __child_of<_Sender>, _Env...> { static_assert(sender_expr_for<_Sender, then_t>); + // TODO: update this to use constant evaluation: return {}; }; + // template + // static consteval auto get_completion_signatures() // + // -> __completions_t<__decay_t<__data_of<_Sender>>, __child_of<_Sender>, _Env...> { + // static_assert(sender_expr_for<_Sender, then_t>); + // return {}; + // } + struct __complete_fn { template STDEXEC_ATTRIBUTE(host, device) diff --git a/include/stdexec/__detail/__transfer_just.hpp b/include/stdexec/__detail/__transfer_just.hpp index 21a7d5de9..ad3bc120b 100644 --- a/include/stdexec/__detail/__transfer_just.hpp +++ b/include/stdexec/__detail/__transfer_just.hpp @@ -23,7 +23,6 @@ #include "__continues_on.hpp" #include "__just.hpp" #include "__schedulers.hpp" -#include "__sender_introspection.hpp" #include "__tuple.hpp" STDEXEC_PRAGMA_PUSH() diff --git a/include/stdexec/__detail/__type_traits.hpp b/include/stdexec/__detail/__type_traits.hpp index a9a6a27ad..5f03ed068 100644 --- a/include/stdexec/__detail/__type_traits.hpp +++ b/include/stdexec/__detail/__type_traits.hpp @@ -52,7 +52,6 @@ namespace STDEXEC { ////////////////////////////////////////////////////////////////////////////////////////////////// // __decay_t: An efficient implementation for std::decay #if STDEXEC_HAS_BUILTIN(__decay) && (!STDEXEC_CLANG() || STDEXEC_CLANG_VERSION >= 21'00) - namespace __tt { template struct __wrap; @@ -65,92 +64,10 @@ namespace STDEXEC { } // namespace __tt template - using __decay_t = __tt::__decay_ *) == ~0ul>::template __f<_Ty>; - -#elif STDEXEC_EDG() - - template - using __decay_t = std::decay_t<_Ty>; - + using __decay_t = __tt::__decay_))>::template __f<_Ty>; #else - - namespace __tt { - struct __decay_object { - template - static _Ty __g(_Ty const &); - template - using __f = decltype(__g(__declval<_Ty>())); - }; - - struct __decay_default { - template - static _Ty __g(_Ty); - template - using __f = decltype(__g(__declval<_Ty>())); - }; - - struct __decay_abominable { - template - using __f = _Ty; - }; - - struct __decay_void { - template - using __f = void; - }; - - template - extern __decay_object __mdecay; - - template - extern __decay_default __mdecay<_Ty(Us...)>; - - template - extern __decay_default __mdecay<_Ty(Us...) noexcept>; - - template - extern __decay_default __mdecay<_Ty (&)(Us...)>; - - template - extern __decay_default __mdecay<_Ty (&)(Us...) noexcept>; - - template - extern __decay_abominable __mdecay<_Ty(Us...) const>; - - template - extern __decay_abominable __mdecay<_Ty(Us...) const noexcept>; - - template - extern __decay_abominable __mdecay<_Ty(Us...) const &>; - - template - extern __decay_abominable __mdecay<_Ty(Us...) const & noexcept>; - - template - extern __decay_abominable __mdecay<_Ty(Us...) const &&>; - - template - extern __decay_abominable __mdecay<_Ty(Us...) const && noexcept>; - - template - extern __decay_default __mdecay<_Ty[]>; - - template - extern __decay_default __mdecay<_Ty[N]>; - - template - extern __decay_default __mdecay<_Ty (&)[N]>; - - template <> - inline __decay_void __mdecay; - - template <> - inline __decay_void __mdecay; - } // namespace __tt - template - using __decay_t = decltype(__tt::__mdecay<_Ty>)::template __f<_Ty>; - + using __decay_t = std::decay_t<_Ty>; #endif ////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/stdexec/__detail/__upon_error.hpp b/include/stdexec/__detail/__upon_error.hpp index 81df1fe27..d556a2b37 100644 --- a/include/stdexec/__detail/__upon_error.hpp +++ b/include/stdexec/__detail/__upon_error.hpp @@ -58,10 +58,11 @@ namespace STDEXEC { }; struct __upon_error_impl : __sexpr_defaults { - static constexpr auto get_completion_signatures = - [](_Sender&&, _Env&&...) noexcept + template + static consteval auto get_completion_signatures() // -> __completion_signatures_t<__decay_t<__data_of<_Sender>>, __child_of<_Sender>, _Env...> { static_assert(sender_expr_for<_Sender, upon_error_t>); + // TODO: update this to use constant evaluation: return {}; }; diff --git a/include/stdexec/__detail/__upon_stopped.hpp b/include/stdexec/__detail/__upon_stopped.hpp index cd7ecec5a..ff459233a 100644 --- a/include/stdexec/__detail/__upon_stopped.hpp +++ b/include/stdexec/__detail/__upon_stopped.hpp @@ -61,10 +61,11 @@ namespace STDEXEC { }; struct __upon_stopped_impl : __sexpr_defaults { - static constexpr auto get_completion_signatures = - [](_Sender&&, _Env&&...) noexcept + template + static consteval auto get_completion_signatures() // -> __completion_signatures_t<__decay_t<__data_of<_Sender>>, __child_of<_Sender>, _Env...> { static_assert(sender_expr_for<_Sender, upon_stopped_t>); + // TODO: update this to use constant evaluation: return {}; }; diff --git a/include/stdexec/__detail/__when_all.hpp b/include/stdexec/__detail/__when_all.hpp index 7d319a7dd..2e5c77263 100644 --- a/include/stdexec/__detail/__when_all.hpp +++ b/include/stdexec/__detail/__when_all.hpp @@ -336,17 +336,6 @@ namespace STDEXEC { }; struct __when_all_impl : __sexpr_defaults { - template - using __error_t = std::conditional_t< - sizeof...(_Env) == 0, - __dependent_sender_error<_Self>, - __mexception< - _INVALID_ARGUMENTS_TO_WHEN_ALL_, - __children_of<_Self, __qq<_WITH_SENDERS_>>, - _WITH_ENVIRONMENT_<_Env>... - > - >; - template using __completions_t = __children_of<_Self, __when_all::__completions_t<__env_t<_Env>...>>; @@ -354,11 +343,22 @@ namespace STDEXEC { return __when_all::__attrs<_Child...>{}; }; - static constexpr auto get_completion_signatures = - [](_Self&&, _Env&&...) noexcept { - static_assert(sender_expr_for<_Self, when_all_t>); - return __minvoke<__mtry_catch<__q<__completions_t>, __q<__error_t>>, _Self, _Env...>(); - }; + template + static consteval auto get_completion_signatures() { + static_assert(sender_expr_for<_Self, when_all_t>); + if constexpr (__mvalid<__completions_t, _Self, _Env...>) { + // TODO: update this to use constant evaluation: + return __completions_t<_Self, _Env...>{}; + } else if constexpr (sizeof...(_Env) == 0) { + return STDEXEC::__dependent_sender_error<_Self>(); + } else { + return STDEXEC::__invalid_completion_signature< + _INVALID_ARGUMENTS_TO_WHEN_ALL_, + __children_of<_Self, __qq<_WITH_SENDERS_>>, + _WITH_ENVIRONMENT_<_Env>... + >(); + } + } static constexpr auto get_env = []( diff --git a/include/stdexec/__detail/__write_env.hpp b/include/stdexec/__detail/__write_env.hpp index f36b9c830..949589981 100644 --- a/include/stdexec/__detail/__write_env.hpp +++ b/include/stdexec/__detail/__write_env.hpp @@ -61,6 +61,18 @@ namespace STDEXEC { static_assert(sender_expr_for<_Self, write_env_t>); return {}; }; + + // TODO: figure out why this doesn't work + // template + // static consteval auto get_completion_signatures() + // -> __completion_signatures_of_t< + // __child_of<_Self>, + // __meval<__join_env_t, const __decay_t<__data_of<_Self>>&, _Env>... + // > { + // static_assert(sender_expr_for<_Self, write_env_t>); + // // TODO: update this to use constant evaluation: + // return {}; + // } }; } // namespace __write diff --git a/test/test_common/schedulers.hpp b/test/test_common/schedulers.hpp index 35177a330..96fb06984 100644 --- a/test/test_common/schedulers.hpp +++ b/test/test_common/schedulers.hpp @@ -351,15 +351,25 @@ namespace { //! Scheduler that returns a sender that always completes with cancellation. struct stopped_scheduler { + private: + struct sender; + + public: using __id = stopped_scheduler; using __t = stopped_scheduler; + auto operator==(const stopped_scheduler&) const noexcept -> bool = default; + [[nodiscard]] auto schedule() const noexcept { return sender{}; } - auto operator==(const stopped_scheduler&) const noexcept -> bool = default; + template Tag> + [[nodiscard]] + auto query(ex::get_completion_scheduler_t) const noexcept { + return stopped_scheduler{}; + } private: template From 9600c31c47f9b8a0154bce2ec139d6dd0bdb9380 Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Fri, 16 Jan 2026 14:29:39 -0800 Subject: [PATCH 4/4] still more --- include/stdexec/__detail/__basic_sender.hpp | 35 +++++---------------- include/stdexec/__detail/__write_env.hpp | 25 ++++----------- 2 files changed, 14 insertions(+), 46 deletions(-) diff --git a/include/stdexec/__detail/__basic_sender.hpp b/include/stdexec/__detail/__basic_sender.hpp index e25f8a504..33a9e66d1 100644 --- a/include/stdexec/__detail/__basic_sender.hpp +++ b/include/stdexec/__detail/__basic_sender.hpp @@ -172,8 +172,6 @@ namespace STDEXEC { template struct __receiver_box { - _Receiver __rcvr_; - STDEXEC_ATTRIBUTE(always_inline) auto __rcvr() & noexcept -> _Receiver& { return this->__rcvr_; } @@ -181,6 +179,8 @@ namespace STDEXEC { STDEXEC_ATTRIBUTE(always_inline) auto __rcvr() const & noexcept -> const _Receiver& { return this->__rcvr_; } + + _Receiver __rcvr_; }; template @@ -188,7 +188,7 @@ namespace STDEXEC { using __tag_t = __decay_t<_Sexpr>::__tag_t; using __state_t = __state_type_t<__tag_t, _Sexpr, _Receiver>; - __state_box(_Sexpr&& __sndr, _Receiver& __rcvr) + explicit __state_box(_Sexpr&& __sndr, _Receiver& __rcvr) noexcept(__noexcept_of<__sexpr_impl<__tag_t>::get_state, _Sexpr, _Receiver&>) { ::new (static_cast(__buf_)) auto( __sexpr_impl<__tag_t>::get_state(static_cast<_Sexpr&&>(__sndr), __rcvr)); @@ -325,27 +325,9 @@ namespace STDEXEC { }; }; - template - using __tuple_size_t = char[sizeof...(_Child) + 2]; // NOLINT(modernize-avoid-c-arrays) - - template - concept __in_range = (_Idx < sizeof(__minvoke<_Descriptor, __q<__tuple_size_t>>)); - - template - concept __has_get_completion_signatures_lambda = requires { - static_cast(&__sexpr_impl<_Tag>::get_completion_signatures); - }; - template - concept __has_new_get_completion_signatures = - // [WAR]: GCC hard-errors in the requires clause if - // __sexpr_impl<_Tag>::get_completion_signatures is not a function template. - STDEXEC_PP_WHEN(STDEXEC_GCC(), !__has_get_completion_signatures_lambda<_Tag> &&) - requires { __sexpr_impl<_Tag>::template get_completion_signatures<_Self, _Env...>(); }; - - template - concept __has_old_get_completion_signatures = requires(__declfn_t<_Self> __self) { - __sexpr_impl<_Tag>::get_completion_signatures(__self(), __declval<_Env>()...); + concept __has_get_completion_signatures = requires { + __sexpr_impl<_Tag>::template get_completion_signatures<_Self, _Env...>(); }; } // namespace __detail @@ -486,12 +468,10 @@ namespace STDEXEC { template static consteval auto get_completion_signatures() { using namespace __detail; - if constexpr (__has_new_get_completion_signatures<__tag_t, _Self, _Env...>) { + if constexpr (__has_get_completion_signatures<__tag_t, _Self, _Env...>) { return __sexpr_impl<__tag_t>::template get_completion_signatures<_Self, _Env...>(); - } else if constexpr (__has_new_get_completion_signatures<__tag_t, _Self>) { + } else if constexpr (__has_get_completion_signatures<__tag_t, _Self>) { return __sexpr_impl<__tag_t>::template get_completion_signatures<_Self>(); - } else if constexpr (__has_old_get_completion_signatures<__tag_t, _Self, _Env...>) { - return __result_of<__sexpr_impl<__tag_t>::get_completion_signatures, _Self, _Env...>(); } else if constexpr (sizeof...(_Env) == 0) { return __dependent_sender<_Self>(); } else { @@ -528,6 +508,7 @@ namespace STDEXEC { return __sexpr_impl<__tag_t>::connect(*this, static_cast<_Receiver&&>(__rcvr)); } + // Non-standard extension: template STDEXEC_ATTRIBUTE(nodiscard, always_inline) static constexpr auto submit(_Self&& __self, _Receiver __rcvr) diff --git a/include/stdexec/__detail/__write_env.hpp b/include/stdexec/__detail/__write_env.hpp index 949589981..ccdc8857e 100644 --- a/include/stdexec/__detail/__write_env.hpp +++ b/include/stdexec/__detail/__write_env.hpp @@ -52,27 +52,14 @@ namespace STDEXEC { return __env::__join(__state, STDEXEC::get_env(__rcvr)); }; - static constexpr auto get_completion_signatures = - [](_Self&&, _Env&&...) noexcept - -> __completion_signatures_of_t< + template + static consteval auto get_completion_signatures() { + static_assert(sender_expr_for<_Self, write_env_t>); + return STDEXEC::get_completion_signatures< __child_of<_Self>, __meval<__join_env_t, const __decay_t<__data_of<_Self>>&, _Env>... - > { - static_assert(sender_expr_for<_Self, write_env_t>); - return {}; - }; - - // TODO: figure out why this doesn't work - // template - // static consteval auto get_completion_signatures() - // -> __completion_signatures_of_t< - // __child_of<_Self>, - // __meval<__join_env_t, const __decay_t<__data_of<_Self>>&, _Env>... - // > { - // static_assert(sender_expr_for<_Self, write_env_t>); - // // TODO: update this to use constant evaluation: - // return {}; - // } + >(); + } }; } // namespace __write