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
6 changes: 4 additions & 2 deletions include/exec/sequence/merge_each.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1095,8 +1095,10 @@ namespace experimental::execution
using sender_concept = STDEXEC::sender_tag;

template <class _NextReceiver>
using __next_sequence_op_t =
__next_sequence_op<_NestedSequenceSender, _NextReceiver, _OperationBase, _NestedSeqOp>;
using __next_sequence_op_t = __next_sequence_op<_NestedSequenceSender,
__decay_t<_NextReceiver>,
_OperationBase,
_NestedSeqOp>;

template <class _Self, class... _Env>
static consteval auto get_completion_signatures() noexcept
Expand Down
2 changes: 1 addition & 1 deletion include/stdexec/__detail/__parallel_scheduler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ namespace STDEXEC
auto st = get_stop_token(STDEXEC::get_env(__state_.__rcvr_));
if (st.stop_requested())
{
STDEXEC::set_stopped(__state_.__rcvr_);
STDEXEC::set_stopped(static_cast<__rcvr_t&&>(__state_.__rcvr_));
return;
}

Expand Down
10 changes: 7 additions & 3 deletions include/stdexec/__detail/__receivers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ namespace STDEXEC
{
namespace __detail
{
// For deduced forwarding-reference arguments, rejects lvalues and const rvalues.
template <class _Ty>
concept __non_const_rvalue = !STDEXEC_IS_SAME(_Ty, _Ty const);

template <__disposition _Disposition>
struct __completion_tag
{
Expand Down Expand Up @@ -114,7 +118,7 @@ namespace STDEXEC
//! `.set_value(_As...)` member.
//! @tparam _As The value-datum argument types.
template <class _Receiver, class... _As>
requires __set_value_member<_Receiver, _As...>
requires __detail::__non_const_rvalue<_Receiver> && __set_value_member<_Receiver, _As...>
STDEXEC_ATTRIBUTE(host, device, always_inline)
constexpr void operator()(_Receiver &&__rcvr, _As &&...__as) const noexcept
{
Expand Down Expand Up @@ -182,7 +186,7 @@ namespace STDEXEC
//! @tparam _Receiver A type with a matching `.set_error(_Error)` member.
//! @tparam _Error The error datum type.
template <class _Receiver, class _Error>
requires __set_error_member<_Receiver, _Error>
requires __detail::__non_const_rvalue<_Receiver> && __set_error_member<_Receiver, _Error>
STDEXEC_ATTRIBUTE(host, device, always_inline)
constexpr void operator()(_Receiver &&__rcvr, _Error &&__err) const noexcept
{
Expand Down Expand Up @@ -248,7 +252,7 @@ namespace STDEXEC
//! @tparam _Receiver A type with a matching nullary
//! `.set_stopped()` member.
template <class _Receiver>
requires __set_stopped_member<_Receiver>
requires __detail::__non_const_rvalue<_Receiver> && __set_stopped_member<_Receiver>
STDEXEC_ATTRIBUTE(host, device, always_inline)
constexpr void operator()(_Receiver &&__rcvr) const noexcept
{
Expand Down
68 changes: 34 additions & 34 deletions test/stdexec/cpos/test_cpo_receiver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ namespace

struct recv_value
{
int* target_;
int *target_;

void set_value(int val) noexcept
{
Expand All @@ -55,7 +55,7 @@ namespace

struct recv_rvalref
{
int* target_;
int *target_;

void set_value(int val) noexcept
{
Expand All @@ -75,7 +75,7 @@ namespace

struct recv_ref
{
int* target_;
int *target_;

void set_value(int val) noexcept
{
Expand All @@ -95,7 +95,7 @@ namespace

struct recv_cref
{
int* target_;
int *target_;

void set_value(int val) noexcept
{
Expand All @@ -113,6 +113,14 @@ namespace
}
};

// Const members ensure that rejection comes from the CPO constraints.
struct recv_const
{
void set_value(int) const noexcept {}
void set_error(int) const noexcept {}
void set_stopped() const noexcept {}
};

TEST_CASE("can call set_value on a void receiver", "[cpo][cpo_receiver]")
{
ex::set_value(expect_void_receiver{});
Expand Down Expand Up @@ -170,16 +178,6 @@ namespace
REQUIRE(val == 10);
}

TEST_CASE("can call set_value on a receiver with ref type", "[cpo][cpo_receiver]")
{
static_assert(std::invocable<ex::set_value_t, recv_ref&, int>,
"cannot call set_value on recv_ref");
int val = 0;
recv_ref recv{&val};
ex::set_value(recv, 10);
REQUIRE(val == 10);
}

TEST_CASE("can call set_value on a receiver with const ref type", "[cpo][cpo_receiver]")
{
static_assert(std::invocable<ex::set_value_t, recv_cref, int>,
Expand Down Expand Up @@ -207,16 +205,6 @@ namespace
REQUIRE(val == -10);
}

TEST_CASE("can call set_error on a receiver with ref type", "[cpo][cpo_receiver]")
{
static_assert(std::invocable<ex::set_error_t, recv_ref&, int>,
"cannot call set_error on recv_ref");
int val = 0;
recv_ref recv{&val};
ex::set_error(recv, 10);
REQUIRE(val == -10);
}

TEST_CASE("can call set_error on a receiver with const ref type", "[cpo][cpo_receiver]")
{
static_assert(std::invocable<ex::set_error_t, recv_cref, int>,
Expand Down Expand Up @@ -244,16 +232,6 @@ namespace
REQUIRE(val == INT_MAX);
}

TEST_CASE("can call set_stopped on a receiver with ref type", "[cpo][cpo_receiver]")
{
static_assert(std::invocable<ex::set_stopped_t, recv_ref&>,
"cannot call set_stopped on recv_ref");
int val = 0;
recv_ref recv{&val};
ex::set_stopped(recv);
REQUIRE(val == INT_MAX);
}

TEST_CASE("can call set_stopped on a receiver with const ref type", "[cpo][cpo_receiver]")
{
static_assert(std::invocable<ex::set_stopped_t, recv_cref>,
Expand All @@ -263,6 +241,28 @@ namespace
REQUIRE(val == INT_MAX);
}

TEST_CASE("completion CPOs reject lvalue receivers", "[cpo][cpo_receiver]")
{
static_assert(!std::invocable<ex::set_value_t, recv_const &, int>);
static_assert(!std::invocable<ex::set_error_t, recv_const &, int>);
static_assert(!std::invocable<ex::set_stopped_t, recv_const &>);

static_assert(!std::invocable<ex::set_value_t, recv_const const &, int>);
static_assert(!std::invocable<ex::set_error_t, recv_const const &, int>);
static_assert(!std::invocable<ex::set_stopped_t, recv_const const &>);
}

TEST_CASE("completion CPOs reject const rvalue receivers", "[cpo][cpo_receiver]")
{
static_assert(std::invocable<ex::set_value_t, recv_const &&, int>);
static_assert(std::invocable<ex::set_error_t, recv_const &&, int>);
static_assert(std::invocable<ex::set_stopped_t, recv_const &&>);

static_assert(!std::invocable<ex::set_value_t, recv_const const &&, int>);
static_assert(!std::invocable<ex::set_error_t, recv_const const &&, int>);
static_assert(!std::invocable<ex::set_stopped_t, recv_const const &&>);
}

TEST_CASE("tag types can be deduced from set_value, set_error and set_stopped",
"[cpo][cpo_receiver]")
{
Expand Down
Loading