From c9ec7ed77cac659810c1a783ad70c059a8702640 Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Mon, 12 Oct 2026 20:08:38 +0000 Subject: [PATCH 1/2] port `into_tuple`'s cmplsigs computation to consteval --- include/exec/into_tuple.hpp | 133 ++++++++++++++++------------------ test/exec/test_into_tuple.cpp | 25 +++++-- 2 files changed, 79 insertions(+), 79 deletions(-) diff --git a/include/exec/into_tuple.hpp b/include/exec/into_tuple.hpp index 5efdf2ef9..3d51f6d98 100644 --- a/include/exec/into_tuple.hpp +++ b/include/exec/into_tuple.hpp @@ -15,116 +15,107 @@ */ #pragma once -#include "../stdexec/__detail/__basic_sender.hpp" -#include "../stdexec/__detail/__meta.hpp" #include "../stdexec/execution.hpp" +#include "completion_signatures.hpp" namespace experimental::execution { - namespace __into_tuple + namespace _into_tuple { using namespace STDEXEC; struct into_tuple_t; - struct _THE_INPUT_SENDER_MUST_HAVE_AT_EXACTLY_ONE_POSSIBLE_VALUE_COMPLETION_ - {}; + struct THE_INPUT_SENDER_MUST_HAVE_AT_EXACTLY_ONE_POSSIBLE_VALUE_COMPLETION; - template - using __too_many_completions_error = - __mexception<_WHAT_(_INVALID_ARGUMENT_), - _WHY_(_THE_INPUT_SENDER_MUST_HAVE_AT_EXACTLY_ONE_POSSIBLE_VALUE_COMPLETION_), - _WHERE_(_IN_ALGORITHM_, into_tuple_t), - _WITH_SENDER_<_Sender>, - _WITH_ENVIRONMENT_(_Env)>; - - template - using __try_result_tuple_t = __value_types_t<__completion_signatures_of_t<_Sender, _Env...>, - __q<__decayed_std_tuple>, - __q<__msingle>>; - - template - using __result_tuple_t = - __minvoke<__mtry_catch_q<__try_result_tuple_t, __q<__too_many_completions_error>>, - _Sender, - _Env...>; - - template - using __tuple_completions_t = - STDEXEC::completion_signatures; - - template - using __completions_t = __transform_completion_signatures_t< - __completion_signatures_of_t<_Sender, _Env...>, - __minvoke_q<__tuple_completions_t, __result_tuple_t<_Sender, _Env...>>, - __mconst>::__f>; - - struct __into_tuple_impl : __sexpr_defaults + struct _impl : __sexpr_defaults { - template + template struct __state { - using __tuple_t = _Tuple; - STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS - _Receiver __rcvr_; + Receiver __rcvr_; }; - template + static constexpr auto _value_cmplsigs = []() noexcept + { + constexpr bool __is_nothrow = __nothrow_decay_copyable; + return exec::concat_completion_signatures( + STDEXEC::completion_signatures)>(), + __eptr_completion_unless_t<__mbool<__is_nothrow>>()); + }; + + template static consteval auto __get_completion_signatures() { - // TODO: port this to use constant evaluation - return __completions_t<__child_of<_Sender>, _Env...>{}; + auto completions = STDEXEC::get_completion_signatures<__child_of, Env...>(); + STDEXEC_IF_OK(completions) + { + if constexpr (completions.__count(set_value) != 1) + { + return exec::throw_compile_time_error< + _WHAT_(_INVALID_ARGUMENT_), + _WHY_(THE_INPUT_SENDER_MUST_HAVE_AT_EXACTLY_ONE_POSSIBLE_VALUE_COMPLETION), + _WHERE_(_IN_ALGORITHM_, into_tuple_t), + _WITH_SENDER_, + _WITH_ENVIRONMENT_(Env)...>(); + } + else + { + return exec::transform_completion_signatures(completions, _value_cmplsigs); + } + } } - static constexpr auto __get_state = - [](_Sender &&, _Receiver &&__rcvr) + static constexpr auto __get_state = [](__ignore, Receiver &&__rcvr) noexcept { - using __tuple_t = __result_tuple_t<__child_of<_Sender>, env_of_t<_Receiver>>; - return __state<_Receiver, __tuple_t>{static_cast<_Receiver &&>(__rcvr)}; + return __state{static_cast(__rcvr)}; }; static constexpr auto __complete = - [](__ignore, - _State &__state, - _Tag, - _Args &&...__args) noexcept -> void + [](__ignore, + State &__state, + Tag, + Args &&...__args) noexcept -> void { - if constexpr (__std::same_as<_Tag, set_value_t>) + if constexpr (Tag() == set_value) { - using __tuple_t = _State::__tuple_t; STDEXEC_TRY { - set_value(static_cast<_State &&>(__state).__rcvr_, - __tuple_t{static_cast<_Args &&>(__args)...}); + set_value(static_cast(__state).__rcvr_, + __decayed_std_tuple{static_cast(__args)...}); } STDEXEC_CATCH_ALL { - STDEXEC::set_error(static_cast<_State &&>(__state).__rcvr_, std::current_exception()); + if constexpr (__nothrow_decay_copyable) + __std::unreachable(); + else + set_error(static_cast(__state).__rcvr_, std::current_exception()); } } else { - _Tag()(static_cast<_State &&>(__state).__rcvr_, static_cast<_Args &&>(__args)...); + Tag()(static_cast(__state).__rcvr_, static_cast(__args)...); } }; }; + } // namespace _into_tuple - struct into_tuple_t + struct into_tuple_t + { + template + constexpr auto operator()(Sender &&__sndr) const // + noexcept(STDEXEC::__nothrow_decay_copyable) { - template - constexpr auto operator()(_Sender &&__sndr) const - { - return __sexpr{into_tuple_t(), __(), static_cast<_Sender &&>(__sndr)}; - } + using namespace STDEXEC; + return __sexpr{into_tuple_t(), __(), static_cast(__sndr)}; + } - STDEXEC_ATTRIBUTE(always_inline) - constexpr auto operator()() const noexcept - { - return __closure(*this); - } - }; - } // namespace __into_tuple + STDEXEC_ATTRIBUTE(always_inline) + constexpr auto operator()() const noexcept + { + return STDEXEC::__closure(*this); + } + }; - using __into_tuple::into_tuple_t; inline constexpr into_tuple_t into_tuple{}; } // namespace experimental::execution @@ -133,6 +124,6 @@ namespace exec = experimental::execution; namespace STDEXEC { template <> - struct __sexpr_impl : exec::__into_tuple::__into_tuple_impl + struct __sexpr_impl : exec::_into_tuple::_impl {}; } // namespace STDEXEC diff --git a/test/exec/test_into_tuple.cpp b/test/exec/test_into_tuple.cpp index 01bfd1046..5b1ea0f49 100644 --- a/test/exec/test_into_tuple.cpp +++ b/test/exec/test_into_tuple.cpp @@ -24,6 +24,11 @@ using namespace STDEXEC; namespace { + struct throwing_move_only + { + throwing_move_only() = default; + throwing_move_only(throwing_move_only &&) {} + }; TEST_CASE("into_tuple is a sender", "[adaptors][into_tuple]") { @@ -35,17 +40,21 @@ namespace { using void_sender = result_of_t>; STATIC_REQUIRE(sender_in>); - STATIC_REQUIRE( - set_equivalent< - completion_signatures_of_t>, - completion_signatures)>>); + STATIC_REQUIRE(set_equivalent>, + completion_signatures)>>); + + using ints_sender = result_of_t>; + STATIC_REQUIRE(sender_in>); + STATIC_REQUIRE(set_equivalent>, + completion_signatures)>>); - using ints_sender = result_of_t>; + using throwing_move_only_sender = + result_of_t>; STATIC_REQUIRE(sender_in>); STATIC_REQUIRE( - set_equivalent< - completion_signatures_of_t>, - completion_signatures)>>); + set_equivalent>, + completion_signatures)>>); } TEST_CASE("into_tuple rejects senders unless they have a single value completion", From 5b058693197149a745c561e3a71e1596f0de1f0f Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Mon, 12 Oct 2026 22:36:15 +0000 Subject: [PATCH 2/2] try to avoid nvc++ parsing error with function types and variadics --- include/exec/into_tuple.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/exec/into_tuple.hpp b/include/exec/into_tuple.hpp index 3d51f6d98..b3fae7b02 100644 --- a/include/exec/into_tuple.hpp +++ b/include/exec/into_tuple.hpp @@ -56,7 +56,7 @@ namespace experimental::execution _WHY_(THE_INPUT_SENDER_MUST_HAVE_AT_EXACTLY_ONE_POSSIBLE_VALUE_COMPLETION), _WHERE_(_IN_ALGORITHM_, into_tuple_t), _WITH_SENDER_, - _WITH_ENVIRONMENT_(Env)...>(); + __fn_t<_WITH_ENVIRONMENT_, Env>...>(); } else {