From 7b40c5a9921db263a7c693c6b94f01f4fd44587a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Sat, 25 Apr 2026 21:42:16 +0100 Subject: [PATCH 1/3] added tests for completions being called and fixed an issue with let --- include/beman/execution/detail/let.hpp | 7 +- tests/beman/execution/exec-just.test.cpp | 9 ++ tests/beman/execution/exec-let.test.cpp | 11 ++ .../include/test/completion_test.hpp | 128 ++++++++++++++++++ 4 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 tests/beman/execution/include/test/completion_test.hpp 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..8a456618 100644 --- a/tests/beman/execution/exec-let.test.cpp +++ b/tests/beman/execution/exec-let.test.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #ifdef BEMAN_HAS_MODULES import beman.execution; #else @@ -120,6 +121,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 +143,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..b8ffae32 --- /dev/null +++ b/tests/beman/execution/include/test/completion_test.hpp @@ -0,0 +1,128 @@ +// 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 +#include +#include +#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.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 +#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 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) && noexcept { + return state{std::forward(r), std::move(this->sender)}; + } +}; + +template <::beman::execution::sender Sender> +completion_test(Sender&& sender) -> completion_test>; +} // namespace test + +// ---------------------------------------------------------------------------- + +#endif From 38fb6d58fdd4a7520a1eb8eae119270f327ed09d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Sun, 26 Apr 2026 08:19:06 +0100 Subject: [PATCH 2/3] action AI review feedback --- tests/beman/execution/exec-let.test.cpp | 1 + .../execution/include/test/completion_test.hpp | 18 +++++++----------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/tests/beman/execution/exec-let.test.cpp b/tests/beman/execution/exec-let.test.cpp index 8a456618..21bdd6df 100644 --- a/tests/beman/execution/exec-let.test.cpp +++ b/tests/beman/execution/exec-let.test.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include diff --git a/tests/beman/execution/include/test/completion_test.hpp b/tests/beman/execution/include/test/completion_test.hpp index b8ffae32..ca79dbe4 100644 --- a/tests/beman/execution/include/test/completion_test.hpp +++ b/tests/beman/execution/include/test/completion_test.hpp @@ -9,18 +9,11 @@ #ifdef BEMAN_HAS_IMPORT_STD import std; #else -#include -#include -#include -#include -#include #include -#include #include #include -#include -#include -#include +#include +#include #endif #ifdef BEMAN_HAS_MODULES import beman.execution.detail.completion_signatures; @@ -93,7 +86,10 @@ struct completion_test { ::beman::execution::env_of_t>())> { using receiver_concept = ::beman::execution::receiver_t; state* st; - auto complete() const noexcept -> void override { + 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) {} @@ -114,7 +110,7 @@ struct completion_test { Sender sender; template <::beman::execution::receiver Receiver> - auto connect(Receiver&& r) && noexcept { + auto connect(Receiver&& r) && { return state{std::forward(r), std::move(this->sender)}; } }; From b11a2a1d83ebc24cf7648a2fbc08c8e71095ae38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dietmar=20K=C3=BChl?= Date: Sun, 26 Apr 2026 08:22:35 +0100 Subject: [PATCH 3/3] added a missing header --- tests/beman/execution/include/test/completion_test.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/beman/execution/include/test/completion_test.hpp b/tests/beman/execution/include/test/completion_test.hpp index ca79dbe4..4866095e 100644 --- a/tests/beman/execution/include/test/completion_test.hpp +++ b/tests/beman/execution/include/test/completion_test.hpp @@ -20,6 +20,7 @@ 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; @@ -32,6 +33,7 @@ import beman.execution.detail.start; #include #include #include +#include #include #include #include