diff --git a/include/stdexec/__detail/__connect.hpp b/include/stdexec/__detail/__connect.hpp index 9ec5c8687..598b1e908 100644 --- a/include/stdexec/__detail/__connect.hpp +++ b/include/stdexec/__detail/__connect.hpp @@ -16,6 +16,7 @@ #pragma once #include "__execution_fwd.hpp" +#include "__diagnostics.hpp" // include these after __execution_fwd.hpp #include "__completion_signatures_of.hpp" @@ -206,7 +207,9 @@ namespace STDEXEC { class _Receiver, class _DeclFn = __connect_declfn_t<_Sender, _Receiver> > - requires __connectable_to<_Sender, _Receiver> + requires assert_with< + __connectable_to<_Sender, _Receiver>, + __connect_error_t>, _Receiver>> STDEXEC_ATTRIBUTE(always_inline) constexpr auto operator()(_Sender&& __sndr, _Receiver&& __rcvr) const noexcept(__nothrow_callable<_DeclFn>) -> __call_result_t<_DeclFn> { diff --git a/include/stdexec/__detail/__diagnostics.hpp b/include/stdexec/__detail/__diagnostics.hpp index 190381b6c..f46b350c4 100644 --- a/include/stdexec/__detail/__diagnostics.hpp +++ b/include/stdexec/__detail/__diagnostics.hpp @@ -115,6 +115,18 @@ namespace STDEXEC { _WITH_ENVIRONMENT_(_Env)... >; + struct _CONNECT_ERROR_ {}; + struct _UNABLE_TO_CONNECT_THE_SENDER_TO_THE_RECEIVER_ { }; + + template + using __connect_error_t = __mexception< + _WHAT_(_CONNECT_ERROR_), + _WHY_(_UNABLE_TO_CONNECT_THE_SENDER_TO_THE_RECEIVER_), + _WITH_PRETTY_SENDER_<_Sender>, + _WITH_RECEIVER_(_Receiver), + _WITH_ENVIRONMENT_(env_of_t<_Receiver>) + >; + #if __cpp_lib_constexpr_exceptions >= 2025'02L // constexpr exception types, https://wg21.link/p3378 using __exception = ::std::exception; @@ -258,6 +270,22 @@ namespace STDEXEC { constexpr bool operator==(const __not_a_scheduler&) const noexcept = default; }; + + template + struct __assert_with; + + template + struct __assert_with { + static constexpr bool value = __ok<_WithError>; + static_assert(__ok<_WithError>, "concept assertion failed with.."); + }; + template + struct __assert_with { + static constexpr bool value = true; + }; + + template + concept assert_with = __assert_with<_MustBeTrue, _WithError>::value; } // namespace STDEXEC //////////////////////////////////////////////////////////////////////////////// diff --git a/include/stdexec/__detail/__get_completion_signatures.hpp b/include/stdexec/__detail/__get_completion_signatures.hpp index 19512ece4..785ec222c 100644 --- a/include/stdexec/__detail/__get_completion_signatures.hpp +++ b/include/stdexec/__detail/__get_completion_signatures.hpp @@ -238,10 +238,11 @@ namespace STDEXEC { } template - requires __has_get_completion_signatures<_Sender, _Env> + requires assert_with< + __has_get_completion_signatures<_Sender, _Env>, + __unrecognized_sender_error_t, _Env>> consteval auto get_completion_signatures() { using __new_sndr_t = transform_sender_result_t<_Sender, _Env>; - static_assert(!__merror<__new_sndr_t>); return __cmplsigs::__get_completion_signatures_helper<__new_sndr_t, _Env>(); } @@ -255,7 +256,9 @@ namespace STDEXEC { /////////////////////////////////////////////////////////////////////////////////////////////////// // An minimally constrained alias for the result of get_completion_signatures: template - requires enable_sender<__decay_t<_Sender>> + requires assert_with< + enable_sender<__decay_t<_Sender>>, + __unrecognized_sender_error_t<__decay_t<_Sender>, _Env...>> using __completion_signatures_of_t = decltype(STDEXEC::get_completion_signatures<_Sender, _Env...>()); diff --git a/test/stdexec/concepts/test_concepts_sender.cpp b/test/stdexec/concepts/test_concepts_sender.cpp index 571ae9a81..4c4864671 100644 --- a/test/stdexec/concepts/test_concepts_sender.cpp +++ b/test/stdexec/concepts/test_concepts_sender.cpp @@ -93,7 +93,7 @@ namespace { ex::set_stopped_t() >; - auto connect(empty_recv::recv0&&) const -> oper { + auto connect(empty_recv::recv0&&) const -> oper { return {}; } }; @@ -171,11 +171,9 @@ namespace { "not all combinations of senders & receivers satisfy the sender_to concept", "[concepts][sender]") { REQUIRE_FALSE(ex::sender_to); - REQUIRE_FALSE(ex::sender_to); REQUIRE_FALSE(ex::sender_to); REQUIRE_FALSE(ex::sender_to); REQUIRE_FALSE(ex::sender_to); - REQUIRE_FALSE(ex::sender_to); } TEST_CASE(