diff --git a/include/stdexec/__detail/__basic_sender.hpp b/include/stdexec/__detail/__basic_sender.hpp index 0d05c6dd4..59eb1448a 100644 --- a/include/stdexec/__detail/__basic_sender.hpp +++ b/include/stdexec/__detail/__basic_sender.hpp @@ -164,7 +164,8 @@ namespace STDEXEC static constexpr auto __get_state = // [](_Sender&& __sndr, _Receiver&& __rcvr) noexcept( - __nothrow_decay_copyable<__data_of<_Sender>>) -> decltype(auto) + __nothrow_decay_copyable<__data_of<_Sender>>) + -> __state, std::remove_cvref_t<__data_of<_Sender>>> { return __state{static_cast<_Receiver&&>(__rcvr), STDEXEC::__get<1>(static_cast<_Sender&&>(__sndr))}; diff --git a/test/stdexec/algos/adaptors/test_write_env.cpp b/test/stdexec/algos/adaptors/test_write_env.cpp index 71ca08798..718c4f12f 100644 --- a/test/stdexec/algos/adaptors/test_write_env.cpp +++ b/test/stdexec/algos/adaptors/test_write_env.cpp @@ -21,6 +21,8 @@ #include #include +#include +#include #include #include @@ -68,4 +70,114 @@ namespace receiver{{}, &s}); ::STDEXEC::start(op); } + + template > + struct ReceiverIncomplete + { + using receiver_concept = STDEXEC::receiver_tag; + + IncompleteType* m_ptr; + + void set_value() && noexcept + { + STDEXEC::set_value(std::move(m_ptr->rcvr)); + } + + template + void set_error(Error&& error) && noexcept + { + STDEXEC::set_error(std::move(m_ptr->rcvr), std::forward(error)); + } + + [[nodiscard]] + constexpr auto get_env() const noexcept -> Env + { + return STDEXEC::get_env(*m_ptr); + } + }; + + template + struct OpStateIncomplete + { + using operation_state_concept = STDEXEC::operation_state_tag; + + using rcvr_t = ReceiverIncomplete>; + using inner_opstate_t = STDEXEC::connect_result_t; + + Rcvr rcvr; + inner_opstate_t inner_opstate; + + OpStateIncomplete(Sndr&& sndr, Rcvr rcvr_) + : rcvr(std::move(rcvr_)) + , inner_opstate(STDEXEC::connect(std::forward(sndr), rcvr_t{this})) + {} + + void start() & noexcept + { + STDEXEC::start(inner_opstate); + } + + [[nodiscard]] + constexpr auto get_env() const noexcept -> STDEXEC::env_of_t + { + return STDEXEC::get_env(rcvr); + } + }; + + template + struct SenderIncomplete + { + using sender_concept = STDEXEC::sender_tag; + + template + static consteval auto get_completion_signatures() + { + return exec::get_child_completion_signatures(); + } + + template + auto connect(Rcvr rcvr) && -> OpStateIncomplete + { + return {std::forward(sndr), std::move(rcvr)}; + } + + Sndr sndr; + }; + + struct incomplete_t + { + constexpr auto operator()() const noexcept + { + return STDEXEC::__closure(*this); + } + + template + constexpr auto operator()(Sndr&& sndr) const -> SenderIncomplete + { + return {std::forward(sndr)}; + } + }; + + inline constexpr incomplete_t incomplete{}; + + TEST_CASE("write_env with a receiver pointing to an incomplete operation state", + "[adaptors][write_env]") + { + exec::single_thread_context stc{}; + + int value = 0; + + STDEXEC::sender auto sndr = + STDEXEC::read_env(STDEXEC::get_allocator) + | STDEXEC::then([](auto allocator) + { static_assert(std::same_as>); }) + | STDEXEC::continues_on(stc.get_scheduler()) | incomplete() + | STDEXEC::write_env(STDEXEC::prop{STDEXEC::get_allocator, std::allocator{}}) + | STDEXEC::then([&value]() { ++value; }); + + STDEXEC::sync_wait(std::move(sndr)); + + CHECK(value == 1); + } + } // namespace