diff --git a/include/beman/execution/detail/let.hpp b/include/beman/execution/detail/let.hpp index c75065ce..e90ce816 100644 --- a/include/beman/execution/detail/let.hpp +++ b/include/beman/execution/detail/let.hpp @@ -169,6 +169,10 @@ struct let_t { template struct apply_decayed { using sender_type = ::beman::execution::detail::call_result_t...>; + using completions = ::std::conditional_t< + noexcept(::std::declval()(std::declval<::std::decay_t>()...)), + ::beman::execution::completion_signatures<>, + ::beman::execution::completion_signatures<::beman::execution::set_error_t(::std::exception_ptr)>>; }; template struct get_completions; @@ -176,7 +180,8 @@ struct let_t { struct get_completions> { using type = ::beman::execution::detail::meta::unique<::beman::execution::detail::meta::combine< ::beman::execution::completion_signatures<>, - ::beman::execution::completion_signatures_of_t::sender_type, Env...>...>>; + ::beman::execution::completion_signatures_of_t::sender_type, Env...>..., + typename apply_decayed::completions...>>; }; using upstream_env = typename let_upstream_env_helper::type; diff --git a/tests/beman/execution/exec-just.test.cpp b/tests/beman/execution/exec-just.test.cpp index 1579a1f2..eceea102 100644 --- a/tests/beman/execution/exec-just.test.cpp +++ b/tests/beman/execution/exec-just.test.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #ifdef BEMAN_HAS_MODULES import beman.execution; import beman.execution.detail; @@ -201,6 +202,13 @@ auto test_just_allocator() -> void { test::use(state); ASSERT(resource.count == 2u); } + +auto test_completion_signatures() -> void { + test_std::sync_wait(test::completion_test(test_std::just())); + test_std::sync_wait(test::completion_test(test_std::just(1, 2, 3))); + test_std::sync_wait(test::completion_test(test_std::just_error(1))); + test_std::sync_wait(test::completion_test(test_std::just_stopped())); +} } // namespace TEST(exec_just) { @@ -211,6 +219,7 @@ TEST(exec_just) { try { test_just_constraints(); test_just(); + test_completion_signatures(); #ifndef _MSC_VER //-dk:TODO re-enable allocator test for MSVC++ test_just_allocator(); diff --git a/tests/beman/execution/exec-let.test.cpp b/tests/beman/execution/exec-let.test.cpp index 89b47ed9..21bdd6df 100644 --- a/tests/beman/execution/exec-let.test.cpp +++ b/tests/beman/execution/exec-let.test.cpp @@ -4,10 +4,12 @@ #include #include #include +#include #include #include #include #include +#include #ifdef BEMAN_HAS_MODULES import beman.execution; #else @@ -120,6 +122,15 @@ auto test_let_value_env() -> void { ex::sync_wait(ex::just() | ex::let_value([] { return ex::read_env(ex::get_scheduler); }) | ex::then([](auto s) { static_assert(ex::scheduler); })); } + +auto test_completion_signatures() -> void { + test_std::sync_wait( + test::completion_test(test_std::just() | test_std::let_value([]() { return test_std::just(); }))); + test_std::sync_wait( + test::completion_test(test_std::just() | test_std::let_value([]() noexcept { return test_std::just(); }))); + test_std::sync_wait(test::completion_test( + test_std::just() | test_std::let_value([]() noexcept { return test_std::just_error(std::exception_ptr{}); }))); +} } // namespace // ---------------------------------------------------------------------------- @@ -133,6 +144,7 @@ TEST(exec_let) { test_let_value(); test_let_value_allocator(); test_let_value_env(); + test_completion_signatures(); } catch (...) { // NOLINTBEGIN(cert-dcl03-c,hicpp-static-assert,misc-static-assert) ASSERT(nullptr == "let tests are not expected to throw"); diff --git a/tests/beman/execution/include/test/completion_test.hpp b/tests/beman/execution/include/test/completion_test.hpp new file mode 100644 index 00000000..4866095e --- /dev/null +++ b/tests/beman/execution/include/test/completion_test.hpp @@ -0,0 +1,126 @@ +// tests/beman/execution/include/test/completion_test.hpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef INCLUDED_TESTS_BEMAN_EXECUTION_INCLUDE_TEST_COMPLETION_TEST +#define INCLUDED_TESTS_BEMAN_EXECUTION_INCLUDE_TEST_COMPLETION_TEST + +#include +#include +#ifdef BEMAN_HAS_IMPORT_STD +import std; +#else +#include +#include +#include +#include +#include +#endif +#ifdef BEMAN_HAS_MODULES +import beman.execution.detail.completion_signatures; +import beman.execution.detail.connect; +import beman.execution.detail.env_of_t; +import beman.execution.detail.get_completion_signatures; +import beman.execution.detail.get_env; +import beman.execution.detail.operation_state; +import beman.execution.detail.receiver; +import beman.execution.detail.sender; +import beman.execution.detail.set_error; +import beman.execution.detail.set_stopped; +import beman.execution.detail.set_value; +import beman.execution.detail.start; +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +// ---------------------------------------------------------------------------- + +namespace test { +struct completion_test_base { + virtual auto complete() const noexcept -> void = 0; +}; +template +struct completion_test_function; + +template +struct completion_test_function<::beman::execution::set_value_t(T...)> : completion_test_base { + auto set_value(T...) const noexcept -> void { this->complete(); } +}; +template +struct completion_test_function<::beman::execution::set_error_t(E)> : completion_test_base { + auto set_error(E) const noexcept -> void { this->complete(); } +}; +template <> +struct completion_test_function<::beman::execution::set_stopped_t()> : completion_test_base { + auto set_stopped() const noexcept -> void { this->complete(); } +}; + +template +struct completion_test_receiver; +template +struct completion_test_receiver<::beman::execution::completion_signatures> + : completion_test_function... {}; + +template <::beman::execution::sender Sender> +struct completion_test { + using sender_concept = ::beman::execution::sender_t; + template + static consteval auto get_completion_signatures() noexcept { + return ::beman::execution::completion_signatures<::beman::execution::set_value_t()>(); + } + + template <::beman::execution::receiver Receiver> + struct state { + using operation_state_concept = ::beman::execution::operation_state_t; + struct inner_receiver + : test::completion_test_receiver< + decltype(::beman::execution::get_completion_signatures>())> { + using receiver_concept = ::beman::execution::receiver_t; + state* st; + auto get_env() const noexcept -> ::beman::execution::env_of_t { + return ::beman::execution::get_env(this->st->receiver); + } + auto complete() const noexcept -> void override { + ::beman::execution::set_value(std::move(this->st->receiver)); + } + inner_receiver(state* s) : st(s) {} + }; + using inner_state_t = + decltype(::beman::execution::connect(std::declval(), std::declval())); + + std::remove_cvref_t receiver; + inner_state_t inner_state; + + template <::beman::execution::receiver R, ::beman::execution::sender S> + state(R&& r, S&& s) + : receiver(std::forward(r)), + inner_state(::beman::execution::connect(std::forward(s), inner_receiver(this))) {} + auto start() noexcept { ::beman::execution::start(this->inner_state); } + }; + + Sender sender; + + template <::beman::execution::receiver Receiver> + auto connect(Receiver&& r) && { + return state{std::forward(r), std::move(this->sender)}; + } +}; + +template <::beman::execution::sender Sender> +completion_test(Sender&& sender) -> completion_test>; +} // namespace test + +// ---------------------------------------------------------------------------- + +#endif