From b0022abea1538162471dcff891e2d6437ac7b2f9 Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Sun, 16 Aug 2026 02:45:55 +0200 Subject: [PATCH 1/3] Fix read_with_default connect noexcept --- include/exec/env.hpp | 5 +++- test/exec/test_env.cpp | 55 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/include/exec/env.hpp b/include/exec/env.hpp index ed8edb6e7..bbaec83da 100644 --- a/include/exec/env.hpp +++ b/include/exec/env.hpp @@ -177,7 +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>) + 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>; diff --git a/test/exec/test_env.cpp b/test/exec/test_env.cpp index 943a28a63..48c0bf6e9 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 From ffe7f09bf81ca1230e39762bd673d9c79806c627 Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Sun, 16 Aug 2026 21:19:16 +0200 Subject: [PATCH 2/3] Format Clang sources --- include/exec/env.hpp | 9 +++++---- test/exec/test_env.cpp | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/include/exec/env.hpp b/include/exec/env.hpp index bbaec83da..20df2f696 100644 --- a/include/exec/env.hpp +++ b/include/exec/env.hpp @@ -177,10 +177,11 @@ namespace experimental::execution template <__decays_to<__sender> _Self, class _Receiver> constexpr STDEXEC_EXPLICIT_THIS_BEGIN(auto connect)(this _Self&& __self, _Receiver __rcvr) - noexcept(__nothrow_constructible_from< - __opstate<_Query, __default_t>, _Receiver>, - __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>; diff --git a/test/exec/test_env.cpp b/test/exec/test_env.cpp index 48c0bf6e9..dd36216b2 100644 --- a/test/exec/test_env.cpp +++ b/test/exec/test_env.cpp @@ -34,7 +34,7 @@ namespace : throw_on_move_{throw_on_move} {} - throwing_default(throwing_default const&) noexcept = default; + throwing_default(throwing_default const &) noexcept = default; throwing_default(throwing_default&& other) : throw_on_move_{other.throw_on_move_} @@ -102,7 +102,7 @@ namespace 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}); + 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{}))); From f070a2040d838cda09bbc17b82c50992853e23a5 Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Sun, 16 Aug 2026 22:21:33 +0200 Subject: [PATCH 3/3] Format read_with_default changes --- include/exec/env.hpp | 10 ++++------ test/exec/test_env.cpp | 4 ++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/include/exec/env.hpp b/include/exec/env.hpp index 20df2f696..d1f911dfe 100644 --- a/include/exec/env.hpp +++ b/include/exec/env.hpp @@ -177,12 +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( - __nothrow_constructible_from< - __opstate<_Query, __default_t>, _Receiver>, - __default_t>, - _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 dd36216b2..4b020f37f 100644 --- a/test/exec/test_env.cpp +++ b/test/exec/test_env.cpp @@ -102,8 +102,8 @@ namespace 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; + 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);