Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions include/exec/sequence/transform_each.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,11 +217,10 @@ namespace experimental::execution
}

template <class _Sexpr>
static auto get_env(_Sexpr const & __sexpr) noexcept -> env_of_t<__child_of<_Sexpr>>
static decltype(auto) get_env(_Sexpr const & __sexpr) noexcept
{
static_assert(sender_for<_Sexpr, transform_each_t>);
return __apply([]<class _Child>(__ignore, __ignore, _Child const & __child)
{ return STDEXEC::get_env(__child); },
-> decltype(auto) { return STDEXEC::get_env(__child); },
__sexpr);
}
};
Expand Down
24 changes: 16 additions & 8 deletions include/exec/sequence_senders.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -508,8 +508,12 @@ namespace experimental::execution

namespace __debug
{
template <class _Env = STDEXEC::env<>, class _Sequence>
constexpr void __debug_sequence_sender(_Sequence&& __sequence, _Env const & = {});
// N.B. the template parameter list here must match the definition of
// __debug_sequence_sender below (at the end of this header); otherwise the
// declaration and definition are distinct function templates and every
// odr-use of this function is ill-formed (no definition).
template <class _CvSequence, class _Env = STDEXEC::env<>>
constexpr void __debug_sequence_sender(_CvSequence&& __sequence, _Env const & = {});
} // namespace __debug
using __debug::__debug_sequence_sender;

Expand Down Expand Up @@ -697,7 +701,7 @@ namespace experimental::execution
sequence_sender_in<_Sequence, STDEXEC::env_of_t<_Receiver>> //
&& STDEXEC::receiver_of<
_Receiver,
STDEXEC::completion_signatures_of_t<_Sequence, STDEXEC::env_of_t<_Receiver>>>;
__sequence_completion_signatures_of_t<_Sequence, STDEXEC::env_of_t<_Receiver>>>;

template <class _Receiver, class _Sequence>
concept __stopped_means_break_receiver_from = //
Expand Down Expand Up @@ -765,11 +769,15 @@ namespace experimental::execution
{
if constexpr (sequence_sender_in<_Sequence, env_of_t<_Receiver>>)
{
// Instantiate __debug_sender via completion_signatures_of_t and
// item_types_of_t to check that the actual completions and item_types
// match the expected completions and values.
using __checked_signatures
[[maybe_unused]] = completion_signatures_of_t<_Sequence, env_of_t<_Receiver>>;
// Instantiate the sequence completion signatures and item_types to
// check that the actual completions and item_types match the
// expected completions and values. (N.B. don't use
// completion_signatures_of_t here: sequence senders are not regular
// senders, and with STDEXEC_ENABLE_EXTRA_TYPE_CHECKING=ON that
// alias would instantiate __debug_sender, which tries to connect()
// the sequence sender.)
using __checked_signatures [[maybe_unused]] =
__sequence_completion_signatures_of_t<_Sequence, env_of_t<_Receiver>>;
using __checked_item_types
[[maybe_unused]] = item_types_of_t<_Sequence, env_of_t<_Receiver>>;
}
Expand Down
29 changes: 29 additions & 0 deletions test/exec/sequence/test_sequence_any_sender.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,17 @@

#include <exec/any_sender_of.hpp>
#include <exec/sequence.hpp>
#include <exec/sequence/any_sequence_of.hpp>
#include <exec/sequence/iterate.hpp>
#include <exec/sequence/transform_each.hpp>
#include <stdexec/execution.hpp>

#include <test_common/catch2.hpp>

#include <exception>
#include <ranges>
#include <type_traits>
#include <vector>

namespace ex = STDEXEC;

Expand All @@ -54,6 +60,29 @@ TEST_CASE("sequence with 3 any_senders compiles and runs", "[sequence][any_sende
CHECK(result == 42);
}

// Regression test for https://github.com/NVIDIA/stdexec/issues/2111:
// composing an erased sequence sender with transform_each used to fail to
// compile when STDEXEC_ENABLE_EXTRA_TYPE_CHECKING is ON, because the
// type-checking paths instantiated __debug_sender for sequence senders (via
// completion_signatures_of_t) and transformed the (immovable) child
// environment by value.
TEST_CASE("any_sequence_sender composed with transform_each under extra type checking",
"[sequence][any_sender]")
{
using SigsInt = ex::completion_signatures<ex::set_value_t(int),
ex::set_error_t(std::exception_ptr),
ex::set_stopped_t()>;

using AnySeqInt = exec::any_sequence_sender<exec::any_sequence_receiver<SigsInt>>;

std::vector<int> values{1, 2};
AnySeqInt seq = exec::iterate(std::views::all(values));

auto composed = std::move(seq) | exec::transform_each(ex::then([](int v) { return v + 1; }));

STATIC_REQUIRE(exec::sequence_sender_in<decltype(composed), ex::env<>>);
}

TEST_CASE("sequence with 4 any_senders compiles and runs", "[sequence][any_sender]")
{
using SigsVoid =
Expand Down
Loading