From fc236f759f918ffd456b07c7f7b68aea5c6d2d9a Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Fri, 17 Apr 2026 17:07:08 -0700 Subject: [PATCH 1/8] experimental code to compute completions during constant evaluation --- include/exec/detail/basic_sequence.hpp | 2 +- include/exec/repeat_until.hpp | 4 +- include/exec/static_thread_pool.hpp | 1 - include/nvexec/nvtx.cuh | 2 +- include/nvexec/stream/algorithm_base.cuh | 4 +- include/nvexec/stream/bulk.cuh | 5 +- include/nvexec/stream/continues_on.cuh | 2 +- include/nvexec/stream/ensure_started.cuh | 2 +- include/nvexec/stream/launch.cuh | 2 +- include/nvexec/stream/let_xxx.cuh | 2 +- include/nvexec/stream/reduce.cuh | 2 +- include/nvexec/stream/schedule_from.cuh | 2 +- include/nvexec/stream/split.cuh | 2 +- include/nvexec/stream/then.cuh | 2 +- include/nvexec/stream/upon_error.cuh | 2 +- include/nvexec/stream/upon_stopped.cuh | 2 +- include/nvexec/stream/when_all.cuh | 2 +- include/stdexec/__detail/__basic_sender.hpp | 2 +- .../stdexec/__detail/__completion_info.hpp | 159 +++++++++++++ .../__detail/__completion_signatures.hpp | 45 ++-- include/stdexec/__detail/__concepts.hpp | 10 + include/stdexec/__detail/__continues_on.hpp | 8 +- include/stdexec/__detail/__cpo.hpp | 138 ----------- include/stdexec/__detail/__diagnostics.hpp | 13 +- include/stdexec/__detail/__domain.hpp | 8 - include/stdexec/__detail/__execution_fwd.hpp | 18 ++ include/stdexec/__detail/__finally.hpp | 8 +- .../__detail/__get_completion_signatures.hpp | 14 ++ include/stdexec/__detail/__let.hpp | 215 +++++++++++------- include/stdexec/__detail/__meta.hpp | 24 +- include/stdexec/__detail/__receivers.hpp | 7 - include/stdexec/__detail/__sequence.hpp | 2 +- include/stdexec/__detail/__static_vector.hpp | 120 ++++++++++ .../__detail/__stopped_as_optional.hpp | 4 +- .../__transform_completion_signatures.hpp | 4 +- include/stdexec/__detail/__typeinfo.hpp | 19 +- include/stdexec/__detail/__utility.hpp | 11 +- include/stdexec/execution.hpp | 1 - include/stdexec/functional.hpp | 13 +- 39 files changed, 556 insertions(+), 327 deletions(-) create mode 100644 include/stdexec/__detail/__completion_info.hpp delete mode 100644 include/stdexec/__detail/__cpo.hpp create mode 100644 include/stdexec/__detail/__static_vector.hpp diff --git a/include/exec/detail/basic_sequence.hpp b/include/exec/detail/basic_sequence.hpp index 2c4a92b38..0357b7a0c 100644 --- a/include/exec/detail/basic_sequence.hpp +++ b/include/exec/detail/basic_sequence.hpp @@ -127,6 +127,6 @@ namespace STDEXEC::__detail extern decltype(_DescriptorFn()) __desc_of_v>; template - extern __declfn_t<__minvoke>> + extern __mtype<__minvoke>> __demangle_v>; } // namespace STDEXEC::__detail diff --git a/include/exec/repeat_until.hpp b/include/exec/repeat_until.hpp index c44b36645..13b18f3cf 100644 --- a/include/exec/repeat_until.hpp +++ b/include/exec/repeat_until.hpp @@ -275,8 +275,8 @@ namespace experimental::execution using __eptr_completion_t = set_error_t(std::exception_ptr); constexpr auto __eptr_completion = (__eptr_completion_t *) nullptr; - STDEXEC_COMPLSIGS_LET( - __sigs, + STDEXEC_TRY_LET( + auto __sigs, exec::transform_completion_signatures(get_completion_signatures<__child_t, _Env...>(), __transform_values<__child_t>, __transform_errors)) diff --git a/include/exec/static_thread_pool.hpp b/include/exec/static_thread_pool.hpp index 582f91b12..6673cc619 100644 --- a/include/exec/static_thread_pool.hpp +++ b/include/exec/static_thread_pool.hpp @@ -45,7 +45,6 @@ #include "sequence/iterate.hpp" #include "sequence_senders.hpp" -#include #include #include #include diff --git a/include/nvexec/nvtx.cuh b/include/nvexec/nvtx.cuh index 0c5bd4de2..d0be24e8b 100644 --- a/include/nvexec/nvtx.cuh +++ b/include/nvexec/nvtx.cuh @@ -196,7 +196,7 @@ namespace nvexec = nv::execution; namespace STDEXEC::__detail { template - extern __declfn_t>> + extern __mtype>> __demangle_v>; } // namespace STDEXEC::__detail diff --git a/include/nvexec/stream/algorithm_base.cuh b/include/nvexec/stream/algorithm_base.cuh index c5b6a54ab..8832c1a84 100644 --- a/include/nvexec/stream/algorithm_base.cuh +++ b/include/nvexec/stream/algorithm_base.cuh @@ -145,7 +145,7 @@ namespace nvexec = nv::execution; namespace STDEXEC::__detail { template - extern __declfn_t, InitT, Fun, __demangle_t>> + extern __mtype, InitT, Fun, __demangle_t>> __demangle_v>; } // namespace STDEXEC::__detail diff --git a/include/nvexec/stream/bulk.cuh b/include/nvexec/stream/bulk.cuh index e8b277ab2..a3a2a6ab8 100644 --- a/include/nvexec/stream/bulk.cuh +++ b/include/nvexec/stream/bulk.cuh @@ -440,12 +440,11 @@ namespace nvexec = nv::execution; namespace STDEXEC::__detail { template - inline constexpr __declfn_t, Shape, Fun>> + inline constexpr __mtype, Shape, Fun>> __demangle_v>{}; template - inline constexpr __declfn_t< - nvexec::_strm::multi_gpu_bulk_sender<__demangle_t, Shape, Fun>> + inline constexpr __mtype, Shape, Fun>> __demangle_v>{}; } // namespace STDEXEC::__detail diff --git a/include/nvexec/stream/continues_on.cuh b/include/nvexec/stream/continues_on.cuh index a7ee8f260..b06697755 100644 --- a/include/nvexec/stream/continues_on.cuh +++ b/include/nvexec/stream/continues_on.cuh @@ -284,7 +284,7 @@ namespace nvexec = nv::execution; namespace STDEXEC::__detail { template - extern __declfn_t>> + extern __mtype>> __demangle_v>; } // namespace STDEXEC::__detail diff --git a/include/nvexec/stream/ensure_started.cuh b/include/nvexec/stream/ensure_started.cuh index 324842cd2..30ae50ab4 100644 --- a/include/nvexec/stream/ensure_started.cuh +++ b/include/nvexec/stream/ensure_started.cuh @@ -430,7 +430,7 @@ namespace nvexec = nv::execution; namespace STDEXEC::__detail { template - extern __declfn_t>> + extern __mtype>> __demangle_v>; } // namespace STDEXEC::__detail diff --git a/include/nvexec/stream/launch.cuh b/include/nvexec/stream/launch.cuh index 35b29e785..de3f79f1d 100644 --- a/include/nvexec/stream/launch.cuh +++ b/include/nvexec/stream/launch.cuh @@ -204,7 +204,7 @@ namespace nvexec = nv::execution; namespace STDEXEC::__detail { template - extern __declfn_t, Fun>> + extern __mtype, Fun>> __demangle_v>; } // namespace STDEXEC::__detail diff --git a/include/nvexec/stream/let_xxx.cuh b/include/nvexec/stream/let_xxx.cuh index 4d7e3b940..0ed840cb5 100644 --- a/include/nvexec/stream/let_xxx.cuh +++ b/include/nvexec/stream/let_xxx.cuh @@ -344,7 +344,7 @@ namespace nvexec = nv::execution; namespace STDEXEC::__detail { template - extern __declfn_t, Fun, SetTag>> + extern __mtype, Fun, SetTag>> __demangle_v>; } // namespace STDEXEC::__detail diff --git a/include/nvexec/stream/reduce.cuh b/include/nvexec/stream/reduce.cuh index 90a5c3780..b1ec38b56 100644 --- a/include/nvexec/stream/reduce.cuh +++ b/include/nvexec/stream/reduce.cuh @@ -165,7 +165,7 @@ namespace nvexec = nv::execution; namespace STDEXEC::__detail { template - extern __declfn_t, Init, Fun>> + extern __mtype, Init, Fun>> __demangle_v>; } // namespace STDEXEC::__detail diff --git a/include/nvexec/stream/schedule_from.cuh b/include/nvexec/stream/schedule_from.cuh index cb979c650..8f7edee11 100644 --- a/include/nvexec/stream/schedule_from.cuh +++ b/include/nvexec/stream/schedule_from.cuh @@ -196,6 +196,6 @@ namespace nvexec = nv::execution; namespace STDEXEC::__detail { template - extern __declfn_t>> + extern __mtype>> __demangle_v>; } // namespace STDEXEC::__detail diff --git a/include/nvexec/stream/split.cuh b/include/nvexec/stream/split.cuh index 7488cbb39..d461e3ff1 100644 --- a/include/nvexec/stream/split.cuh +++ b/include/nvexec/stream/split.cuh @@ -412,7 +412,7 @@ namespace nvexec = nv::execution; namespace STDEXEC::__detail { template - extern __declfn_t>> + extern __mtype>> __demangle_v>; } // namespace STDEXEC::__detail diff --git a/include/nvexec/stream/then.cuh b/include/nvexec/stream/then.cuh index 9d8f0171c..44e3a53b7 100644 --- a/include/nvexec/stream/then.cuh +++ b/include/nvexec/stream/then.cuh @@ -228,7 +228,7 @@ namespace nvexec = nv::execution; namespace STDEXEC::__detail { template - extern __declfn_t, Fun>> + extern __mtype, Fun>> __demangle_v>; } // namespace STDEXEC::__detail diff --git a/include/nvexec/stream/upon_error.cuh b/include/nvexec/stream/upon_error.cuh index 4f0904f1a..38350b9ff 100644 --- a/include/nvexec/stream/upon_error.cuh +++ b/include/nvexec/stream/upon_error.cuh @@ -216,7 +216,7 @@ namespace nvexec = nv::execution; namespace STDEXEC::__detail { template - extern __declfn_t, Fun>> + extern __mtype, Fun>> __demangle_v>; } // namespace STDEXEC::__detail diff --git a/include/nvexec/stream/upon_stopped.cuh b/include/nvexec/stream/upon_stopped.cuh index f18e4a120..cb7668c59 100644 --- a/include/nvexec/stream/upon_stopped.cuh +++ b/include/nvexec/stream/upon_stopped.cuh @@ -207,7 +207,7 @@ namespace nvexec = nv::execution; namespace STDEXEC::__detail { template - extern __declfn_t, Fun>> + extern __mtype, Fun>> __demangle_v>; } // namespace STDEXEC::__detail diff --git a/include/nvexec/stream/when_all.cuh b/include/nvexec/stream/when_all.cuh index a367a9653..efa005e18 100644 --- a/include/nvexec/stream/when_all.cuh +++ b/include/nvexec/stream/when_all.cuh @@ -576,7 +576,7 @@ namespace nvexec = nv::execution; namespace STDEXEC::__detail { template - extern __declfn_t...>> + extern __mtype...>> __demangle_v>; } // namespace STDEXEC::__detail diff --git a/include/stdexec/__detail/__basic_sender.hpp b/include/stdexec/__detail/__basic_sender.hpp index 1f987463a..44fdf8a6b 100644 --- a/include/stdexec/__detail/__basic_sender.hpp +++ b/include/stdexec/__detail/__basic_sender.hpp @@ -456,7 +456,7 @@ namespace STDEXEC using __basic_sender_t = __basic_sender<_Tag, _Data, __demangle_t<_Child>...>::type; template - extern __declfn_t<__minvoke>> + extern __mtype<__minvoke>> __demangle_v<__sexpr<_Descriptor>>; } // namespace __detail } // namespace STDEXEC diff --git a/include/stdexec/__detail/__completion_info.hpp b/include/stdexec/__detail/__completion_info.hpp new file mode 100644 index 000000000..fa793c293 --- /dev/null +++ b/include/stdexec/__detail/__completion_info.hpp @@ -0,0 +1,159 @@ +/* + * 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 "__execution_fwd.hpp" + +// IWYU pragma: begin_keep +#include "__completion_behavior.hpp" +#include "__completion_signatures.hpp" +#include "__meta.hpp" +#include "__static_vector.hpp" +#include "__typeinfo.hpp" + +#include "../functional.hpp" + +#include +#include +#include +#include +#include +// IWYU pragma: end_keep + +namespace STDEXEC +{ + template + constexpr _Sig *__signature = nullptr; + + struct __completion_info + { + using __behavior_t = __completion_behavior::__behavior; + + STDEXEC::__disposition __disposition = __invalid_disposition; + __type_index __signature = __mtypeid; + __type_index __domain = __mtypeid; + __behavior_t __behavior = __completion_behavior::__unknown; + + __completion_info() = default; + + template <__completion_tag _Tag, class... _Args> + constexpr __completion_info(_Tag (*)(_Args...), + __type_index __domain = __mtypeid, + __behavior_t __behavior = __completion_behavior::__unknown) noexcept + : __disposition(_Tag::__disposition) + , __signature(__mtypeid<_Tag(_Args...)>) + , __domain(__domain) + , __behavior(__behavior) + {} + + template + constexpr auto __populate() noexcept -> __completion_info & + { + switch (__disposition) + { + case __disposition::__value: + __domain = __mtypeid<__completion_domain_t, _Env...>>; + __behavior = STDEXEC::__get_completion_behavior(); + break; + case __disposition::__error: + __domain = __mtypeid<__completion_domain_t, _Env...>>; + __behavior = STDEXEC::__get_completion_behavior(); + break; + case __disposition::__stopped: + __domain = __mtypeid<__completion_domain_t, _Env...>>; + __behavior = STDEXEC::__get_completion_behavior(); + break; + } + return *this; + } + + [[nodiscard]] + constexpr auto + operator<=>(__completion_info const &) const noexcept -> std::strong_ordering = default; + }; + + namespace __cmplsigs + { + template + constexpr auto __completion_info_from_v = []() noexcept + { + STDEXEC_TRY_LET(auto __cmpl_info, _GetComplInfo()) + { + constexpr auto __size = _GetComplInfo().size(); + auto __arr = __static_vector<__completion_info, __size>(); + std::ranges::sort(__cmpl_info); + auto const __end = std::ranges::unique_copy(__cmpl_info, __arr.begin()).out; + __arr.resize(__end - __arr.begin()); + return __arr; + } + }(); + + template + consteval auto __completion_info_from(_GetComplInfo) noexcept -> auto const & + { + return __completion_info_from_v<(_GetComplInfo())>; + } + + template + constexpr auto __completion_sigs_from_v = []() noexcept + { + STDEXEC_TRY_LET(constexpr auto __completions, __completion_info_from_v<_GetComplInfo>) + { + auto __signatures = __static_vector<__type_index, __completions.size()>(); + __signatures.resize(__completions.size()); + std::ranges::transform(__completions, + __signatures.begin(), + &__completion_info::__signature); + auto const __end = std::ranges::unique(__signatures).begin(); + __signatures.resize(__end - __signatures.begin()); + return __signatures; + } + }(); + + template + consteval auto __completion_sigs_from(_GetComplInfo) noexcept + { + STDEXEC_TRY_LET(constexpr auto __sigs, __completion_sigs_from_v<(_GetComplInfo())>) + { + constexpr auto __fn = [=](__indices<_Is...>) + { + return completion_signatures<__mtypeof<__sigs[_Is]>...>(); + }; + return __fn(__make_indices<__sigs.size()>()); + } + } + + template + [[nodiscard]] + consteval auto __to_array(completion_signatures<_Sigs...>) noexcept + { + using __array_t = __static_vector<__completion_info, sizeof...(_Sigs)>; + auto __compls = __array_t{__completion_info(__signature<_Sigs>)...}; + std::ranges::sort(__compls); + return __compls; + } + + template + constexpr auto __append_range(std::vector<_Ty> &__dst, std::span<_Ty const> __src) + { +#if __cpp_lib_containers_ranges >= 202202L + __dst.append_range(__src); +#else + __dst.insert(__dst.end(), __src.begin(), __src.end()); +#endif + } + } // namespace __cmplsigs +} // namespace STDEXEC diff --git a/include/stdexec/__detail/__completion_signatures.hpp b/include/stdexec/__detail/__completion_signatures.hpp index 2d6fd54b9..cbf612bec 100644 --- a/include/stdexec/__detail/__completion_signatures.hpp +++ b/include/stdexec/__detail/__completion_signatures.hpp @@ -541,38 +541,50 @@ namespace STDEXEC __cmplsigs::__partitions_of_t<_Sigs>::__count_stopped::value; } // namespace __detail - // Below is the definition of the STDEXEC_COMPLSIGS_LET portability macro. It - // is used to check that an expression's type is a valid completion_signature - // specialization. + // Below is the definition of the STDEXEC_TRY_LET portability macro. It is used to check + // that an expression's type is not an __mexception type. // // USAGE: // - // STDEXEC_COMPLSIGS_LET(__cs, ) + // STDEXEC_TRY_LET([constexpr]opt auto c, ) // { - // // __cs is guaranteed to be a specialization of completion_signatures. + // // c is guaranteed to not be an instantiation of __mexception. // } // - // When constexpr exceptions are available (C++26), the macro simply expands to - // the moral equivalent of: + // When constexpr exceptions are available (C++26), the macro simply expands to the + // moral equivalent of: // // // With constexpr exceptions: - // auto __cs = ; // throws if __cs is not a completion_signatures + // auto c = ; // throws if c is an __mexception type // // When constexpr exceptions are not available, the macro expands to: // // // Without constexpr exceptions: - // if constexpr (auto __cs = ; !__valid_completion_signatures) + // if constexpr (auto c = ; __merror) // { - // return __cs; + // return c; // } // else #if STDEXEC_NO_STDCPP_CONSTEXPR_EXCEPTIONS() -# define STDEXEC_COMPLSIGS_LET(_ID, ...) \ - if constexpr ([[maybe_unused]] auto _ID = __VA_ARGS__; \ - !STDEXEC::__valid_completion_signatures) { \ - return _ID; \ +# define STDEXEC_EAT_AUTO_auto +# define STDEXEC_EAT_CONSTEXPR_constexpr +# define STDEXEC_EAT_CONSTEXPR(_ID) STDEXEC_EAT_CONSTEXPR_ ## _ID + +# define STDEXEC_PROBE_CONSTEXPR_constexpr STDEXEC_PP_PROBE(~, 1) +# define STDEXEC_TRY_LET_ID(_ID) \ + STDEXEC_PP_CAT( \ + STDEXEC_EAT_AUTO_, \ + STDEXEC_PP_IIF( \ + STDEXEC_PP_CHECK(STDEXEC_PROBE_CONSTEXPR_ ## _ID), \ + STDEXEC_EAT_CONSTEXPR, \ + STDEXEC_PP_EXPAND)(_ID)) + +# define STDEXEC_TRY_LET(_ID, ...) \ + if constexpr ([[maybe_unused]] _ID = __VA_ARGS__; __merror) \ + { \ + return STDEXEC_TRY_LET_ID(_ID); \ } else template @@ -591,8 +603,9 @@ namespace STDEXEC #else // ^^^ no constexpr exceptions ^^^ / vvv constexpr exceptions vvv -# define STDEXEC_COMPLSIGS_LET(_ID, ...) \ - if constexpr ([[maybe_unused]] auto _ID = __VA_ARGS__; false) { \ +# define STDEXEC_TRY_LET(_ID, ...) \ + if constexpr ([[maybe_unused]] _ID = __VA_ARGS__; false) \ + { \ } else template diff --git a/include/stdexec/__detail/__concepts.hpp b/include/stdexec/__detail/__concepts.hpp index f4de37fb1..21e235857 100644 --- a/include/stdexec/__detail/__concepts.hpp +++ b/include/stdexec/__detail/__concepts.hpp @@ -252,6 +252,16 @@ namespace STDEXEC } // namespace __std + template + concept __initializable_from = requires(__declfn_t<_As &&>... __as) { + { _Ty{__as()...} }; + }; + + template + concept __nothrow_initializable_from = requires(__declfn_t<_As &&>... __as) { + { _Ty{__as()...} } noexcept; + }; + template concept __movable_value = __std::move_constructible<__decay_t<_Ty>> && __std::constructible_from<__decay_t<_Ty>, _Ty>; diff --git a/include/stdexec/__detail/__continues_on.hpp b/include/stdexec/__detail/__continues_on.hpp index b87c400f8..e0498c53c 100644 --- a/include/stdexec/__detail/__continues_on.hpp +++ b/include/stdexec/__detail/__continues_on.hpp @@ -318,8 +318,8 @@ namespace STDEXEC template static consteval auto __get_child_completions() { - STDEXEC_COMPLSIGS_LET(__child_completions, - STDEXEC::get_completion_signatures<_Child, __fwd_env_t<_Env>...>()) + STDEXEC_TRY_LET(auto __child_completions, + STDEXEC::get_completion_signatures<_Child, __fwd_env_t<_Env>...>()) { // continues_on has the completions of the child sender, but with value and // error result types decayed. @@ -334,8 +334,8 @@ namespace STDEXEC static consteval auto __get_scheduler_completions() { using __sndr_t = schedule_result_t<_Scheduler>; - STDEXEC_COMPLSIGS_LET(__sched_completions, - STDEXEC::get_completion_signatures<__sndr_t, __fwd_env_t<_Env>...>()) + STDEXEC_TRY_LET(auto __sched_completions, + STDEXEC::get_completion_signatures<__sndr_t, __fwd_env_t<_Env>...>()) { // The scheduler contributes only error and stopped completions; we ignore value // completions here diff --git a/include/stdexec/__detail/__cpo.hpp b/include/stdexec/__detail/__cpo.hpp deleted file mode 100644 index bbe2ae8b4..000000000 --- a/include/stdexec/__detail/__cpo.hpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (c) 2024 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" -#include "__execution_fwd.hpp" - -#if STDEXEC_MSVC() -# pragma deprecated(STDEXEC_CUSTOM) -# pragma deprecated(STDEXEC_MEMFN_DECL) -#endif - -/////////////////////////////////////////////////////////////////////////////// -/// To hook a customization point like STDEXEC::get_env, first bring the names -/// in STDEXEC::tags into scope: -/// -/// @code -/// using namespace STDEXEC::tags; -/// @endcode -/// -/// Then define a member function like this: -/// -/// @code -/// STDEXEC_MEMFN_DECL(auto get_env)(this const MySender& self) { -/// return ...; -/// } -/// @endcode -#define STDEXEC_MEMFN_DECL_IMPL(...) \ - friend STDEXEC_PP_EVAL( \ - STDEXEC_MEMFN_DECL_TAG_INVOKE, STDEXEC_MEMFN_DECL_WHICH(__VA_ARGS__), __VA_ARGS__) - -#define STDEXEC_MEMFN_DECL_WHICH(_A1, ...) \ - STDEXEC_PP_CAT(STDEXEC_MEMFN_DECL_WHICH_, STDEXEC_PP_FRONT(__VA_OPT__(1, ) 0))(_A1, __VA_ARGS__) -#define STDEXEC_MEMFN_DECL_WHICH_0(_A1, ...) \ - STDEXEC_PP_CHECK(STDEXEC_MEMFN_DECL_PROBE_##_A1), STDEXEC_PP_CHECK(_A1##_STDEXEC_MEMFN_DECL_PROBE) -#define STDEXEC_MEMFN_DECL_WHICH_1(_A1, ...) \ - 0, STDEXEC_PP_CHECK(STDEXEC_PP_CAT(STDEXEC_PP_BACK(__VA_ARGS__), _STDEXEC_MEMFN_DECL_PROBE)) - -#define STDEXEC_MEMFN_DECL_TAG_INVOKE(_WHICH, _QUERY, ...) \ - STDEXEC_MEMFN_DECL_RETURN_ ## _WHICH(__VA_ARGS__) \ - __tag_invoke(STDEXEC_MEMFN_DECL_TAG_ ## _WHICH ## _QUERY(__VA_ARGS__) - -#define STDEXEC_MEMFN_DECL_ARGS(...) \ - STDEXEC_PP_CAT(STDEXEC_EAT_THIS_, __VA_ARGS__)) - -#define STDEXEC_MEMFN_DECL_QUERY(_SELF, _TAG, ...) \ - _TAG, STDEXEC_PP_CAT(STDEXEC_EAT_THIS_, _SELF) __VA_OPT__(, __VA_ARGS__)) - -#define STDEXEC_EAT_THIS_this -#define STDEXEC_EAT_AUTO_auto -#define STDEXEC_EAT_VOID_void - -#define query_STDEXEC_MEMFN_DECL_PROBE STDEXEC_PP_PROBE(~, 1) -#define STDEXEC_MEMFN_DECL_PROBE_auto STDEXEC_PP_PROBE(~, 1) -#define STDEXEC_MEMFN_DECL_PROBE_void STDEXEC_PP_PROBE(~, 2) - -#define STDEXEC_MEMFN_DECL_RETURN_0(...) ::STDEXEC::__arg_type_t -#define STDEXEC_MEMFN_DECL_RETURN_1(...) auto -#define STDEXEC_MEMFN_DECL_RETURN_2(...) void - -#define STDEXEC_MEMFN_DECL_TAG_00(...) \ - const ::STDEXEC::__tag_type_t<__VA_ARGS__##_t::*>&, STDEXEC_MEMFN_DECL_ARGS -#define STDEXEC_MEMFN_DECL_TAG_10(...) \ - const STDEXEC_EAT_AUTO_##__VA_ARGS__##_t&, STDEXEC_MEMFN_DECL_ARGS -#define STDEXEC_MEMFN_DECL_TAG_20(...) \ - const STDEXEC_EAT_VOID_##__VA_ARGS__##_t&, STDEXEC_MEMFN_DECL_ARGS -#define STDEXEC_MEMFN_DECL_TAG_01(...) STDEXEC_MEMFN_DECL_QUERY -#define STDEXEC_MEMFN_DECL_TAG_11(...) STDEXEC_MEMFN_DECL_QUERY -#define STDEXEC_MEMFN_DECL_TAG_21(...) STDEXEC_MEMFN_DECL_QUERY - -#define STDEXEC_MEMFN_FRIEND(_TAG) \ - using STDEXEC_PP_CAT(_TAG, _t) = STDEXEC_PP_CAT(STDEXEC::_TAG, _t) - -#if STDEXEC_GCC() || (STDEXEC_CLANG() && STDEXEC_CLANG_VERSION < 1400) -# define STDEXEC_CUSTOM \ - _Pragma("GCC warning \"STDEXEC_CUSTOM is deprecated.\"") STDEXEC_MEMFN_DECL_IMPL -# define STDEXEC_MEMFN_DECL \ - _Pragma("GCC warning \"STDEXEC_MEMFN_DECL is deprecated.\"") STDEXEC_MEMFN_DECL_IMPL -#else -# define STDEXEC_CUSTOM STDEXEC_MEMFN_DECL_IMPL -# define STDEXEC_MEMFN_DECL STDEXEC_MEMFN_DECL_IMPL -#endif - -#if STDEXEC_CLANG() && STDEXEC_CLANG_VERSION >= 1400 -# pragma clang deprecated(STDEXEC_CUSTOM) -# pragma clang deprecated(STDEXEC_MEMFN_DECL) -#endif - -namespace STDEXEC -{ - template - struct __arg_type; - - template - struct __arg_type - { - using type = _Arg; - }; - - template - using __arg_type_t = __arg_type<_Fn>::type; - - template - struct __tag_type; - - template - struct __tag_type<_Ret _Tag::*> - { - using type = _Tag; - }; - - template - using __tag_type_t = __tag_type<_Fn>::type; - - namespace tags - { - using STDEXEC::set_value_t; - using STDEXEC::set_error_t; - using STDEXEC::set_stopped_t; - using STDEXEC::connect_t; - using STDEXEC::start_t; - using STDEXEC::get_env_t; - using STDEXEC::get_completion_signatures_t; - } // namespace tags -} // namespace STDEXEC diff --git a/include/stdexec/__detail/__diagnostics.hpp b/include/stdexec/__detail/__diagnostics.hpp index 485a67625..fe2f67bf6 100644 --- a/include/stdexec/__detail/__diagnostics.hpp +++ b/include/stdexec/__detail/__diagnostics.hpp @@ -267,15 +267,22 @@ namespace STDEXEC "complete."} {} - STDEXEC_ATTRIBUTE(host, device) constexpr auto operator+() const -> _ERROR_; + STDEXEC_ATTRIBUTE(host, device) + constexpr auto operator+() const -> _ERROR_; template STDEXEC_ATTRIBUTE(host, device) - constexpr auto operator,(_Ty const &) const -> _ERROR_; + constexpr auto operator,(_Ty const &) const -> _ERROR_ + { + return *this; + } template STDEXEC_ATTRIBUTE(host, device) - constexpr auto operator,(const _ERROR_ &) const -> _ERROR_; + constexpr auto operator,(const _ERROR_ &__other) const -> _ERROR_ + { + return __other; + } }; // By making __dependent_sender_error_t an alias for _ERROR_<...>, we ensure that diff --git a/include/stdexec/__detail/__domain.hpp b/include/stdexec/__detail/__domain.hpp index 2d49145c5..096671ccc 100644 --- a/include/stdexec/__detail/__domain.hpp +++ b/include/stdexec/__detail/__domain.hpp @@ -18,10 +18,8 @@ #include "__execution_fwd.hpp" #include "__completion_behavior.hpp" -#include "__completion_signatures_of.hpp" #include "__concepts.hpp" #include "__config.hpp" -#include "__execution_fwd.hpp" #include "__meta.hpp" #include "__sender_introspection.hpp" #include "__utility.hpp" @@ -171,12 +169,6 @@ namespace STDEXEC } // namespace __detail //////////////////////////////////////////////////////////////////////////////////////////////// - template - using __completion_domain_t = __call_result_or_t, - indeterminate_domain<>, - _Attrs, - _Env const &...>; - template requires __sends<_Tag, _Sender, _Env...> using __completion_domain_of_t = __completion_domain_t<_Tag, env_of_t<_Sender>, _Env const &...>; diff --git a/include/stdexec/__detail/__execution_fwd.hpp b/include/stdexec/__detail/__execution_fwd.hpp index 38e8408c7..031b5a5a0 100644 --- a/include/stdexec/__detail/__execution_fwd.hpp +++ b/include/stdexec/__detail/__execution_fwd.hpp @@ -74,6 +74,15 @@ namespace STDEXEC extern set_error_t const set_error; extern set_stopped_t const set_stopped; + enum class __disposition + { + __value, + __error, + __stopped + }; + + constexpr auto __invalid_disposition = static_cast<__disposition>(3); // invalid value + template concept __completion_tag = __one_of<_Tag, set_value_t, set_error_t, set_stopped_t>; @@ -135,6 +144,12 @@ namespace STDEXEC extern get_domain_t const get_domain; extern get_await_completion_adaptor_t const get_await_completion_adaptor; + template + using __completion_domain_t = __call_result_or_t, + indeterminate_domain<>, + _Attrs, + _Env const &...>; + template concept __is_debug_env = __callable<__debug_env_t, _Env>; @@ -184,17 +199,20 @@ namespace STDEXEC #if STDEXEC_NO_STDCPP_CONSTEXPR_EXCEPTIONS() template + [[nodiscard]] consteval auto __throw_compile_time_error(_Values...) -> __mexception<_What...>; #else // ^^^ no constexpr exceptions ^^^ / vvv constexpr exceptions vvv // C++26, https://wg21.link/p3068 template + [[noreturn, nodiscard]] consteval auto __throw_compile_time_error(_Values...) -> completion_signatures<>; #endif // ^^^ constexpr exceptions ^^^ template + [[nodiscard]] consteval auto __throw_compile_time_error(__mexception<_What...>); ////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/stdexec/__detail/__finally.hpp b/include/stdexec/__detail/__finally.hpp index 45d06d572..a517d6f1c 100644 --- a/include/stdexec/__detail/__finally.hpp +++ b/include/stdexec/__detail/__finally.hpp @@ -307,12 +307,12 @@ namespace STDEXEC template consteval auto __get_completion_signatures() { - STDEXEC_COMPLSIGS_LET(__initial_completions, - get_completion_signatures<_CvInitialSender, _Env...>()) + STDEXEC_TRY_LET(auto __initial_completions, + get_completion_signatures<_CvInitialSender, _Env...>()) { using __initial_completions_t = decltype(__initial_completions); - STDEXEC_COMPLSIGS_LET( - __final_completions, + STDEXEC_TRY_LET( + auto __final_completions, get_completion_signatures<_CvFinalSender, __mk_final_env_t<_CvInitialSender, _Env>...>()) { if constexpr (__never_sends + namespace STDEXEC { namespace __detail @@ -381,4 +384,15 @@ namespace STDEXEC template using __count_of = __msize_t<__detail::__count_of<_Tag, __completion_signatures_of_t<_Sender, _Env...>>>; + + template + consteval auto __get_completion_info() + { + STDEXEC_TRY_LET(auto __cmplsigs, STDEXEC::get_completion_signatures<_Sender, _Env...>()) + { + auto __cmplinfo = STDEXEC::__cmplsigs::__to_array(__cmplsigs); + std::ranges::for_each(__cmplinfo, &__completion_info::__populate<_Sender, _Env...>); + return __cmplinfo; + } + } } // namespace STDEXEC diff --git a/include/stdexec/__detail/__let.hpp b/include/stdexec/__detail/__let.hpp index 1c1d89fe2..0ba825f9f 100644 --- a/include/stdexec/__detail/__let.hpp +++ b/include/stdexec/__detail/__let.hpp @@ -19,6 +19,7 @@ // include these after __execution_fwd.hpp #include "__basic_sender.hpp" +#include "__completion_info.hpp" #include "__diagnostics.hpp" #include "__domain.hpp" #include "__env.hpp" @@ -27,7 +28,6 @@ #include "__sender_adaptor_closure.hpp" #include "__senders.hpp" #include "__submit.hpp" -#include "__transform_completion_signatures.hpp" #include "__utility.hpp" #include "__variant.hpp" @@ -106,6 +106,12 @@ namespace STDEXEC __fn_t<_WITH_ENVIRONMENT_, _JoinEnv2>..., __mapply_q<_NESTED_ERROR_, __try_completion_signatures_of_t<_Sender, _JoinEnv2...>>>; + template + using __not_decay_copyable_error_t = + __mexception<_WHAT_(_SENDER_RESULTS_ARE_NOT_DECAY_COPYABLE_), + _WHERE_(_IN_ALGORITHM_, _LetTag), + _WITH_ARGUMENTS_(_Args...)>; + template concept __potentially_valid_sender_in = sender_in<_Sender, _JoinEnv2...> || (sender<_Sender> && (sizeof...(_JoinEnv2) == 0)); @@ -132,7 +138,8 @@ namespace STDEXEC using __rcvr2_t = __receiver_archetype<_Env2>; template - using __f = __mbool<__nothrow_decay_copyable<_Ts...> + using __f = __mbool<__nothrow_decay_copyable<_Ts...> // + && __nothrow_invocable<_Fn, __decay_t<_Ts>&...> // && __nothrow_connectable<__sndr2_t<_Ts...>, __rcvr2_t>>; }; @@ -514,111 +521,143 @@ namespace STDEXEC struct __impls : __sexpr_defaults { private: - using __set_t = __t<_LetTag>; - template - using __fn_t = __decay_t<__data_of<_Sender>>; + using __set_t = __t<_LetTag>; + using __cmpl_vec_t = std::vector<__completion_info>; + using __eptr_sig_t = set_error_t(std::exception_ptr); + + template + using __fn_t = __decay_t<__data_of<_CvSender>>; + + template + using __env2_t = __let::__result_env_t<__set_t, _CvSender, _Env>; - template + template + using __rcvr2_t = __receiver_archetype<__env2_t<_CvSender, _Env>>; + + template using __opstate_t = __gather_completions_of_t< - __t<_LetTag>, - __child_of<_Sender>, + __set_t, + __child_of<_CvSender>, __fwd_env_t>, __q<__decayed_tuple>, - __mbind_front_q<__opstate, __t<_LetTag>, __child_of<_Sender>, __fn_t<_Sender>, _Receiver>>; + __mbind_front_q<__opstate, __set_t, __child_of<_CvSender>, __fn_t<_CvSender>, _Receiver>>; - template - using __not_decay_copyable_error_t = - __mexception<_WHAT_(_SENDER_RESULTS_ARE_NOT_DECAY_COPYABLE_), - _WHERE_(_IN_ALGORITHM_, _LetTag), - _WITH_ARGUMENTS_(_Args...)>; - - public: - static constexpr auto __get_attrs = - [](__ignore, __ignore, _Child const & __child) noexcept -> decltype(auto) + template + static constexpr auto __transform_cmplsig = // + [](__set_t (*)(_As...), __completion_info __info, __cmpl_vec_t& __out) // + -> decltype(auto) { - // TODO(ericniebler): this needs a proper implementation - return __fwd_env(STDEXEC::get_env(__child)); - }; - - template - static consteval auto __get_completion_signatures() - { - static_assert(__sender_for<_CvSndr, _LetTag>); - using __fn_t = __impls::__fn_t<_CvSndr>; - using __child_t = __child_of<_CvSndr>; - auto __completions = STDEXEC::get_completion_signatures<__child_t, _Env...>(); - auto __transform_fn = []() + if constexpr (!__decay_copyable<_As...>) { - if constexpr (!__invocable<__fn_t, __decay_t<_Args>&...>) - { - using __what_t = __callable_error_t<_LetTag, __fn_t, __decay_t<_Args>&...>; - return STDEXEC::__throw_compile_time_error(__what_t()); - } - else if constexpr (!__decay_copyable<_Args...>) - { - using __what_t = __not_decay_copyable_error_t<_Args...>; - return STDEXEC::__throw_compile_time_error(__what_t()); - } - else if constexpr (!__potentially_valid_sender_in< - __invoke_result_t<__fn_t, __decay_t<_Args>&...>, - __result_env_t<__set_t, __child_t, _Env>...>) - { - using __sndr_t = __invoke_result_t<__fn_t, __decay_t<_Args>&...>; - using __what_t = - __bad_result_sender<__sndr_t, _LetTag, __result_env_t<__set_t, __child_t, _Env>...>; - return STDEXEC::__throw_compile_time_error(__what_t()); - } - else + using __what_t = __not_decay_copyable_error_t<_LetTag, _As...>; + return STDEXEC::__throw_compile_time_error(__what_t()); + } + else if constexpr (!__invocable<_Fun, __decay_t<_As>&...>) + { + using __what_t = __callable_error_t<_LetTag, _Fun, __decay_t<_As>&...>; + return STDEXEC::__throw_compile_time_error(__what_t()); + } + else if constexpr (!__potentially_valid_sender_in< + __invoke_result_t<_Fun, __decay_t<_As>&...>, + __env2_t<_Child, _Env>...>) + { + using __sndr_t = __invoke_result_t<_Fun, __decay_t<_As>&...>; + using __what_t = __bad_result_sender<__sndr_t, _LetTag, __env2_t<_Child, _Env>...>; + return STDEXEC::__throw_compile_time_error(__what_t()); + } + else + { + using __sndr2_t = __invoke_result_t<_Fun, __decay_t<_As>&...>; + STDEXEC_TRY_LET(constexpr auto __cmpls, + STDEXEC::__get_completion_info<__sndr2_t, __env2_t<_Child, _Env>...>()) { - using __sndr_t = __invoke_result_t<__fn_t, __decay_t<_Args>&...>; - using __nothrow_t = __mbool<__nothrow_invocable<__fn_t, __decay_t<_Args>&...> - && __nothrow_decay_copyable<_Args...>>; - - STDEXEC_COMPLSIGS_LET( - __sigs, - STDEXEC::__concat_completion_signatures( - STDEXEC::get_completion_signatures<__sndr_t, - __result_env_t<__set_t, __child_t, _Env>...>(), - __eptr_completion_unless_t<__nothrow_t>())) + __cmplsigs::__append_range(__out, std::span{__cmpls}); + + if constexpr (!__nothrow_decay_copyable<_As...> + || !__nothrow_invocable<_Fun, __decay_t<_As>&...> + || (!__nothrow_connectable<__sndr2_t, __rcvr2_t<_Child, _Env>> || ...)) { - if constexpr (__sigs.template __contains()) - { - return __sigs; - } - else if constexpr (sizeof...(_Env) == 0) - { - return STDEXEC::__dependent_sender<__sndr_t>(); - } - else - { - using __env_t = __mfront<__result_env_t<__set_t, __child_t, _Env>...>; - using __rcvr_t = __receiver_archetype<__env_t>; - using __nothrow_t = __mbool<__nothrow_connectable<__sndr_t, __rcvr_t>>; - using __eptr_t = __eptr_completion_unless_t<__nothrow_t>; - - return STDEXEC::__concat_completion_signatures(__sigs, __eptr_t()); - } + __out.emplace_back(__signature<__eptr_sig_t>, __info.__domain, __info.__behavior); } + + return (__out); } - }; + } + }; - if constexpr (!__decay_copyable<_CvSndr>) + template <__completion_info _Info> + static constexpr auto __maybe_transform_cmplsig = + [](auto __transform, __cmpl_vec_t& __out) -> decltype(auto) + { + if constexpr (_Info.__disposition != __set_t::__disposition) { - return STDEXEC::__throw_compile_time_error<_SENDER_TYPE_IS_NOT_DECAY_COPYABLE_, - _WITH_PRETTY_SENDER_<_CvSndr>>(); + __out.push_back(_Info); + return (__out); } - else if constexpr (__same_as<_LetTag, let_value_t>) + else { - return STDEXEC::__transform_completion_signatures(__completions, __transform_fn); + using __sig_t = __mtypeof<_Info.__signature>; + return __transform(__signature<__sig_t>, _Info, __out); } - else if constexpr (__same_as<_LetTag, let_error_t>) + }; + + //! @tparam _Info A `__static_vector` of `__completion_info` objects representing + //! the completions of the predecessor sender. + template + static constexpr auto __get_cmpl_info_i = [](__indices<_Is...>) + { + return [] { - return STDEXEC::__transform_completion_signatures(__completions, {}, __transform_fn); - } - else + std::vector<__completion_info> __result; + // NB: this fold uses an overloaded comma operator that propagates __mexception + // objects when constexpr exceptions are not available. + return (__maybe_transform_cmplsig<_Info[_Is]>(_Transform, __result), ..., __result); + }; + }; + + template + static constexpr auto __get_cmpl_info = [] + { + constexpr auto __transform = __transform_cmplsig<_Fun, _Child, _Env...>; + constexpr auto __get_sig = &__completion_info::__signature; + constexpr auto __eptr_sig_id = __mtypeid<__eptr_sig_t>; + + STDEXEC_TRY_LET(constexpr auto __cmpls, STDEXEC::__get_completion_info<_Child, _Env...>()) { - return STDEXEC::__transform_completion_signatures(__completions, {}, {}, __transform_fn); + constexpr auto __idx = __make_indices<__cmpls.size()>(); + constexpr auto __get_cmpls2 = __get_cmpl_info_i<__cmpls, __transform>(__idx); + + STDEXEC_TRY_LET(constexpr auto __cmpls2, __cmplsigs::__completion_info_from(__get_cmpls2)) + { + if constexpr (std::ranges::find(__cmpls2, __eptr_sig_id, __get_sig) == __cmpls2.end() + && sizeof...(_Env) == 0) + return STDEXEC::__dependent_sender<_Child>(); + else + return __cmpls2; + } } + }; + + public: + static constexpr auto __get_attrs = + [](__ignore, __ignore, _Child const & __child) noexcept -> decltype(auto) + { + // TODO(ericniebler): this needs a proper implementation + return __fwd_env(STDEXEC::get_env(__child)); + }; + + template + static consteval auto __get_completion_signatures() + { + static_assert(__sender_for<_CvSender, _LetTag>); + constexpr auto __get_cmpl_info = + __impls::__get_cmpl_info<__fn_t<_CvSender>, __child_of<_CvSender>, _Env...>; + + if constexpr (!__decay_copyable<_CvSender>) + return STDEXEC::__throw_compile_time_error<_SENDER_TYPE_IS_NOT_DECAY_COPYABLE_, + _WITH_PRETTY_SENDER_<_CvSender>>(); + else + return __cmplsigs::__completion_sigs_from(__get_cmpl_info); } static constexpr auto __connect = diff --git a/include/stdexec/__detail/__meta.hpp b/include/stdexec/__detail/__meta.hpp index 9623bfcbb..1f4b595e8 100644 --- a/include/stdexec/__detail/__meta.hpp +++ b/include/stdexec/__detail/__meta.hpp @@ -51,12 +51,17 @@ namespace STDEXEC namespace __detail { // NB: This variable template is partially specialized for __type_index in __typeinfo.hpp: +#if STDEXEC_GCC() && STDEXEC_GCC_VERSION < 1300 template - extern __fn_ptr_t __mtypeof_v; + extern __mtype> __mtypeof_v; +#else + template + extern __mtype __mtypeof_v; +#endif } // namespace __detail template - using __mtypeof = decltype(__detail::__mtypeof_v<_Value>()); + using __mtypeof = decltype(__detail::__mtypeof_v<_Value>)::__t; template struct __mlist; @@ -203,7 +208,10 @@ namespace STDEXEC constexpr auto operator+() const -> _ERROR_; STDEXEC_ATTRIBUTE(host, device) - constexpr auto operator,(__ignore) const -> _ERROR_; + constexpr auto operator,(__ignore) const -> _ERROR_ + { + return *this; + } }; template @@ -869,19 +877,19 @@ namespace STDEXEC namespace __detail { template - extern __declfn_t<_Ty> __demangle_v; + extern __mtype<_Ty> __demangle_v; template - extern __declfn_t<_Ty &> __demangle_v<_Ty &>; + extern __mtype<_Ty &> __demangle_v<_Ty &>; template - extern __declfn_t<_Ty &&> __demangle_v<_Ty &&>; + extern __mtype<_Ty &&> __demangle_v<_Ty &&>; template - extern __declfn_t<_Ty const &> __demangle_v<_Ty const &>; + extern __mtype<_Ty const &> __demangle_v<_Ty const &>; template - using __demangle_t = decltype(__demangle_v<_Ty>()); + using __demangle_t = decltype(__demangle_v<_Ty>)::__t; } // namespace __detail // A utility for pretty-printing type names in diagnostics diff --git a/include/stdexec/__detail/__receivers.hpp b/include/stdexec/__detail/__receivers.hpp index d67dd8ff5..5650273b9 100644 --- a/include/stdexec/__detail/__receivers.hpp +++ b/include/stdexec/__detail/__receivers.hpp @@ -28,13 +28,6 @@ namespace STDEXEC { - enum class __disposition - { - __value, - __error, - __stopped - }; - namespace __detail { template <__disposition _Disposition> diff --git a/include/stdexec/__detail/__sequence.hpp b/include/stdexec/__detail/__sequence.hpp index ff9c37a5e..9ba8081a7 100644 --- a/include/stdexec/__detail/__sequence.hpp +++ b/include/stdexec/__detail/__sequence.hpp @@ -499,7 +499,7 @@ namespace STDEXEC namespace __detail { template - extern __declfn_t<__seq::__sndr<__demangle_t<_Senders>...>> + extern __mtype<__seq::__sndr<__demangle_t<_Senders>...>> __demangle_v<__seq::__sndr<_Senders...>>; } // namespace __detail } // namespace STDEXEC diff --git a/include/stdexec/__detail/__static_vector.hpp b/include/stdexec/__detail/__static_vector.hpp new file mode 100644 index 000000000..49ec55fa9 --- /dev/null +++ b/include/stdexec/__detail/__static_vector.hpp @@ -0,0 +1,120 @@ +/* + * 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 "__execution_fwd.hpp" + +#include "__concepts.hpp" + +#include +#include +#include + +namespace STDEXEC +{ + template + struct __static_vector + { + using value_type = _Tp; + using iterator = value_type *; + using const_iterator = value_type const *; + + __static_vector() = default; + + constexpr __static_vector(std::initializer_list __init) + noexcept(__nothrow_copy_constructible) + { + auto const __end = std::copy_n(__init.begin(), (std::min) (__init.size(), _Size), __data_); + __size_ = __end - __data_; + } + + [[nodiscard]] + constexpr auto operator[](std::size_t __i) noexcept -> value_type & + { + return __data_[__i]; + } + + [[nodiscard]] + constexpr auto operator[](std::size_t __i) const noexcept -> value_type const & + { + return __data_[__i]; + } + + [[nodiscard]] + constexpr auto begin() noexcept -> iterator + { + return __data_; + } + + [[nodiscard]] + constexpr auto begin() const noexcept -> const_iterator + { + return __data_; + } + + [[nodiscard]] + constexpr auto end() noexcept -> iterator + { + return __data_ + __size_; + } + + [[nodiscard]] + constexpr auto end() const noexcept -> const_iterator + { + return __data_ + __size_; + } + + [[nodiscard]] + constexpr auto size() const noexcept -> std::size_t + { + return __size_; + } + + [[nodiscard]] + static constexpr auto capacity() noexcept -> std::size_t + { + return _Size; + } + + constexpr void resize(std::size_t __new_size) noexcept + { + __size_ = __new_size; + } + + constexpr auto erase(const_iterator __first, const_iterator __last) noexcept -> iterator + { + std::move(__last, end(), const_cast(__first)); + resize(size() - (__last - __first)); + return end(); + } + + std::size_t __size_ = 0; + value_type __data_[_Size]; + }; + + template + [[nodiscard]] + constexpr auto __concat(__static_vector<_Tp, _Size0> const &__lhs, // + __static_vector<_Tp, _Size1> const &__rhs) + noexcept(__nothrow_copy_constructible<_Tp>) -> __static_vector<_Tp, _Size0 + _Size1> + { + __static_vector<_Tp, _Size0 + _Size1> __result; + std::copy(__lhs.begin(), __lhs.end(), __result.begin()); + std::copy(__rhs.begin(), __rhs.end(), __result.begin() + __lhs.size()); + __result.resize(__lhs.size() + __rhs.size()); + return __result; + } +} // namespace STDEXEC diff --git a/include/stdexec/__detail/__stopped_as_optional.hpp b/include/stdexec/__detail/__stopped_as_optional.hpp index 43e4f49b4..fd867b60a 100644 --- a/include/stdexec/__detail/__stopped_as_optional.hpp +++ b/include/stdexec/__detail/__stopped_as_optional.hpp @@ -66,8 +66,8 @@ namespace STDEXEC static constexpr auto __get_completion_signatures() { static_assert(__sender_for<_Self, stopped_as_optional_t>); - STDEXEC_COMPLSIGS_LET(__completions, - STDEXEC::get_completion_signatures<__child_of<_Self>, _Env...>()) + STDEXEC_TRY_LET(auto __completions, + STDEXEC::get_completion_signatures<__child_of<_Self>, _Env...>()) { using _Completions = decltype(__completions); if constexpr (__single_value_sender<__child_of<_Self>, _Env...>) diff --git a/include/stdexec/__detail/__transform_completion_signatures.hpp b/include/stdexec/__detail/__transform_completion_signatures.hpp index a61b441ff..0910e5272 100644 --- a/include/stdexec/__detail/__transform_completion_signatures.hpp +++ b/include/stdexec/__detail/__transform_completion_signatures.hpp @@ -575,9 +575,9 @@ namespace STDEXEC _StoppedFn __stopped_fn = {}, _ExtraSigs = {}) { - STDEXEC_COMPLSIGS_LET(__completions, _Completions{}) + STDEXEC_TRY_LET(auto __completions, _Completions{}) { - STDEXEC_COMPLSIGS_LET(__extra_sigs, _ExtraSigs{}) + STDEXEC_TRY_LET(auto __extra_sigs, _ExtraSigs{}) { __cmplsigs::__transform_one __tfx1{__value_fn, __error_fn, __stopped_fn}; return __concat_completion_signatures(__completions.__apply( diff --git a/include/stdexec/__detail/__typeinfo.hpp b/include/stdexec/__detail/__typeinfo.hpp index 9fa81172c..e7a78bd8f 100644 --- a/include/stdexec/__detail/__typeinfo.hpp +++ b/include/stdexec/__detail/__typeinfo.hpp @@ -97,8 +97,10 @@ namespace STDEXEC // __type_index struct __type_index { - constexpr __type_index(__type_info const &info) noexcept - : __info_(&info) + __type_index() = default; + + constexpr __type_index(__type_info const &__info) noexcept + : __info_(&__info) {} [[nodiscard]] @@ -108,15 +110,15 @@ namespace STDEXEC } [[nodiscard]] - constexpr bool operator==(__type_index const &other) const noexcept + constexpr bool operator==(__type_index const &__other) const noexcept { - return *__info_ == *other.__info_; + return *__info_ == *__other.__info_; } [[nodiscard]] - constexpr std::strong_ordering operator<=>(__type_index const &other) const noexcept + constexpr std::strong_ordering operator<=>(__type_index const &__other) const noexcept { - return *__info_ <=> *other.__info_; + return *__info_ <=> *__other.__info_; } #if !STDEXEC_NO_STDCPP_TYPEID() @@ -133,7 +135,7 @@ namespace STDEXEC } #endif - __type_info const *__info_; + __type_info const *__info_ = &__detail::__mtypeid_v; }; namespace __detail @@ -167,8 +169,7 @@ namespace STDEXEC // This specialization is what makes __mtypeof< Id > return the type associated with Id. template requires __same_as - extern __fn_t<__t()))>> - *__mtypeof_v<_Index>; + extern decltype(__typeid_lookup(__detail::__mtypeid_key<_Index>())) __mtypeof_v<_Index>; } // namespace __detail // For a given type, return a __type_index object diff --git a/include/stdexec/__detail/__utility.hpp b/include/stdexec/__detail/__utility.hpp index 9de462b55..71d642781 100644 --- a/include/stdexec/__detail/__utility.hpp +++ b/include/stdexec/__detail/__utility.hpp @@ -65,21 +65,18 @@ namespace STDEXEC struct __move_only { - __move_only() = default; - + __move_only() = default; __move_only(__move_only&&) noexcept = default; + __move_only(__move_only const &) = delete; auto operator=(__move_only&&) noexcept -> __move_only& = default; - - __move_only(__move_only const &) = delete; - auto operator=(__move_only const &) -> __move_only& = delete; + auto operator=(__move_only const &) -> __move_only& = delete; }; template using __call_result_t = decltype(__declval<_Fun>()(__declval<_As>()...)); template - using __call_result_or_t = - __mcall<__mtry_catch_q<__call_result_t, __mconst<_Default>>, _Fun, _As...>; + using __call_result_or_t = __minvoke_or_q<__call_result_t, _Default, _Fun, _As...>; // BUGBUG TODO file this bug with nvc++ #if STDEXEC_EDG() diff --git a/include/stdexec/execution.hpp b/include/stdexec/execution.hpp index 5f30676a1..9f5f6a4da 100644 --- a/include/stdexec/execution.hpp +++ b/include/stdexec/execution.hpp @@ -27,7 +27,6 @@ #include "__detail/__connect_awaitable.hpp" #include "__detail/__continues_on.hpp" #include "__detail/__counting_scopes.hpp" -#include "__detail/__cpo.hpp" #include "__detail/__debug.hpp" #include "__detail/__domain.hpp" #include "__detail/__env.hpp" diff --git a/include/stdexec/functional.hpp b/include/stdexec/functional.hpp index 09d0e8ed6..1c8b86e9d 100644 --- a/include/stdexec/functional.hpp +++ b/include/stdexec/functional.hpp @@ -267,12 +267,12 @@ namespace STDEXEC struct __construct_from { template - requires __std::constructible_from<_Ty, _As...> + requires __initializable_from<_Ty, _As...> STDEXEC_ATTRIBUTE(host, device, always_inline) - constexpr auto operator()(_As &&...__as) const noexcept( // - __nothrow_constructible_from<_Ty, _As...>) -> _Ty + constexpr auto + operator()(_As &&...__as) const noexcept(__nothrow_initializable_from<_Ty, _As...>) -> _Ty { - return _Ty(static_cast<_As &&>(__as)...); + return _Ty{static_cast<_As &&>(__as)...}; } }; @@ -363,8 +363,7 @@ namespace STDEXEC constexpr auto __bind_back(_Fn &&__fn, _BoundArgs... __bound_args) noexcept(__nothrow_move_constructible<_BoundArgs...> && __nothrow_decay_copyable<_Fn>) { - return __back_binder<__decay_t<_Fn>, _BoundArgs...>{static_cast<_Fn &&>(__fn), - static_cast<_BoundArgs &&>( - __bound_args)...}; + using __binder_t = __back_binder<__decay_t<_Fn>, _BoundArgs...>; + return __binder_t{static_cast<_Fn &&>(__fn), static_cast<_BoundArgs &&>(__bound_args)...}; }; } // namespace STDEXEC From ebcaaba80f1df2f6a73d0964e2bc83ea596c2ef1 Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Sat, 18 Apr 2026 12:43:57 -0700 Subject: [PATCH 2/8] add `-Wfatal-errors` to CI command line for smaller build logs --- test/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e14a0469d..006e1d6f2 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -18,7 +18,7 @@ check_compiler_flag(CXX -Wno-c2y-extensions HAVE_C2Y_EXTENSIONS_WARNING) function(add_compile_diagnostics TARGET) if(CMAKE_CXX_COMPILER_ID STREQUAL Clang OR CMAKE_CXX_COMPILER_ID STREQUAL GNU) - target_compile_options(${TARGET} PRIVATE -Wall -Wextra -Wpedantic -Werror $<$:-Wno-c2y-extensions>) + target_compile_options(${TARGET} PRIVATE -Wall -Wextra -Wpedantic -Werror -Wfatal-errors $<$:-Wno-c2y-extensions>) endif() endfunction() From 866336b9e27ce989383389b4010f58313d9ad846 Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Sat, 18 Apr 2026 14:33:08 -0700 Subject: [PATCH 3/8] give up trying to use `std::vector` in constant evaluation --- .../stdexec/__detail/__completion_info.hpp | 11 -- include/stdexec/__detail/__let.hpp | 39 +++--- include/stdexec/__detail/__static_vector.hpp | 114 +++++++++++++++--- 3 files changed, 114 insertions(+), 50 deletions(-) diff --git a/include/stdexec/__detail/__completion_info.hpp b/include/stdexec/__detail/__completion_info.hpp index fa793c293..885ed383f 100644 --- a/include/stdexec/__detail/__completion_info.hpp +++ b/include/stdexec/__detail/__completion_info.hpp @@ -30,7 +30,6 @@ #include #include #include -#include // IWYU pragma: end_keep namespace STDEXEC @@ -145,15 +144,5 @@ namespace STDEXEC std::ranges::sort(__compls); return __compls; } - - template - constexpr auto __append_range(std::vector<_Ty> &__dst, std::span<_Ty const> __src) - { -#if __cpp_lib_containers_ranges >= 202202L - __dst.append_range(__src); -#else - __dst.insert(__dst.end(), __src.begin(), __src.end()); -#endif - } } // namespace __cmplsigs } // namespace STDEXEC diff --git a/include/stdexec/__detail/__let.hpp b/include/stdexec/__detail/__let.hpp index 0ba825f9f..960cdaca4 100644 --- a/include/stdexec/__detail/__let.hpp +++ b/include/stdexec/__detail/__let.hpp @@ -522,7 +522,6 @@ namespace STDEXEC { private: using __set_t = __t<_LetTag>; - using __cmpl_vec_t = std::vector<__completion_info>; using __eptr_sig_t = set_error_t(std::exception_ptr); template @@ -543,8 +542,8 @@ namespace STDEXEC __mbind_front_q<__opstate, __set_t, __child_of<_CvSender>, __fn_t<_CvSender>, _Receiver>>; template - static constexpr auto __transform_cmplsig = // - [](__set_t (*)(_As...), __completion_info __info, __cmpl_vec_t& __out) // + static constexpr auto __transform_cmplsig = // + [](__set_t (*)(_As...), __completion_info __info) // -> decltype(auto) { if constexpr (!__decay_copyable<_As...>) @@ -571,34 +570,30 @@ namespace STDEXEC STDEXEC_TRY_LET(constexpr auto __cmpls, STDEXEC::__get_completion_info<__sndr2_t, __env2_t<_Child, _Env>...>()) { - __cmplsigs::__append_range(__out, std::span{__cmpls}); - if constexpr (!__nothrow_decay_copyable<_As...> || !__nothrow_invocable<_Fun, __decay_t<_As>&...> || (!__nothrow_connectable<__sndr2_t, __rcvr2_t<_Child, _Env>> || ...)) { - __out.emplace_back(__signature<__eptr_sig_t>, __info.__domain, __info.__behavior); + __completion_info const __eptr_info(__signature<__eptr_sig_t>, + __info.__domain, + __info.__behavior); + return __cmpls + __static_vector{__eptr_info}; + } + else + { + return __cmpls; } - - return (__out); } } }; template <__completion_info _Info> - static constexpr auto __maybe_transform_cmplsig = - [](auto __transform, __cmpl_vec_t& __out) -> decltype(auto) + static constexpr auto __maybe_transform_cmplsig = [](auto __transform) -> decltype(auto) { if constexpr (_Info.__disposition != __set_t::__disposition) - { - __out.push_back(_Info); - return (__out); - } + return __static_vector{_Info}; else - { - using __sig_t = __mtypeof<_Info.__signature>; - return __transform(__signature<__sig_t>, _Info, __out); - } + return __transform(__signature<__mtypeof<_Info.__signature>>, _Info); }; //! @tparam _Info A `__static_vector` of `__completion_info` objects representing @@ -608,10 +603,10 @@ namespace STDEXEC { return [] { - std::vector<__completion_info> __result; - // NB: this fold uses an overloaded comma operator that propagates __mexception - // objects when constexpr exceptions are not available. - return (__maybe_transform_cmplsig<_Info[_Is]>(_Transform, __result), ..., __result); + __static_vector<__completion_info, 0> __result; + // NB: this fold uses an overloaded addition operator that propagates + // __mexception objects when constexpr exceptions are not available. + return (__maybe_transform_cmplsig<_Info[_Is]>(_Transform) + ... + __result); }; }; diff --git a/include/stdexec/__detail/__static_vector.hpp b/include/stdexec/__detail/__static_vector.hpp index 49ec55fa9..13af22e95 100644 --- a/include/stdexec/__detail/__static_vector.hpp +++ b/include/stdexec/__detail/__static_vector.hpp @@ -25,8 +25,48 @@ namespace STDEXEC { - template - struct __static_vector + template + struct __static_vector; + + namespace __detail + { + struct __static_vector_base + { + template + [[nodiscard]] + friend constexpr auto operator+(__mexception<_What...>, // + __static_vector<_Tp, _Capacity> const &) noexcept // + -> __mexception<_What...> + { + return {}; + } + + template + [[nodiscard]] + friend constexpr auto operator+(__static_vector<_Tp, _Capacity> const &, // + __mexception<_What...>) noexcept // + -> __mexception<_What...> + { + return {}; + } + + template + [[nodiscard]] + friend constexpr auto operator+(__static_vector<_Ty, _Capacity0> const &__lhs, // + __static_vector<_Ty, _Capacity1> const &__rhs) + noexcept(__nothrow_copy_constructible<_Ty>) -> __static_vector<_Ty, _Capacity0 + _Capacity1> + { + __static_vector<_Ty, _Capacity0 + _Capacity1> __result; + std::copy(__lhs.begin(), __lhs.end(), __result.begin()); + std::copy(__rhs.begin(), __rhs.end(), __result.begin() + __lhs.size()); + __result.resize(__lhs.size() + __rhs.size()); + return __result; + } + }; + } // namespace __detail + + template + struct __static_vector : __detail::__static_vector_base { using value_type = _Tp; using iterator = value_type *; @@ -37,8 +77,9 @@ namespace STDEXEC constexpr __static_vector(std::initializer_list __init) noexcept(__nothrow_copy_constructible) { - auto const __end = std::copy_n(__init.begin(), (std::min) (__init.size(), _Size), __data_); - __size_ = __end - __data_; + auto const __count = (std::min) (__init.size(), _Capacity); + auto const __end = std::ranges::copy_n(__init.begin(), __count, __data_).out; + __size_ = __end - __data_; } [[nodiscard]] @@ -86,7 +127,7 @@ namespace STDEXEC [[nodiscard]] static constexpr auto capacity() noexcept -> std::size_t { - return _Size; + return _Capacity; } constexpr void resize(std::size_t __new_size) noexcept @@ -102,19 +143,58 @@ namespace STDEXEC } std::size_t __size_ = 0; - value_type __data_[_Size]; + value_type __data_[_Capacity]; }; - template - [[nodiscard]] - constexpr auto __concat(__static_vector<_Tp, _Size0> const &__lhs, // - __static_vector<_Tp, _Size1> const &__rhs) - noexcept(__nothrow_copy_constructible<_Tp>) -> __static_vector<_Tp, _Size0 + _Size1> + // Specialization of __static_vector for zero capacity that doesn't require default + // constructibility of _Tp. + template + struct __static_vector<_Tp, 0> : __detail::__static_vector_base { - __static_vector<_Tp, _Size0 + _Size1> __result; - std::copy(__lhs.begin(), __lhs.end(), __result.begin()); - std::copy(__rhs.begin(), __rhs.end(), __result.begin() + __lhs.size()); - __result.resize(__lhs.size() + __rhs.size()); - return __result; - } + using value_type = _Tp; + using iterator = value_type *; + using const_iterator = value_type const *; + + __static_vector() = default; + + [[nodiscard]] + constexpr auto begin() noexcept -> iterator + { + return nullptr; + } + + [[nodiscard]] + constexpr auto begin() const noexcept -> const_iterator + { + return nullptr; + } + + [[nodiscard]] + constexpr auto end() noexcept -> iterator + { + return nullptr; + } + + [[nodiscard]] + constexpr auto end() const noexcept -> const_iterator + { + return nullptr; + } + + [[nodiscard]] + static constexpr auto size() noexcept -> std::size_t + { + return 0; + } + + [[nodiscard]] + static constexpr auto capacity() noexcept -> std::size_t + { + return 0; + } + }; + + template ... _Rest> + STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE + __static_vector(_First, _Rest...) -> __static_vector<_First, 1 + sizeof...(_Rest)>; } // namespace STDEXEC From 51f538669c2a5c22cb5e043b467c0914e7a8acfe Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Sat, 18 Apr 2026 15:05:53 -0700 Subject: [PATCH 4/8] work around clang-16 deduction guide bug --- include/stdexec/__detail/__let.hpp | 4 ++-- include/stdexec/__detail/__static_vector.hpp | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/include/stdexec/__detail/__let.hpp b/include/stdexec/__detail/__let.hpp index 960cdaca4..b43408ad0 100644 --- a/include/stdexec/__detail/__let.hpp +++ b/include/stdexec/__detail/__let.hpp @@ -577,7 +577,7 @@ namespace STDEXEC __completion_info const __eptr_info(__signature<__eptr_sig_t>, __info.__domain, __info.__behavior); - return __cmpls + __static_vector{__eptr_info}; + return __cmpls + STDEXEC::__make_static_vector(__eptr_info); } else { @@ -591,7 +591,7 @@ namespace STDEXEC static constexpr auto __maybe_transform_cmplsig = [](auto __transform) -> decltype(auto) { if constexpr (_Info.__disposition != __set_t::__disposition) - return __static_vector{_Info}; + return STDEXEC::__make_static_vector(_Info); else return __transform(__signature<__mtypeof<_Info.__signature>>, _Info); }; diff --git a/include/stdexec/__detail/__static_vector.hpp b/include/stdexec/__detail/__static_vector.hpp index 13af22e95..84ca746f6 100644 --- a/include/stdexec/__detail/__static_vector.hpp +++ b/include/stdexec/__detail/__static_vector.hpp @@ -197,4 +197,10 @@ namespace STDEXEC template ... _Rest> STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE __static_vector(_First, _Rest...) -> __static_vector<_First, 1 + sizeof...(_Rest)>; + + template ... _Rest> + constexpr auto __make_static_vector(_First __first, _Rest... __rest) noexcept + { + return __static_vector<_First, 1 + sizeof...(_Rest)>{__first, __rest...}; + } } // namespace STDEXEC From 9c008c2d588109e8714a0fd1311d1c8723182648 Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Sat, 18 Apr 2026 16:11:28 -0700 Subject: [PATCH 5/8] split `__mtypeof` into `__mtypeof` and `__msplice` --- include/stdexec/__detail/__completion_info.hpp | 2 +- include/stdexec/__detail/__let.hpp | 2 +- include/stdexec/__detail/__meta.hpp | 15 ++++----------- include/stdexec/__detail/__typeinfo.hpp | 12 +++++------- 4 files changed, 11 insertions(+), 20 deletions(-) diff --git a/include/stdexec/__detail/__completion_info.hpp b/include/stdexec/__detail/__completion_info.hpp index 885ed383f..a66d27951 100644 --- a/include/stdexec/__detail/__completion_info.hpp +++ b/include/stdexec/__detail/__completion_info.hpp @@ -129,7 +129,7 @@ namespace STDEXEC { constexpr auto __fn = [=](__indices<_Is...>) { - return completion_signatures<__mtypeof<__sigs[_Is]>...>(); + return completion_signatures<__msplice<__sigs[_Is]>...>(); }; return __fn(__make_indices<__sigs.size()>()); } diff --git a/include/stdexec/__detail/__let.hpp b/include/stdexec/__detail/__let.hpp index b43408ad0..36e01ab08 100644 --- a/include/stdexec/__detail/__let.hpp +++ b/include/stdexec/__detail/__let.hpp @@ -593,7 +593,7 @@ namespace STDEXEC if constexpr (_Info.__disposition != __set_t::__disposition) return STDEXEC::__make_static_vector(_Info); else - return __transform(__signature<__mtypeof<_Info.__signature>>, _Info); + return __transform(__signature<__msplice<_Info.__signature>>, _Info); }; //! @tparam _Info A `__static_vector` of `__completion_info` objects representing diff --git a/include/stdexec/__detail/__meta.hpp b/include/stdexec/__detail/__meta.hpp index 1f4b595e8..082b51af9 100644 --- a/include/stdexec/__detail/__meta.hpp +++ b/include/stdexec/__detail/__meta.hpp @@ -48,20 +48,13 @@ namespace STDEXEC template concept __mnever = false; - namespace __detail - { - // NB: This variable template is partially specialized for __type_index in __typeinfo.hpp: #if STDEXEC_GCC() && STDEXEC_GCC_VERSION < 1300 - template - extern __mtype> __mtypeof_v; + template + using __mtypeof = std::remove_const_t; #else - template - extern __mtype __mtypeof_v; -#endif - } // namespace __detail - template - using __mtypeof = decltype(__detail::__mtypeof_v<_Value>)::__t; + using __mtypeof = decltype(_Value); +#endif template struct __mlist; diff --git a/include/stdexec/__detail/__typeinfo.hpp b/include/stdexec/__detail/__typeinfo.hpp index e7a78bd8f..89805f45e 100644 --- a/include/stdexec/__detail/__typeinfo.hpp +++ b/include/stdexec/__detail/__typeinfo.hpp @@ -27,7 +27,7 @@ STDEXEC_PRAGMA_PUSH() STDEXEC_PRAGMA_IGNORE_GNU("-Wunused-private-field") ////////////////////////////////////////////////////////////////////////////////////////// -// __type_info, __mtypeid, and __mtypeof +// __type_info, __mtypeid, and __msplice namespace STDEXEC { @@ -165,19 +165,17 @@ namespace STDEXEC }; STDEXEC_PRAGMA_POP() - - // This specialization is what makes __mtypeof< Id > return the type associated with Id. - template - requires __same_as - extern decltype(__typeid_lookup(__detail::__mtypeid_key<_Index>())) __mtypeof_v<_Index>; } // namespace __detail // For a given type, return a __type_index object template inline constexpr __type_index __mtypeid = __detail::__mtypeid_value<_Ty>::__id; + template <__type_index _Index> + using __msplice = decltype(__typeid_lookup(__detail::__mtypeid_key<_Index>()))::__t; + // Sanity check: - static_assert(STDEXEC_IS_SAME(void, __mtypeof<__mtypeid>)); + static_assert(STDEXEC_IS_SAME(void, __msplice<__mtypeid>)); } // namespace STDEXEC STDEXEC_PRAGMA_POP() From e343b987a46af1bb0a29e2a4bd783f364a111a72 Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Sat, 18 Apr 2026 16:32:50 -0700 Subject: [PATCH 6/8] avoid gcc-12 ICE, cache the splice result --- include/stdexec/__detail/__typeinfo.hpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/include/stdexec/__detail/__typeinfo.hpp b/include/stdexec/__detail/__typeinfo.hpp index 89805f45e..2517551c7 100644 --- a/include/stdexec/__detail/__typeinfo.hpp +++ b/include/stdexec/__detail/__typeinfo.hpp @@ -165,14 +165,23 @@ namespace STDEXEC }; STDEXEC_PRAGMA_POP() + + // Cache the result of the lookup: + template + extern decltype(__typeid_lookup(__detail::__mtypeid_key<_Id>())) __msplice_v; } // namespace __detail // For a given type, return a __type_index object template inline constexpr __type_index __mtypeid = __detail::__mtypeid_value<_Ty>::__id; - template <__type_index _Index> - using __msplice = decltype(__typeid_lookup(__detail::__mtypeid_key<_Index>()))::__t; +#if STDEXEC_GCC() && STDEXEC_GCC_VERSION < 1300 + template + using __msplice = decltype(__detail::__msplice_v<_Id>)::__t; +#else + template <__type_index _Id> + using __msplice = decltype(__detail::__msplice_v<_Id>)::__t; +#endif // Sanity check: static_assert(STDEXEC_IS_SAME(void, __msplice<__mtypeid>)); From 687060a02c3dff00bcb415fb055ee5842b472b7b Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Sat, 18 Apr 2026 17:04:39 -0700 Subject: [PATCH 7/8] try to work around apple-clang name lokup bug --- include/exec/repeat_until.hpp | 6 +-- .../stdexec/__detail/__completion_info.hpp | 9 ++-- .../__detail/__completion_signatures.hpp | 50 +++---------------- include/stdexec/__detail/__continues_on.hpp | 10 ++-- include/stdexec/__detail/__finally.hpp | 10 ++-- .../__detail/__get_completion_signatures.hpp | 3 +- include/stdexec/__detail/__let.hpp | 11 ++-- .../__detail/__stopped_as_optional.hpp | 5 +- .../__transform_completion_signatures.hpp | 6 ++- 9 files changed, 42 insertions(+), 68 deletions(-) diff --git a/include/exec/repeat_until.hpp b/include/exec/repeat_until.hpp index 13b18f3cf..146750da1 100644 --- a/include/exec/repeat_until.hpp +++ b/include/exec/repeat_until.hpp @@ -275,11 +275,11 @@ namespace experimental::execution using __eptr_completion_t = set_error_t(std::exception_ptr); constexpr auto __eptr_completion = (__eptr_completion_t *) nullptr; - STDEXEC_TRY_LET( - auto __sigs, + auto __sigs = exec::transform_completion_signatures(get_completion_signatures<__child_t, _Env...>(), __transform_values<__child_t>, - __transform_errors)) + __transform_errors); + STDEXEC_IF_OK(__sigs) { // The repeat_until sender is a dependent sender if one of the following is // true: diff --git a/include/stdexec/__detail/__completion_info.hpp b/include/stdexec/__detail/__completion_info.hpp index a66d27951..183182aaf 100644 --- a/include/stdexec/__detail/__completion_info.hpp +++ b/include/stdexec/__detail/__completion_info.hpp @@ -89,7 +89,8 @@ namespace STDEXEC template constexpr auto __completion_info_from_v = []() noexcept { - STDEXEC_TRY_LET(auto __cmpl_info, _GetComplInfo()) + auto __cmpl_info = _GetComplInfo(); + STDEXEC_IF_OK(__cmpl_info) { constexpr auto __size = _GetComplInfo().size(); auto __arr = __static_vector<__completion_info, __size>(); @@ -109,7 +110,8 @@ namespace STDEXEC template constexpr auto __completion_sigs_from_v = []() noexcept { - STDEXEC_TRY_LET(constexpr auto __completions, __completion_info_from_v<_GetComplInfo>) + constexpr auto __completions = __completion_info_from_v<_GetComplInfo>; + STDEXEC_IF_OK(__completions) { auto __signatures = __static_vector<__type_index, __completions.size()>(); __signatures.resize(__completions.size()); @@ -125,7 +127,8 @@ namespace STDEXEC template consteval auto __completion_sigs_from(_GetComplInfo) noexcept { - STDEXEC_TRY_LET(constexpr auto __sigs, __completion_sigs_from_v<(_GetComplInfo())>) + constexpr auto __sigs = __completion_sigs_from_v<(_GetComplInfo())>; + STDEXEC_IF_OK(__sigs) { constexpr auto __fn = [=](__indices<_Is...>) { diff --git a/include/stdexec/__detail/__completion_signatures.hpp b/include/stdexec/__detail/__completion_signatures.hpp index cbf612bec..1a864218f 100644 --- a/include/stdexec/__detail/__completion_signatures.hpp +++ b/include/stdexec/__detail/__completion_signatures.hpp @@ -541,50 +541,15 @@ namespace STDEXEC __cmplsigs::__partitions_of_t<_Sigs>::__count_stopped::value; } // namespace __detail - // Below is the definition of the STDEXEC_TRY_LET portability macro. It is used to check + // 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. - // - // USAGE: - // - // STDEXEC_TRY_LET([constexpr]opt auto c, ) - // { - // // c is guaranteed to not be an instantiation of __mexception. - // } - // - // When constexpr exceptions are available (C++26), the macro simply expands to the - // moral equivalent of: - // - // // With constexpr exceptions: - // auto c = ; // throws if c is an __mexception type - // - // When constexpr exceptions are not available, the macro expands to: - // - // // Without constexpr exceptions: - // if constexpr (auto c = ; __merror) - // { - // return c; - // } - // else #if STDEXEC_NO_STDCPP_CONSTEXPR_EXCEPTIONS() -# define STDEXEC_EAT_AUTO_auto -# define STDEXEC_EAT_CONSTEXPR_constexpr -# define STDEXEC_EAT_CONSTEXPR(_ID) STDEXEC_EAT_CONSTEXPR_ ## _ID - -# define STDEXEC_PROBE_CONSTEXPR_constexpr STDEXEC_PP_PROBE(~, 1) -# define STDEXEC_TRY_LET_ID(_ID) \ - STDEXEC_PP_CAT( \ - STDEXEC_EAT_AUTO_, \ - STDEXEC_PP_IIF( \ - STDEXEC_PP_CHECK(STDEXEC_PROBE_CONSTEXPR_ ## _ID), \ - STDEXEC_EAT_CONSTEXPR, \ - STDEXEC_PP_EXPAND)(_ID)) - -# define STDEXEC_TRY_LET(_ID, ...) \ - if constexpr ([[maybe_unused]] _ID = __VA_ARGS__; __merror) \ - { \ - return STDEXEC_TRY_LET_ID(_ID); \ +# define STDEXEC_IF_OK(_ID) \ + if constexpr (STDEXEC::__merror) \ + { \ + return _ID; \ } else template @@ -603,10 +568,7 @@ namespace STDEXEC #else // ^^^ no constexpr exceptions ^^^ / vvv constexpr exceptions vvv -# define STDEXEC_TRY_LET(_ID, ...) \ - if constexpr ([[maybe_unused]] _ID = __VA_ARGS__; false) \ - { \ - } else +# define STDEXEC_IF_OK(_ID) template [[noreturn, nodiscard]] diff --git a/include/stdexec/__detail/__continues_on.hpp b/include/stdexec/__detail/__continues_on.hpp index e0498c53c..c14fbec02 100644 --- a/include/stdexec/__detail/__continues_on.hpp +++ b/include/stdexec/__detail/__continues_on.hpp @@ -318,8 +318,9 @@ namespace STDEXEC template static consteval auto __get_child_completions() { - STDEXEC_TRY_LET(auto __child_completions, - STDEXEC::get_completion_signatures<_Child, __fwd_env_t<_Env>...>()) + auto __child_completions = + STDEXEC::get_completion_signatures<_Child, __fwd_env_t<_Env>...>(); + STDEXEC_IF_OK(__child_completions) { // continues_on has the completions of the child sender, but with value and // error result types decayed. @@ -334,8 +335,9 @@ namespace STDEXEC static consteval auto __get_scheduler_completions() { using __sndr_t = schedule_result_t<_Scheduler>; - STDEXEC_TRY_LET(auto __sched_completions, - STDEXEC::get_completion_signatures<__sndr_t, __fwd_env_t<_Env>...>()) + auto __sched_completions = + STDEXEC::get_completion_signatures<__sndr_t, __fwd_env_t<_Env>...>(); + STDEXEC_IF_OK(__sched_completions) { // The scheduler contributes only error and stopped completions; we ignore value // completions here diff --git a/include/stdexec/__detail/__finally.hpp b/include/stdexec/__detail/__finally.hpp index a517d6f1c..433781399 100644 --- a/include/stdexec/__detail/__finally.hpp +++ b/include/stdexec/__detail/__finally.hpp @@ -307,13 +307,13 @@ namespace STDEXEC template consteval auto __get_completion_signatures() { - STDEXEC_TRY_LET(auto __initial_completions, - get_completion_signatures<_CvInitialSender, _Env...>()) + auto __initial_completions = get_completion_signatures<_CvInitialSender, _Env...>(); + STDEXEC_IF_OK(__initial_completions) { using __initial_completions_t = decltype(__initial_completions); - STDEXEC_TRY_LET( - auto __final_completions, - get_completion_signatures<_CvFinalSender, __mk_final_env_t<_CvInitialSender, _Env>...>()) + auto __final_completions = + get_completion_signatures<_CvFinalSender, __mk_final_env_t<_CvInitialSender, _Env>...>(); + STDEXEC_IF_OK(__final_completions) { if constexpr (__never_sends consteval auto __get_completion_info() { - STDEXEC_TRY_LET(auto __cmplsigs, STDEXEC::get_completion_signatures<_Sender, _Env...>()) + auto __cmplsigs = STDEXEC::get_completion_signatures<_Sender, _Env...>(); + STDEXEC_IF_OK(__cmplsigs) { auto __cmplinfo = STDEXEC::__cmplsigs::__to_array(__cmplsigs); std::ranges::for_each(__cmplinfo, &__completion_info::__populate<_Sender, _Env...>); diff --git a/include/stdexec/__detail/__let.hpp b/include/stdexec/__detail/__let.hpp index 36e01ab08..17dd19cf5 100644 --- a/include/stdexec/__detail/__let.hpp +++ b/include/stdexec/__detail/__let.hpp @@ -567,8 +567,9 @@ namespace STDEXEC else { using __sndr2_t = __invoke_result_t<_Fun, __decay_t<_As>&...>; - STDEXEC_TRY_LET(constexpr auto __cmpls, - STDEXEC::__get_completion_info<__sndr2_t, __env2_t<_Child, _Env>...>()) + constexpr auto __cmpls = + STDEXEC::__get_completion_info<__sndr2_t, __env2_t<_Child, _Env>...>(); + STDEXEC_IF_OK(__cmpls) { if constexpr (!__nothrow_decay_copyable<_As...> || !__nothrow_invocable<_Fun, __decay_t<_As>&...> @@ -616,13 +617,15 @@ namespace STDEXEC constexpr auto __transform = __transform_cmplsig<_Fun, _Child, _Env...>; constexpr auto __get_sig = &__completion_info::__signature; constexpr auto __eptr_sig_id = __mtypeid<__eptr_sig_t>; + constexpr auto __cmpls = STDEXEC::__get_completion_info<_Child, _Env...>(); - STDEXEC_TRY_LET(constexpr auto __cmpls, STDEXEC::__get_completion_info<_Child, _Env...>()) + STDEXEC_IF_OK(__cmpls) { constexpr auto __idx = __make_indices<__cmpls.size()>(); constexpr auto __get_cmpls2 = __get_cmpl_info_i<__cmpls, __transform>(__idx); + constexpr auto __cmpls2 = __cmplsigs::__completion_info_from(__get_cmpls2); - STDEXEC_TRY_LET(constexpr auto __cmpls2, __cmplsigs::__completion_info_from(__get_cmpls2)) + STDEXEC_IF_OK(__cmpls2) { if constexpr (std::ranges::find(__cmpls2, __eptr_sig_id, __get_sig) == __cmpls2.end() && sizeof...(_Env) == 0) diff --git a/include/stdexec/__detail/__stopped_as_optional.hpp b/include/stdexec/__detail/__stopped_as_optional.hpp index fd867b60a..13a488420 100644 --- a/include/stdexec/__detail/__stopped_as_optional.hpp +++ b/include/stdexec/__detail/__stopped_as_optional.hpp @@ -66,8 +66,9 @@ namespace STDEXEC static constexpr auto __get_completion_signatures() { static_assert(__sender_for<_Self, stopped_as_optional_t>); - STDEXEC_TRY_LET(auto __completions, - STDEXEC::get_completion_signatures<__child_of<_Self>, _Env...>()) + auto __completions = STDEXEC::get_completion_signatures<__child_of<_Self>, _Env...>(); + + STDEXEC_IF_OK(__completions) { using _Completions = decltype(__completions); if constexpr (__single_value_sender<__child_of<_Self>, _Env...>) diff --git a/include/stdexec/__detail/__transform_completion_signatures.hpp b/include/stdexec/__detail/__transform_completion_signatures.hpp index 0910e5272..98b6d34c5 100644 --- a/include/stdexec/__detail/__transform_completion_signatures.hpp +++ b/include/stdexec/__detail/__transform_completion_signatures.hpp @@ -575,9 +575,11 @@ namespace STDEXEC _StoppedFn __stopped_fn = {}, _ExtraSigs = {}) { - STDEXEC_TRY_LET(auto __completions, _Completions{}) + auto __completions = _Completions{}; + STDEXEC_IF_OK(__completions) { - STDEXEC_TRY_LET(auto __extra_sigs, _ExtraSigs{}) + auto __extra_sigs = _ExtraSigs{}; + STDEXEC_IF_OK(__extra_sigs) { __cmplsigs::__transform_one __tfx1{__value_fn, __error_fn, __stopped_fn}; return __concat_completion_signatures(__completions.__apply( From 74d9b0629accdac1aee31dee5c769c62cf7a3efe Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Sat, 18 Apr 2026 17:21:01 -0700 Subject: [PATCH 8/8] maybe the workaround for gcc will work for msvc too --- include/stdexec/__detail/__typeinfo.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/stdexec/__detail/__typeinfo.hpp b/include/stdexec/__detail/__typeinfo.hpp index 2517551c7..b53693ed8 100644 --- a/include/stdexec/__detail/__typeinfo.hpp +++ b/include/stdexec/__detail/__typeinfo.hpp @@ -175,7 +175,7 @@ namespace STDEXEC template inline constexpr __type_index __mtypeid = __detail::__mtypeid_value<_Ty>::__id; -#if STDEXEC_GCC() && STDEXEC_GCC_VERSION < 1300 +#if (STDEXEC_GCC() && STDEXEC_GCC_VERSION < 1300) || STDEXEC_MSVC() template using __msplice = decltype(__detail::__msplice_v<_Id>)::__t; #else