diff --git a/include/exec/just_from.hpp b/include/exec/just_from.hpp index 3e9b1c3c6..2bebc3569 100644 --- a/include/exec/just_from.hpp +++ b/include/exec/just_from.hpp @@ -57,7 +57,7 @@ namespace experimental::execution auto operator()(Ts &&...ts) const noexcept -> _error_t; template - requires STDEXEC::__cmplsigs::__is_compl_sig<_set_tag_t(Ts...)> + requires STDEXEC::__completion_signature<_set_tag_t(Ts...)> auto operator()(Ts &&...) const noexcept -> STDEXEC::completion_signatures<_set_tag_t(Ts...)> { return {}; diff --git a/include/exec/sequence/merge_each.hpp b/include/exec/sequence/merge_each.hpp index bf51da376..bf9100b8c 100644 --- a/include/exec/sequence/merge_each.hpp +++ b/include/exec/sequence/merge_each.hpp @@ -28,6 +28,7 @@ #include "../../stdexec/__detail/__config.hpp" #include "../../stdexec/__detail/__diagnostics.hpp" #include "../../stdexec/__detail/__execution_fwd.hpp" +#include "../../stdexec/__detail/__gather_completion_signatures.hpp" #include "../../stdexec/__detail/__meta.hpp" #include "../../stdexec/__detail/__sender_introspection.hpp" #include "../../stdexec/__detail/__stop_token.hpp" @@ -833,7 +834,7 @@ namespace experimental::execution struct __gather_sequences_t { template - using __f = STDEXEC::__gather_completions_t< + using __f = STDEXEC::__gather_completion_signatures_t< STDEXEC::set_value_t, _Completions, __arg_of_t<_Sequence, _Sender, _Env...>, diff --git a/include/exec/sequence_senders.hpp b/include/exec/sequence_senders.hpp index 4eaaddc63..68db5ba8a 100644 --- a/include/exec/sequence_senders.hpp +++ b/include/exec/sequence_senders.hpp @@ -31,6 +31,7 @@ import stdexec; # include "../stdexec/__detail/__env.hpp" # include "../stdexec/__detail/__just.hpp" # include "../stdexec/__detail/__meta.hpp" +# include "../stdexec/__detail/__normalize_completion_signature.hpp" # include "../stdexec/__detail/__receivers.hpp" # include "../stdexec/__detail/__senders.hpp" # include "../stdexec/__detail/__stop_token.hpp" @@ -1315,12 +1316,12 @@ namespace experimental::execution }; template - using __sequence_receiver_t = STDEXEC::__mapply< - STDEXEC::__mtransform< - STDEXEC::__mcompose, - STDEXEC::__q1>, - STDEXEC::__mbind_front_q<__sequence_receiver, _CvSequence, _Env, _Items>>, - _Sigs>; + using __sequence_receiver_t = + STDEXEC::__mapply, + STDEXEC::__q1>, + STDEXEC::__mbind_front_q<__sequence_receiver, _CvSequence, _Env, _Items>>, + _Sigs>; template constexpr void __debug_sequence_sender(_CvSequence&& __sequence, _Env const &) diff --git a/include/nvexec/stream/when_all.cuh b/include/nvexec/stream/when_all.cuh index 420a143ad..17934dc7d 100644 --- a/include/nvexec/stream/when_all.cuh +++ b/include/nvexec/stream/when_all.cuh @@ -302,14 +302,13 @@ namespace nv::execution::_strm exit_opstate_t<__copy_cvref_t<_when_all_sender_t, Sender>, receiver>; // tuple, tuple, ...> - using _child_values_t = - __if_c != 0, - __minvoke<__qq<__tuple>, - __value_types_of_t, - __qq<__decayed_tuple>, - __msingle_or>...>, - __>; + using _child_values_t = __if_c<_completions_t::__count(set_value_t()) != 0, + __minvoke<__qq<__tuple>, + __value_types_of_t, + __qq<__decayed_tuple>, + __msingle_or>...>, + __>; using _errors_t = error_types_of_t, __uniqued_variant>; @@ -389,7 +388,7 @@ namespace nv::execution::_strm switch (state_.load(std::memory_order_relaxed)) { case _when_all::started: - if constexpr (STDEXEC::__detail::__count_of != 0) + if constexpr (_completions_t::__count(set_value_t()) != 0) { // All child operations completed successfully: STDEXEC::__apply( @@ -457,7 +456,7 @@ namespace nv::execution::_strm template void _set_value(Args&&... args) noexcept { - if constexpr (STDEXEC::__detail::__count_of != 0) + if constexpr (_completions_t::__count(set_value_t()) != 0) { // We only need to bother recording the completion values // if we're not already in the "error" or "stopped" state. diff --git a/include/stdexec/__detail/__completion_signatures.hpp b/include/stdexec/__detail/__completion_signatures.hpp index 9516d2701..f28a2d601 100644 --- a/include/stdexec/__detail/__completion_signatures.hpp +++ b/include/stdexec/__detail/__completion_signatures.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2024 NVIDIA Corporation + * Copyright (c) 2026 NVIDIA Corporation * * Licensed under the Apache License Version 2.0 with LLVM Exceptions * (the "License"); you may not use this file except in compliance with @@ -26,10 +26,11 @@ import stdexec; # include "__execution_fwd.hpp" // include these after __execution_fwd.hpp +# include "__concat_completion_signatures.hpp" # include "__concepts.hpp" -# include "__diagnostics.hpp" +# include "__diagnostics.hpp" // IWYU pragma: export # include "__meta.hpp" -# include "__tuple.hpp" // IWYU pragma: keep for __tuple +# include "__partition_completion_signatures.hpp" # include "__utility.hpp" # if !STDEXEC_USE_MODULES() @@ -39,6 +40,7 @@ import stdexec; # include "__prologue.hpp" +// NOLINTNEXTLINE(bugprone-std-namespace-modification, modernize-concat-nested-namespaces) namespace STDEXEC { ////////////////////////////////////////////////////////////////////////////// @@ -47,208 +49,18 @@ namespace STDEXEC { STDEXEC_MODULE_EXPORT_AUTHORING template - inline constexpr bool __is_compl_sig = false; + inline constexpr bool __is_valid_cmplsig = false; template - inline constexpr bool __is_compl_sig = true; + inline constexpr bool __is_valid_cmplsig = true; template - inline constexpr bool __is_compl_sig = true; + inline constexpr bool __is_valid_cmplsig = true; template <> - inline constexpr bool __is_compl_sig = true; + inline constexpr bool __is_valid_cmplsig = true; } // namespace __cmplsigs STDEXEC_MODULE_EXPORT_AUTHORING template - concept __completion_signature = __cmplsigs::__is_compl_sig<_Sig>; - - namespace __cmplsigs - { - // The following code is used to normalize completion signatures. - // "Normalization" means that that rvalue-references are stripped from the - // types in the completion signatures. For example, the completion signature - // `set_value_t(int &&)` would be normalized to `set_value_t(int)`, but - // `set_value_t(int)` and `set_value_t(int &)` would remain unchanged. - template - constexpr auto __normalize_sig_impl(_Args&&...) -> _Tag (*)(_Args...); - - template - constexpr auto __normalize_sig(_Tag (*)(_Args...)) - -> decltype(__cmplsigs::__normalize_sig_impl<_Tag>(__declval<_Args>()...)); - - template - constexpr auto __repack_completions(_Sigs*...) -> completion_signatures<_Sigs...>; - - template - constexpr auto __normalize_completions(completion_signatures<_Sigs...>*) - -> decltype(__cmplsigs::__repack_completions( - __cmplsigs::__normalize_sig(static_cast<_Sigs*>(nullptr))...)); - - template - using __normalize_completions_t = decltype(__cmplsigs::__normalize_completions( - static_cast<_Completions*>(nullptr))); - - STDEXEC_MODULE_EXPORT_AUTHORING - template - using __normalize_sig_t = decltype(__cmplsigs::__normalize_sig(static_cast<_Sig*>(nullptr))); - } // namespace __cmplsigs - - template - using __completion_signature_ptrs_t = decltype(__cmplsigs::__repack_completions( - static_cast<_SigPtrs>(nullptr)...)); - - namespace __cmplsigs - { - // __partitions is a cache of completion signatures for fast access. The - // completion_signatures::__partitioned nested struct contains an - // alias to a __partitions specialization. If the cache is never accessed, - // it is never instantiated. - template , - class _ErrorsList = __mlist<>, - class _StoppedList = __mlist<>> - struct __partitions; - - template - struct __partitions<__mlist<_ValueTuples...>, __mlist<_Errors...>, __mlist<_Stopped...>> - { - template - using __value_types = _Variant::template __f<__mapply<_Tuple, _ValueTuples>...>; - - template > - using __error_types = _Variant::template __f...>; - - template - using __stopped_types = _Variant::template __f<__msecond<_Stopped, _Type>...>; - - using __count_values = __msize_t; - using __count_errors = __msize_t; - using __count_stopped = __msize_t; - - struct __decay_copyable - { - // These aliases are placed in a separate struct to avoid computing them - // if they are not needed. - struct __values - { - static constexpr bool value = (__mapply_q<__decay_copyable_t, _ValueTuples>::value - && ...); - }; - struct __errors - { - static constexpr bool value = STDEXEC::__decay_copyable<_Errors...>; - }; - struct __all : __mand_t<__values, __errors> - {}; - }; - - struct __nothrow_decay_copyable - { - // These aliases are placed in a separate struct to avoid computing them - // if they are not needed. - struct __values - { - static constexpr bool value = (__mapply_q<__nothrow_decay_copyable_t, _ValueTuples>::value - && ...); - }; - struct __errors - { - static constexpr bool value = STDEXEC::__nothrow_decay_copyable<_Errors...>; - }; - struct __all : __mand_t<__values, __errors> - {}; - }; - }; - - template - extern __undefined<_Sig, _PartitionsDeclFn> __partition_declfn; - - template - extern __declfn_t<__partitions<__mlist<_Values..., __mlist<_Args...>>, _Errors, _Stopped>> - __partition_declfn, _Errors, _Stopped>>>; - - template - extern __declfn_t<__partitions<_Values, __mlist<_Errors..., _Error>, _Stopped>> - __partition_declfn, _Stopped>>>; - - template - extern __declfn_t<__partitions<_Values, _Errors, __mlist<__mlist<>>>> - __partition_declfn>>; - - template - using __partitions_declfn_t = decltype(__partition_declfn<_Sig, _PartitionsDeclFn>); - - template - using __partition_completion_signatures_t = __call_result_t< - __mcall<__mfold_left<__declfn_t<__partitions<>>, __q2<__partitions_declfn_t>>, _Sigs...>>; - - template - using __partitions_of_t = _Completions::__partitioned::__t; - } // namespace __cmplsigs - - ////////////////////////////////////////////////////////////////////////////// - // completion signatures type traits - STDEXEC_MODULE_EXPORT_AUTHORING - template , - class _Variant = __qq<__std_variant>> - using __value_types_t = - __cmplsigs::__partitions_of_t<_Sigs>::template __value_types<_Tuple, _Variant>; - - template , class _Transform = __q1<__midentity>> - using __error_types_t = - __cmplsigs::__partitions_of_t<_Sigs>::template __error_types<_Variant, _Transform>; - - template > - using __stopped_types_t = - __cmplsigs::__partitions_of_t<_Sigs>::template __stopped_types<_Variant, _Type>; - - template <__valid_completion_signatures _Sigs> - inline constexpr bool __sends_stopped = - __cmplsigs::__partitions_of_t<_Sigs>::__count_stopped::value != 0; - - // set_value_t() when _Type is void; otherwise, set_value_t(_Type) - template - using __single_value_sig_t = __mcall1<__mremove>, _Type>; - - STDEXEC_PRAGMA_PUSH() - STDEXEC_PRAGMA_IGNORE_EDG(expr_has_no_effect) - STDEXEC_PRAGMA_IGNORE_GNU("-Wunused-value") - - ////////////////////////////////////////////////////////////////////////////// - // concat_completion_signatures - STDEXEC_MODULE_EXPORT_AUTHORING - template - using __concat_completion_signatures_t = - __mcall<__mconcat<__munique<__qq>>, _Sigs...>; - - namespace __detail - { - STDEXEC_MODULE_EXPORT_AUTHORING - struct __concat_completion_signatures_fn - { - template - [[nodiscard]] - consteval auto operator()(Sigs...) const noexcept - { - return __concat_completion_signatures_t{}; - } - - template - [[nodiscard]] - consteval auto operator()(Errors...) const noexcept - { - // NB: this uses an overloaded comma operator on the _ERROR_ type to - // find an error in a pack of types. - using __error_t = decltype(+(Errors{}, ...)); - static_assert(__merror<__error_t>); - return STDEXEC::__throw_compile_time_error(__error_t()); - } - }; - } // namespace __detail - - inline constexpr __detail::__concat_completion_signatures_fn __concat_completion_signatures{}; - - STDEXEC_PRAGMA_POP() + concept __completion_signature = __cmplsigs::__is_valid_cmplsig<_Sig>; namespace __detail { @@ -320,11 +132,11 @@ namespace STDEXEC [[nodiscard]] static consteval auto __count(_Tag) noexcept -> std::size_t { - if constexpr (_Tag{} == set_value) + if constexpr (_Tag() == set_value) { return __partitioned::__t::__count_values::value; } - else if constexpr (_Tag{} == set_error) + else if constexpr (_Tag() == set_error) { return __partitioned::__t::__count_errors::value; } @@ -389,21 +201,22 @@ namespace STDEXEC [[nodiscard]] static consteval auto __select(_Tag) noexcept { - if constexpr (_Tag{} == set_value) + if constexpr (_Tag() == set_value) { return __value_types_t, - __qq>{}; + __qq>(); } - else if constexpr (_Tag{} == set_error) + else if constexpr (_Tag() == set_error) { return __error_types_t, - __qf>{}; + __qf>(); } else { - return __stopped_types_t>{}; + return __stopped_types_t>(); } } @@ -437,167 +250,6 @@ namespace STDEXEC STDEXEC_MODULE_EXPORT_AUTHORING template using __eptr_completion_unless_t = __if<_NoExcept, completion_signatures<>, __eptr_completion_t>; - -# if STDEXEC_NO_STDCPP_CONSTEXPR_EXCEPTIONS() - - STDEXEC_MODULE_EXPORT_AUTHORING - template - [[nodiscard]] - consteval auto __throw_compile_time_error_r(_Values...) -> __mexception<_What...> - { - return {}; - } - - template - [[nodiscard]] - consteval auto __throw_compile_time_error(_Values...) -> __mexception<_What...> - { - return {}; - } - -# else // ^^^ no constexpr exceptions ^^^ / vvv constexpr exceptions vvv - - // C++26, https://wg21.link/p3068 - STDEXEC_MODULE_EXPORT_AUTHORING - template - [[noreturn, nodiscard]] - consteval auto __throw_compile_time_error_r([[maybe_unused]] _Values... __values) -> _Return - { - if constexpr (__same_as<_What, dependent_sender_error>) - { - throw __mexception(); - } - else if constexpr (sizeof...(_Values) == 1) - { - throw __sender_type_check_failure<_Values..., _What, _More...>(__values...); - } - else - { - throw __sender_type_check_failure<__tuple<_Values...>, _What, _More...>(__tuple{__values...}); - } - } - - template - [[noreturn, nodiscard]] - consteval auto __throw_compile_time_error(_Values... __values) -> completion_signatures<> - { - (void) STDEXEC::__throw_compile_time_error_r, _What, _More...>( - static_cast<_Values>(__values)...); - } - -# endif // ^^^ constexpr exceptions ^^^ - - template - [[nodiscard]] - consteval auto __throw_compile_time_error_r(__mexception<_What...>) - { - return STDEXEC::__throw_compile_time_error_r<_Return, _What...>(); - } - - template - [[nodiscard]] - consteval auto __throw_compile_time_error(__mexception<_What...>) - { - return STDEXEC::__throw_compile_time_error<_What...>(); - } - - ////////////////////////////////////////////////////////////////////////////// - // __gather_completions_t - namespace __detail - { - template - struct __gather_sigs_fn; - - template <> - struct __gather_sigs_fn - { - template - using __f = __value_types_t<_Sigs, _Tuple, _Variant>; - }; - - template <> - struct __gather_sigs_fn - { - template - using __f = __error_types_t<_Sigs, _Variant, _Tuple>; - }; - - template <> - struct __gather_sigs_fn - { - template - using __f = __stopped_types_t<_Sigs, _Variant, __minvoke<_Tuple>>; - }; - } // namespace __detail - - STDEXEC_MODULE_EXPORT_AUTHORING - template - using __gather_completions_t = - __detail::__gather_sigs_fn<_WantedTag>::template __f<_Sigs, _Tuple, _Variant>; - - namespace __detail - { - template - inline constexpr std::size_t __count_of = 0; - - template - inline constexpr std::size_t __count_of = - __cmplsigs::__partitions_of_t<_Sigs>::__count_values::value; - - template - inline constexpr std::size_t __count_of = - __cmplsigs::__partitions_of_t<_Sigs>::__count_errors::value; - template - inline constexpr std::size_t __count_of = - __cmplsigs::__partitions_of_t<_Sigs>::__count_stopped::value; - } // namespace __detail - - // Below is the definition of the STDEXEC_IF_OK portability macro. It is used - // to check that an expression's type is not an __mexception type. - -# if STDEXEC_NO_STDCPP_CONSTEXPR_EXCEPTIONS() - -# define STDEXEC_IF_OK(_ID) \ - if constexpr (STDEXEC::__merror) \ - { \ - return _ID; \ - } else - - STDEXEC_MODULE_EXPORT_AUTHORING - template - [[nodiscard]] - consteval auto __throw_dependent_sender_error_r() noexcept -> __dependent_sender_error_t<_Sndr> - { - return {}; - } - - template - [[nodiscard]] - consteval auto __throw_dependent_sender_error() noexcept -> __dependent_sender_error_t<_Sndr> - { - return {}; - } - -# else // ^^^ no constexpr exceptions ^^^ / vvv constexpr exceptions vvv - -# define STDEXEC_IF_OK(_ID) - - STDEXEC_MODULE_EXPORT_AUTHORING - template - [[noreturn, nodiscard]] - consteval auto __throw_dependent_sender_error_r() -> _Result - { - throw __dependent_sender_error_t<_Sndr>{}; - } - - template - [[noreturn, nodiscard]] - consteval auto __throw_dependent_sender_error() -> completion_signatures<> - { - throw __dependent_sender_error_t<_Sndr>{}; - } - -# endif // ^^^ constexpr exceptions ^^^ } // namespace STDEXEC # include "__epilogue.hpp" diff --git a/include/stdexec/__detail/__concat_completion_signatures.hpp b/include/stdexec/__detail/__concat_completion_signatures.hpp new file mode 100644 index 000000000..8f69b82e7 --- /dev/null +++ b/include/stdexec/__detail/__concat_completion_signatures.hpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2026 NVIDIA Corporation + * + * Licensed under the Apache License Version 2.0 with LLVM Exceptions + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://llvm.org/LICENSE.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "__config.hpp" + +#if STDEXEC_USE_MODULES() && !defined(STDEXEC_IN_MODULE_PURVIEW) + +import stdexec; + +#else + +# include "__execution_fwd.hpp" + +// include these after __execution_fwd.hpp +# include "__meta.hpp" + +# include "__prologue.hpp" + +// NOLINTNEXTLINE(bugprone-std-namespace-modification, modernize-concat-nested-namespaces) +namespace STDEXEC +{ + STDEXEC_PRAGMA_PUSH() + STDEXEC_PRAGMA_IGNORE_EDG(expr_has_no_effect) + STDEXEC_PRAGMA_IGNORE_GNU("-Wunused-value") + + ////////////////////////////////////////////////////////////////////////////// + // concat_completion_signatures + STDEXEC_MODULE_EXPORT_AUTHORING + template + using __concat_completion_signatures_t = + __mcall<__mconcat<__munique<__qq>>, _Sigs...>; + + namespace __detail + { + STDEXEC_MODULE_EXPORT_AUTHORING + struct __concat_completion_signatures_fn + { + template + [[nodiscard]] + consteval auto operator()(Sigs...) const noexcept + { + return __concat_completion_signatures_t(); + } + + template + [[nodiscard]] + consteval auto operator()(Errors...) const noexcept + { + // NB: this uses an overloaded comma operator on the _ERROR_ type to + // find an error in a pack of types. + using __error_t = decltype(+(Errors(), ...)); + static_assert(__merror<__error_t>); + return STDEXEC::__throw_compile_time_error(__error_t()); + } + }; + } // namespace __detail + + inline constexpr auto __concat_completion_signatures = + __detail::__concat_completion_signatures_fn{}; + + STDEXEC_PRAGMA_POP() +} // namespace STDEXEC + +# include "__epilogue.hpp" +#endif // !STDEXEC_USE_MODULES() || defined(STDEXEC_IN_MODULE_PURVIEW) diff --git a/include/stdexec/__detail/__debug.hpp b/include/stdexec/__detail/__debug.hpp index dfada1ba7..8e3a21c6f 100644 --- a/include/stdexec/__detail/__debug.hpp +++ b/include/stdexec/__detail/__debug.hpp @@ -29,6 +29,7 @@ import stdexec; # include "__concepts.hpp" # include "__env.hpp" // IWYU pragma: keep for env<> # include "__meta.hpp" +# include "__normalize_completion_signature.hpp" # include "__query.hpp" # include "__sender_concepts.hpp" @@ -130,7 +131,7 @@ namespace STDEXEC template using __receiver_t = __mapply< - __mtransform<__mcompose<__q1, __q1<__cmplsigs::__normalize_sig_t>>, + __mtransform<__mcompose<__q1, __q1<__cmplsigs::__normalize_t>>, __mbind_front_q<__receiver, _CvSender, _Env>>, _Sigs>; diff --git a/include/stdexec/__detail/__diagnostics.hpp b/include/stdexec/__detail/__diagnostics.hpp index 1d3bb6f12..0dbcaaa0f 100644 --- a/include/stdexec/__detail/__diagnostics.hpp +++ b/include/stdexec/__detail/__diagnostics.hpp @@ -28,6 +28,7 @@ import stdexec; // include these after __execution_fwd.hpp # include "__meta.hpp" +# include "__tuple.hpp" // IWYU pragma: keep for __tuple # if !STDEXEC_USE_MODULES() # include // IWYU pragma: keep for std::exception @@ -35,6 +36,7 @@ import stdexec; # include "__prologue.hpp" +// NOLINTNEXTLINE(bugprone-std-namespace-modification, modernize-concat-nested-namespaces) namespace STDEXEC { struct sender_tag; @@ -316,11 +318,121 @@ namespace STDEXEC constexpr auto schedule() noexcept { - return __not_a_sender<_What...>{}; + return __not_a_sender<_What...>(); } constexpr bool operator==(__not_a_scheduler const &) const noexcept = default; }; + +# if STDEXEC_NO_STDCPP_CONSTEXPR_EXCEPTIONS() + + STDEXEC_MODULE_EXPORT_AUTHORING + template + [[nodiscard]] + consteval auto __throw_compile_time_error_r(_Values...) -> __mexception<_What...> + { + return {}; + } + + template + [[nodiscard]] + consteval auto __throw_compile_time_error(_Values...) -> __mexception<_What...> + { + return {}; + } + +# else // ^^^ no constexpr exceptions ^^^ / vvv constexpr exceptions vvv + + // C++26, https://wg21.link/p3068 + STDEXEC_MODULE_EXPORT_AUTHORING + template + [[noreturn, nodiscard]] + consteval auto __throw_compile_time_error_r([[maybe_unused]] _Values... __values) -> _Return + { + if constexpr (__same_as<_What, dependent_sender_error>) + { + throw __mexception(); + } + else if constexpr (sizeof...(_Values) == 1) + { + throw __sender_type_check_failure<_Values..., _What, _More...>(__values...); + } + else + { + throw __sender_type_check_failure<__tuple<_Values...>, _What, _More...>(__tuple{__values...}); + } + } + + template + [[noreturn, nodiscard]] + consteval auto __throw_compile_time_error(_Values... __values) -> completion_signatures<> + { + (void) STDEXEC::__throw_compile_time_error_r, _What, _More...>( + static_cast<_Values>(__values)...); + } + +# endif // ^^^ constexpr exceptions ^^^ + + template + [[nodiscard]] + consteval auto __throw_compile_time_error_r(__mexception<_What...>) + { + return STDEXEC::__throw_compile_time_error_r<_Return, _What...>(); + } + + template + [[nodiscard]] + consteval auto __throw_compile_time_error(__mexception<_What...>) + { + return STDEXEC::__throw_compile_time_error<_What...>(); + } + + // Below is the definition of the STDEXEC_IF_OK portability macro. It is used + // to check that an expression's type is not an __mexception type. + +# if STDEXEC_NO_STDCPP_CONSTEXPR_EXCEPTIONS() + +# define STDEXEC_IF_OK(_ID) \ + if constexpr (STDEXEC::__merror) \ + { \ + return _ID; \ + } else + + STDEXEC_MODULE_EXPORT_AUTHORING + template + [[nodiscard]] + consteval auto __throw_dependent_sender_error_r() noexcept -> __dependent_sender_error_t<_Sndr> + { + return {}; + } + + template + [[nodiscard]] + consteval auto __throw_dependent_sender_error() noexcept -> __dependent_sender_error_t<_Sndr> + { + return {}; + } + +# else // ^^^ no constexpr exceptions ^^^ / vvv constexpr exceptions vvv + +# define STDEXEC_IF_OK(_ID) + + STDEXEC_MODULE_EXPORT_AUTHORING + template + [[noreturn, nodiscard]] + consteval auto __throw_dependent_sender_error_r() -> _Result + { + throw __dependent_sender_error_t<_Sndr>(); + } + + template + [[noreturn, nodiscard]] + consteval auto __throw_dependent_sender_error() -> completion_signatures<> + { + throw __dependent_sender_error_t<_Sndr>(); + } + +# endif // ^^^ constexpr exceptions ^^^ } // namespace STDEXEC # include "__epilogue.hpp" diff --git a/include/stdexec/__detail/__gather_completion_signatures.hpp b/include/stdexec/__detail/__gather_completion_signatures.hpp new file mode 100644 index 000000000..4de9cd4b3 --- /dev/null +++ b/include/stdexec/__detail/__gather_completion_signatures.hpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2026 NVIDIA Corporation + * + * Licensed under the Apache License Version 2.0 with LLVM Exceptions + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://llvm.org/LICENSE.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "__config.hpp" + +#if STDEXEC_USE_MODULES() && !defined(STDEXEC_IN_MODULE_PURVIEW) + +import stdexec; + +#else + +# include "__execution_fwd.hpp" + +// include these after __execution_fwd.hpp +# include "__meta.hpp" +# include "__partition_completion_signatures.hpp" + +# include "__prologue.hpp" + +// NOLINTNEXTLINE(bugprone-std-namespace-modification, modernize-concat-nested-namespaces) +namespace STDEXEC +{ + ////////////////////////////////////////////////////////////////////////////// + // __gather_completion_signatures_t + namespace __detail + { + template + struct __gather_sigs_fn; + + template <> + struct __gather_sigs_fn + { + template + using __f = __value_types_t<_Sigs, _Tuple, _Variant>; + }; + + template <> + struct __gather_sigs_fn + { + template + using __f = __error_types_t<_Sigs, _Variant, _Tuple>; + }; + + template <> + struct __gather_sigs_fn + { + template + using __f = __stopped_types_t<_Sigs, _Variant, __minvoke<_Tuple>>; + }; + } // namespace __detail + + STDEXEC_MODULE_EXPORT_AUTHORING + template + using __gather_completion_signatures_t = + __mcall<__detail::__gather_sigs_fn<_WantedTag>, _Sigs, _Tuple, _Variant>; +} // namespace STDEXEC + +# include "__epilogue.hpp" +#endif // !STDEXEC_USE_MODULES() || defined(STDEXEC_IN_MODULE_PURVIEW) diff --git a/include/stdexec/__detail/__get_completion_signatures.hpp b/include/stdexec/__detail/__get_completion_signatures.hpp index 3b50c55aa..ddef32358 100644 --- a/include/stdexec/__detail/__get_completion_signatures.hpp +++ b/include/stdexec/__detail/__get_completion_signatures.hpp @@ -31,6 +31,7 @@ import stdexec; # include "__connect_awaitable.hpp" # include "__diagnostics.hpp" # include "__env.hpp" +# include "__gather_completion_signatures.hpp" # include "__meta.hpp" # include "__tuple.hpp" // IWYU pragma: keep for __tuple @@ -438,10 +439,10 @@ namespace STDEXEC template using __gather_completions_of_t = - __gather_completions_t<_WantedTag, - __completion_signatures_of_t<_Sender, _Env>, - _Tuple, - _Variant>; + __gather_completion_signatures_t<_WantedTag, + __completion_signatures_of_t<_Sender, _Env>, + _Tuple, + _Variant>; STDEXEC_MODULE_EXPORT_AUTHORING template - using __count_of = - __msize_t<__detail::__count_of<_Tag, __completion_signatures_of_t<_Sender, _Env...>>>; + using __count_of = __msize_t<__completion_signatures_of_t<_Sender, _Env...>::__count(_Tag())>; } // namespace STDEXEC # include "__epilogue.hpp" diff --git a/include/stdexec/__detail/__let.hpp b/include/stdexec/__detail/__let.hpp index 39e1ebb5e..b5d538194 100644 --- a/include/stdexec/__detail/__let.hpp +++ b/include/stdexec/__detail/__let.hpp @@ -29,6 +29,7 @@ import stdexec; # include "__basic_sender.hpp" # include "__diagnostics.hpp" # include "__env.hpp" +# include "__gather_completion_signatures.hpp" # include "__meta.hpp" # include "__schedulers.hpp" # include "__sender_adaptor_closure.hpp" @@ -157,7 +158,7 @@ namespace STDEXEC }; template - using __has_nothrow_completions_t = __gather_completions_t< + using __has_nothrow_completions_t = __gather_completion_signatures_t< completion_signatures_of_t<_Child, _Env>, _SetTag, __has_nothrow_completions_fn<_Fn, __result_env_t<_SetTag, _Child, _Env>>, @@ -418,7 +419,7 @@ namespace STDEXEC // if constexpr (sender_in<_Sndr, _Env...>) // { // using __domain_transform_fn = __let::__domain_transform_fn<_SetTag, _Fun, _Sndr, _Env...>; - // return __minvoke_or_q<__gather_completions_t, + // return __minvoke_or_q<__gather_completion_signatures_t, // indeterminate_domain<>, // __t<_LetTag>, // __completion_signatures_of_t<_Sndr, _Env...>, @@ -493,7 +494,7 @@ namespace STDEXEC // constexpr auto __pred_behavior = // STDEXEC::__get_completion_behavior<__set_tag_t, _Sndr, __fwd_env_t<_Env>...>(); - // constexpr auto __result_behaviors = __gather_completions_t< + // constexpr auto __result_behaviors = __gather_completion_signatures_t< // __set_tag_t, // __completions_t, // __transform_fn, diff --git a/include/stdexec/__detail/__normalize_completion_signature.hpp b/include/stdexec/__detail/__normalize_completion_signature.hpp new file mode 100644 index 000000000..61dacb9d4 --- /dev/null +++ b/include/stdexec/__detail/__normalize_completion_signature.hpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2026 NVIDIA Corporation + * + * Licensed under the Apache License Version 2.0 with LLVM Exceptions + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://llvm.org/LICENSE.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "__config.hpp" + +#if STDEXEC_USE_MODULES() && !defined(STDEXEC_IN_MODULE_PURVIEW) + +import stdexec; + +#else + +# include "__execution_fwd.hpp" + +# include "__meta.hpp" +# include "__type_traits.hpp" + +# include "__prologue.hpp" + +// NOLINTNEXTLINE(bugprone-std-namespace-modification, modernize-concat-nested-namespaces) +namespace STDEXEC +{ + namespace __cmplsigs + { + // The following code is used to normalize completion signatures. + // "Normalization" means that that rvalue-references are stripped from the + // types in the completion signatures. For example, the completion signature + // `set_value_t(int &&)` would be normalized to `set_value_t(int)`, but + // `set_value_t(int)` and `set_value_t(int &)` would remain unchanged. + template + constexpr auto __repack(_Sigs*...) -> completion_signatures<_Sigs...>; + + template + constexpr auto __normalize_fn(_Args&&...) -> _Tag (*)(_Args...); + + template + constexpr auto __normalize(_Tag (*)(_Args...)) + -> decltype(__cmplsigs::__normalize_fn<_Tag>(__declval<_Args>()...)); + + template + constexpr auto __normalize(completion_signatures<_Sigs...>*) + -> decltype(__cmplsigs::__repack(__cmplsigs::__normalize(__midentity<_Sigs*>())...)); + + STDEXEC_MODULE_EXPORT_AUTHORING + template + using __normalize_t = decltype(__cmplsigs::__normalize(__midentity<_CompletionsOrSig*>())); + } // namespace __cmplsigs + + STDEXEC_MODULE_EXPORT_AUTHORING + template + using __completion_signature_ptrs_t = decltype(__cmplsigs::__repack(_SigPtrs()...)); +} // namespace STDEXEC + +# include "__epilogue.hpp" +#endif // !STDEXEC_USE_MODULES() || defined(STDEXEC_IN_MODULE_PURVIEW) diff --git a/include/stdexec/__detail/__partition_completion_signatures.hpp b/include/stdexec/__detail/__partition_completion_signatures.hpp new file mode 100644 index 000000000..e1f4b8ad1 --- /dev/null +++ b/include/stdexec/__detail/__partition_completion_signatures.hpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2026 NVIDIA Corporation + * + * Licensed under the Apache License Version 2.0 with LLVM Exceptions + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://llvm.org/LICENSE.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "__config.hpp" + +#if STDEXEC_USE_MODULES() && !defined(STDEXEC_IN_MODULE_PURVIEW) + +import stdexec; + +#else + +# include "__execution_fwd.hpp" + +// include these after __execution_fwd.hpp +# include "__concepts.hpp" +# include "__meta.hpp" +# include "__type_traits.hpp" +# include "__utility.hpp" + +# include "__prologue.hpp" + +// NOLINTNEXTLINE(bugprone-std-namespace-modification, modernize-concat-nested-namespaces) +namespace STDEXEC +{ + namespace __cmplsigs + { + // __partitions is a cache of completion signatures for fast access. The + // completion_signatures::__partitioned nested struct contains an + // alias to a __partitions specialization. If the cache is never accessed, + // it is never instantiated. + template , + class _ErrorsList = __mlist<>, + class _StoppedList = __mlist<>> + struct __partitions; + + template + struct __partitions<__mlist<_ValueTuples...>, __mlist<_Errors...>, __mlist<_Stopped...>> + { + template + using __value_types = _Variant::template __f<__mapply<_Tuple, _ValueTuples>...>; + + template > + using __error_types = _Variant::template __f...>; + + template + using __stopped_types = _Variant::template __f<__msecond<_Stopped, _Type>...>; + + using __count_values = __msize_t; + using __count_errors = __msize_t; + using __count_stopped = __msize_t; + + struct __decay_copyable + { + // These types are placed in a separate struct to avoid computing them + // if they are not needed. + struct __values : __mand_t<__mapply_q<__decay_copyable_t, _ValueTuples>...> + {}; + + struct __errors : __decay_copyable_t<_Errors...> + {}; + + struct __all : __mand_t<__values, __errors> + {}; + }; + + struct __nothrow_decay_copyable + { + // These types are placed in a separate struct to avoid computing them + // if they are not needed. + struct __values : __mand_t<__mapply_q<__nothrow_decay_copyable_t, _ValueTuples>...> + {}; + + struct __errors : __nothrow_decay_copyable_t<_Errors...> + {}; + + struct __all : __mand_t<__values, __errors> + {}; + }; + }; + + template + extern __undefined<_Sig, _PartitionsDeclFn> __partition_declfn; + + template + extern __declfn_t<__partitions<__mlist<_Values..., __mlist<_Args...>>, _Errors, _Stopped>> + __partition_declfn, _Errors, _Stopped>>>; + + template + extern __declfn_t<__partitions<_Values, __mlist<_Errors..., _Error>, _Stopped>> + __partition_declfn, _Stopped>>>; + + template + extern __declfn_t<__partitions<_Values, _Errors, __mlist<__mlist<>>>> + __partition_declfn>>; + + template + using __partitions_declfn_t = decltype(__partition_declfn<_Sig, _PartitionsDeclFn>); + + template + using __partition_completion_signatures_t = __call_result_t< + __mcall<__mfold_left<__declfn_t<__partitions<>>, __q2<__partitions_declfn_t>>, _Sigs...>>; + + template + using __partitions_of_t = _Completions::__partitioned::__t; + } // namespace __cmplsigs + + ////////////////////////////////////////////////////////////////////////////// + // completion signatures type traits + STDEXEC_MODULE_EXPORT_AUTHORING + template , + class _Variant = __qq<__std_variant>> + using __value_types_t = + __cmplsigs::__partitions_of_t<_Sigs>::template __value_types<_Tuple, _Variant>; + + template , class _Transform = __q1<__midentity>> + using __error_types_t = + __cmplsigs::__partitions_of_t<_Sigs>::template __error_types<_Variant, _Transform>; + + template > + using __stopped_types_t = + __cmplsigs::__partitions_of_t<_Sigs>::template __stopped_types<_Variant, _Type>; + + template <__valid_completion_signatures _Sigs> + inline constexpr bool __sends_stopped = + __cmplsigs::__partitions_of_t<_Sigs>::__count_stopped::value != 0; + + // set_value_t() when _Type is void; otherwise, set_value_t(_Type) + template + using __single_value_sig_t = __mcall1<__mremove>, _Type>; +} // namespace STDEXEC + +# include "__epilogue.hpp" +#endif // !STDEXEC_USE_MODULES() || defined(STDEXEC_IN_MODULE_PURVIEW) diff --git a/include/stdexec/__detail/__senders.hpp b/include/stdexec/__detail/__senders.hpp index 80bff6dfd..6ff44ec0d 100644 --- a/include/stdexec/__detail/__senders.hpp +++ b/include/stdexec/__detail/__senders.hpp @@ -26,9 +26,10 @@ import stdexec; # include "__execution_fwd.hpp" // include these after __execution_fwd.hpp -# include "__completion_signatures.hpp" // IWYU pragma: export -# include "__connect.hpp" // IWYU pragma: export -# include "__sender_concepts.hpp" // IWYU pragma: export +# include "__completion_signatures.hpp" // IWYU pragma: export +# include "__connect.hpp" // IWYU pragma: export +# include "__gather_completion_signatures.hpp" // +# include "__sender_concepts.hpp" // IWYU pragma: export # include "__prologue.hpp" @@ -59,11 +60,12 @@ namespace STDEXEC STDEXEC_MODULE_EXPORT template concept sender_of = sender_in<_Sender, _Env...> - && __same_as<__mlist<_SetSig>, - __gather_completions_t<__signature_tag_t<_SetSig>, - __completion_signatures_of_t<_Sender, _Env...>, - __mcompose<__qq<__mlist>, __qf<__signature_tag_t<_SetSig>>>, - __mconcat<__qq<__mlist>>>>; + && __same_as< + __mlist<_SetSig>, + __gather_completion_signatures_t<__signature_tag_t<_SetSig>, + __completion_signatures_of_t<_Sender, _Env...>, + __mcompose<__qq<__mlist>, __qf<__signature_tag_t<_SetSig>>>, + __mconcat<__qq<__mlist>>>>; template concept __nofail_sender = __never_sends; diff --git a/include/stdexec/__detail/__transform_completion_signatures.hpp b/include/stdexec/__detail/__transform_completion_signatures.hpp index f015713a0..a2bceeb1b 100644 --- a/include/stdexec/__detail/__transform_completion_signatures.hpp +++ b/include/stdexec/__detail/__transform_completion_signatures.hpp @@ -31,6 +31,7 @@ import stdexec; # include "__completion_signatures_of.hpp" # include "__concepts.hpp" # include "__debug.hpp" // IWYU pragma: keep +# include "__gather_completion_signatures.hpp" # include "__get_completion_signatures.hpp" # include "__meta.hpp" @@ -563,10 +564,11 @@ namespace STDEXEC template using __with_error_invoke_t = - __if<__gather_completions_t<_Tag, - __completion_signatures_of_t<_Sender, _Env...>, - __mbind_front<__mtry_catch_q<__nothrow_invocable_t, _Catch>, _Fun>, - __qq<__mand>>, + __if<__gather_completion_signatures_t< + _Tag, + __completion_signatures_of_t<_Sender, _Env...>, + __mbind_front<__mtry_catch_q<__nothrow_invocable_t, _Catch>, _Fun>, + __qq<__mand>>, completion_signatures<>, __eptr_completion_t>; diff --git a/test/stdexec/detail/test_completion_signatures.cpp b/test/stdexec/detail/test_completion_signatures.cpp index 6ccf710ff..72f482984 100644 --- a/test/stdexec/detail/test_completion_signatures.cpp +++ b/test/stdexec/detail/test_completion_signatures.cpp @@ -135,8 +135,10 @@ namespace void expect_val_types() { using expected_t = ex::__mmake_set; - using actual_t = - ex::__gather_completions_t, ex::__q>; + using actual_t = ex::__gather_completion_signatures_t, + ex::__q>; static_assert(ex::__mset_eq); } @@ -144,8 +146,10 @@ namespace void expect_err_types() { using expected_t = ex::__mmake_set; - using actual_t = - ex::__gather_completions_t, ex::__q>; + using actual_t = ex::__gather_completion_signatures_t, + ex::__q>; static_assert(ex::__mset_eq); }