diff --git a/include/beman/execution/detail/run_loop.hpp b/include/beman/execution/detail/run_loop.hpp index 50777110..06a9eab7 100644 --- a/include/beman/execution/detail/run_loop.hpp +++ b/include/beman/execution/detail/run_loop.hpp @@ -31,6 +31,7 @@ import beman.execution.detail.set_value; import beman.execution.detail.unstoppable_token; #else #include +#include #include #include #include @@ -90,10 +91,20 @@ class run_loop { }; struct sender { using sender_concept = ::beman::execution::sender_tag; - template + template static consteval auto get_completion_signatures() noexcept { - if constexpr (::beman::execution::unstoppable_token()...))>) + // [exec.getcomplsigs] permits sizeof...(Env) == 0. Answering that + // query as if for env<> matches [exec.run.loop]'s formula, whose + // E = env<> has an unstoppable stop token, and matches how the + // other environment-sensitive senders here (then, into_variant) + // handle the empty pack. Without this the member's body is + // ill-formed for an empty pack -- get_stop_token() with no + // argument -- and because the return type is deduced, that is a + // hard error rather than a substitution failure. + if constexpr (0u == sizeof...(Env)) + return get_completion_signatures>(); + else if constexpr (::beman::execution::unstoppable_token()...))>) return ::beman::execution::completion_signatures<::beman::execution::set_value_t()>{}; else return ::beman::execution::completion_signatures<::beman::execution::set_value_t(), diff --git a/tests/beman/execution/exec-run-loop-types.test.cpp b/tests/beman/execution/exec-run-loop-types.test.cpp index 4d6f43f8..cced0e8e 100644 --- a/tests/beman/execution/exec-run-loop-types.test.cpp +++ b/tests/beman/execution/exec-run-loop-types.test.cpp @@ -16,6 +16,7 @@ import beman.execution; #include #include #include +#include #include #include #include @@ -98,6 +99,16 @@ TEST(exec_run_loop_types) { ::std::same_as, decltype(test_std::get_completion_signatures())>); + // [exec.getcomplsigs] constrains sizeof...(Env) <= 1, so the + // zero-environment query is well-formed and must be answered. It is + // answered as for env<>, whose stop token is unstoppable. + static_assert(::std::same_as, + decltype(test_std::get_completion_signatures())>); + // ... and because detail::non_dependent_sender is itself spelled with that + // query inside a requires-expression, a sender that cannot answer it makes + // the concept ill-formed rather than false. Asserting the concept is what + // pins that down. + static_assert(not test_std::dependent_sender); // p7: static_assert( test_std::receiver_of())>);