diff --git a/include/exec/env.hpp b/include/exec/env.hpp index ed8edb6e7..d1f911dfe 100644 --- a/include/exec/env.hpp +++ b/include/exec/env.hpp @@ -177,8 +177,10 @@ namespace experimental::execution template <__decays_to<__sender> _Self, class _Receiver> constexpr STDEXEC_EXPLICIT_THIS_BEGIN(auto connect)(this _Self&& __self, _Receiver __rcvr) - noexcept(std::is_nothrow_move_constructible_v<_Receiver>) - -> __opstate<_Query, __default_t>, _Receiver> + noexcept(__nothrow_constructible_from< + __opstate<_Query, __default_t>, _Receiver>, + __default_t>, + _Receiver>) -> __opstate<_Query, __default_t>, _Receiver> { using __opstate_t = __opstate<_Query, __default_t>, _Receiver>; return __opstate_t{static_cast<_Self&&>(__self).__default_, diff --git a/test/exec/test_env.cpp b/test/exec/test_env.cpp index 943a28a63..4b020f37f 100644 --- a/test/exec/test_env.cpp +++ b/test/exec/test_env.cpp @@ -20,6 +20,51 @@ namespace { + struct missing_query : STDEXEC::__query + { + using STDEXEC::__query::operator(); + }; + + struct default_move_error + {}; + + struct throwing_default + { + explicit throwing_default(bool* throw_on_move) noexcept + : throw_on_move_{throw_on_move} + {} + + throwing_default(throwing_default const &) noexcept = default; + + throwing_default(throwing_default&& other) + : throw_on_move_{other.throw_on_move_} + { + if (*throw_on_move_) + { + throw default_move_error{}; + } + } + + bool* throw_on_move_; + }; + + struct read_with_default_receiver + { + using receiver_concept = STDEXEC::receiver_tag; + + template + void set_value(_Value&&) noexcept + {} + + void set_error(std::exception_ptr) noexcept {} + void set_stopped() noexcept {} + + auto get_env() const noexcept -> STDEXEC::env<> + { + return {}; + } + }; + // Two dummy properties: constexpr struct Foo : STDEXEC::__query @@ -53,4 +98,14 @@ namespace STATIC_REQUIRE(!std::invocable); CHECK(bar(e4) == 43); } + + TEST_CASE("read_with_default connect propagates default move exceptions", "[env]") + { + bool throw_on_move = false; + auto sndr = exec::read_with_default(missing_query{}, throwing_default{&throw_on_move}); + throw_on_move = true; + + STATIC_REQUIRE_FALSE(noexcept(std::move(sndr).connect(read_with_default_receiver{}))); + CHECK_THROWS_AS(std::move(sndr).connect(read_with_default_receiver{}), default_move_error); + } } // namespace