diff --git a/include/exec/sequence_senders.hpp b/include/exec/sequence_senders.hpp index 734586180..2a8847aa5 100644 --- a/include/exec/sequence_senders.hpp +++ b/include/exec/sequence_senders.hpp @@ -530,8 +530,7 @@ namespace experimental::execution #endif template - struct __sequence_type_check_failure // - : STDEXEC::__compile_time_error<__sequence_type_check_failure<_Data, _What...>> + struct __sequence_type_check_failure : STDEXEC::__compile_time_error { static_assert(STDEXEC::__nothrow_move_constructible<_Data>, "The data member of sender_type_check_failure must be nothrow move " @@ -543,16 +542,14 @@ namespace experimental::execution : __data_(static_cast<_Data&&>(data)) {} - private: - friend struct STDEXEC::__compile_time_error<__sequence_type_check_failure>; - [[nodiscard]] - constexpr auto what() const noexcept -> char const * + constexpr auto what() const noexcept -> char const * // NOLINT(modernize-use-override) { return "This sequence sender is not well-formed. It does not meet the requirements of a " "sequence sender type."; } + // public so that __sequence_type_check_failure is a structural type _Data __data_{}; }; diff --git a/include/stdexec/__detail/__concepts.hpp b/include/stdexec/__detail/__concepts.hpp index 21e235857..7e2bcb6b0 100644 --- a/include/stdexec/__detail/__concepts.hpp +++ b/include/stdexec/__detail/__concepts.hpp @@ -97,6 +97,22 @@ namespace STDEXEC template class _Ty> concept __is_not_instance_of = !__is_instance_of<_Ay, _Ty>; + template + concept __constant = true; + + namespace __detail + { + template + using __is_nttp = void; + template class> + using __nttp_test = void; + } // namespace __detail + + template + concept __structural = requires { typename __detail::__nttp_test<_Ty, __detail::__is_nttp>; }; + + static_assert(__structural); + namespace __std { diff --git a/include/stdexec/__detail/__diagnostics.hpp b/include/stdexec/__detail/__diagnostics.hpp index fe2f67bf6..31f05b662 100644 --- a/include/stdexec/__detail/__diagnostics.hpp +++ b/include/stdexec/__detail/__diagnostics.hpp @@ -146,10 +146,12 @@ namespace STDEXEC #if __cpp_lib_constexpr_exceptions >= 202502L // constexpr exception types, https://wg21.link/p3378 + // constexpr stdlib exception types, https://wg21.link/p3378 using __exception = ::std::exception; #elif __cpp_constexpr >= 202411L // constexpr virtual functions + // constexpr virtual functions struct __exception { constexpr __exception() noexcept = default; @@ -162,8 +164,9 @@ namespace STDEXEC } }; -#else // no constexpr virtual functions: +#else + // no constexpr virtual functions struct __exception { constexpr __exception() noexcept = default; @@ -177,21 +180,11 @@ namespace STDEXEC #endif // __cpp_lib_constexpr_exceptions >= 202502L - template struct __compile_time_error : __exception - { - constexpr __compile_time_error() = default; // NOLINT (bugprone-crtp-constructor-accessibility) - - [[nodiscard]] - constexpr auto what() const noexcept -> char const * - { - return static_cast<_Derived const *>(this)->what(); - } - }; + {}; template - struct __sender_type_check_failure // - : __compile_time_error<__sender_type_check_failure<_Data, _What...>> + struct __sender_type_check_failure : __compile_time_error { static_assert(std::is_nothrow_move_constructible_v<_Data>, "The data member of sender_type_check_failure must be nothrow move " @@ -203,39 +196,24 @@ namespace STDEXEC : __data_(static_cast<_Data &&>(data)) {} - private: - friend struct __compile_time_error<__sender_type_check_failure>; - [[nodiscard]] - constexpr auto what() const noexcept -> char const * + constexpr auto what() const noexcept -> char const * // NOLINT(modernize-use-override) { return "This sender is not well-formed. It does not meet the requirements of a sender type."; } + // public so that __sender_type_check_failure is a structural type _Data __data_{}; }; - struct dependent_sender_error : __compile_time_error + struct dependent_sender_error : __compile_time_error { - constexpr dependent_sender_error() noexcept - : what_("This sender needs to know its execution environment before it can know how it will " - "complete.") - {} - - constexpr explicit dependent_sender_error(char const *what) noexcept - : what_(what) - {} - - private: - friend struct __compile_time_error; - [[nodiscard]] - constexpr auto what() const noexcept -> char const * + constexpr auto what() const noexcept -> char const * // NOLINT(modernize-use-override) { - return what_; + return "This sender needs to know its execution environment before it can " + "know how it will complete."; } - - char const *what_; }; // A specialization of _ERROR_ to be used to report dependent sender. It inherits @@ -261,11 +239,8 @@ namespace STDEXEC using __errors = _ERROR_; using __all = _ERROR_; - constexpr _ERROR_() noexcept - : dependent_sender_error{"This sender needs to know its execution environment before it can " - "know how it will " - "complete."} - {} + constexpr _ERROR_() = default; + constexpr ~_ERROR_() = default; STDEXEC_ATTRIBUTE(host, device) constexpr auto operator+() const -> _ERROR_; @@ -285,6 +260,8 @@ namespace STDEXEC } }; + static_assert(__structural<_ERROR_>); + // By making __dependent_sender_error_t an alias for _ERROR_<...>, we ensure that // it will get propagated correctly through various metafunctions. template diff --git a/include/stdexec/__detail/__get_completion_signatures.hpp b/include/stdexec/__detail/__get_completion_signatures.hpp index 116280a42..87f71325a 100644 --- a/include/stdexec/__detail/__get_completion_signatures.hpp +++ b/include/stdexec/__detail/__get_completion_signatures.hpp @@ -301,11 +301,49 @@ namespace STDEXEC /////////////////////////////////////////////////////////////////////////////////////////////////// // An minimally constrained alias for the result of get_completion_signatures: +#if STDEXEC_GCC() template requires enable_sender<__decay_t<_Sender>> + && __constant()> using __completion_signatures_of_t = decltype(STDEXEC::get_completion_signatures<_Sender, _Env...>()); +#elif STDEXEC_EDG() + + namespace __detail + { + template + using __cmplsigs_of_t = + std::integral_constant()), + STDEXEC::get_completion_signatures<_Sender, _Env...>()>::value_type; + } // namespace __detail + + template + requires enable_sender<__decay_t<_Sender>> + && __minvocable_q<__detail::__cmplsigs_of_t, _Sender, _Env...> + using __completion_signatures_of_t = + decltype(STDEXEC::get_completion_signatures<_Sender, _Env...>()); + +#elif STDEXEC_MSVC() + + // MSVC cannot handle a __completion_signatures_of_t alias template that requires + // get_completion_signatures to be a constant expression, even if we wrap the call to + // get_completion_signatures in an integral_constant like we do for EDG. So we skip + // checking the requirement. + + template + requires enable_sender<__decay_t<_Sender>> + using __completion_signatures_of_t = + decltype(STDEXEC::get_completion_signatures<_Sender, _Env...>()); + +#else + + template + requires enable_sender<__decay_t<_Sender>> + using __completion_signatures_of_t = + __mtypeof()>; +#endif + /////////////////////////////////////////////////////////////////////////////////////////////////// // __get_child_completion_signatures template