Skip to content

Commit

Permalink
Relax overload resolution of any_receiver's completion functions
Browse files Browse the repository at this point in the history
  • Loading branch information
maikel committed Dec 18, 2024
1 parent 184a8ab commit 78963fe
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 32 deletions.
49 changes: 25 additions & 24 deletions include/exec/any_sender_of.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -440,9 +440,7 @@ namespace exec {
(*__other.__vtable_)(__copy_construct, this, __other);
}

auto operator=(const __t& __other) -> __t&
requires(_Copyable)
{
auto operator=(const __t& __other) -> __t& requires(_Copyable) {
if (&__other != this) {
__t tmp(__other);
*this = std::move(tmp);
Expand Down Expand Up @@ -520,7 +518,8 @@ namespace exec {
}

template <class _Tp>
STDEXEC_MEMFN_DECL(void __move_construct)(this __mtype<_Tp>, __t& __self, __t&& __other) noexcept {
STDEXEC_MEMFN_DECL(
void __move_construct)(this __mtype<_Tp>, __t& __self, __t&& __other) noexcept {
if (!__other.__object_pointer_) {
return;
}
Expand All @@ -540,8 +539,7 @@ namespace exec {

template <class _Tp>
requires _Copyable
STDEXEC_MEMFN_DECL(
void __copy_construct)(this __mtype<_Tp>, __t& __self, const __t& __other) {
STDEXEC_MEMFN_DECL(void __copy_construct)(this __mtype<_Tp>, __t& __self, const __t& __other) {
if (!__other.__object_pointer_) {
return;
}
Expand All @@ -563,7 +561,8 @@ namespace exec {

struct __empty_vtable {
template <class _Sender>
STDEXEC_MEMFN_DECL(auto __create_vtable)(this __mtype<__empty_vtable>, __mtype<_Sender>) noexcept
STDEXEC_MEMFN_DECL(
auto __create_vtable)(this __mtype<__empty_vtable>, __mtype<_Sender>) noexcept
-> const __empty_vtable* {
static const __empty_vtable __vtable_{};
return &__vtable_;
Expand Down Expand Up @@ -616,11 +615,12 @@ namespace exec {
, public __query_vfun<_Queries>... {
public:
using __query_vfun<_Queries>::operator()...;
using __any_::__rcvr_vfun<_Sigs>::operator()...;

private:
template <class _Rcvr>
requires receiver_of<_Rcvr, completion_signatures<_Sigs...>>
&& (__callable<__query_vfun_fn<_Rcvr>, _Queries> && ...)
&& (__callable<__query_vfun_fn<_Rcvr>, _Queries> && ...)
STDEXEC_MEMFN_DECL(
auto __create_vtable)(this __mtype<__t>, __mtype<_Rcvr>) noexcept -> const __t* {
static const __t __vtable_{
Expand Down Expand Up @@ -675,33 +675,30 @@ namespace exec {
}

template <class... _As>
requires __one_of<set_value_t(_As...), _Sigs...>
requires __callable<__vtable_t, void*, set_value_t, _As...>
void set_value(_As&&... __as) noexcept {
const __any_::__rcvr_vfun<set_value_t(_As...)>* __vfun = __env_.__vtable_;
(*__vfun->__complete_)(__env_.__rcvr_, static_cast<_As&&>(__as)...);
(*__env_.__vtable_)(__env_.__rcvr_, set_value_t(), static_cast<_As&&>(__as)...);
}

template <class _Error>
requires __one_of<set_error_t(_Error), _Sigs...>
requires __callable<__vtable_t, void*, set_error_t, _Error>
void set_error(_Error&& __err) noexcept {
const __any_::__rcvr_vfun<set_error_t(_Error)>* __vfun = __env_.__vtable_;
(*__vfun->__complete_)(__env_.__rcvr_, static_cast<_Error&&>(__err));
(*__env_.__vtable_)(__env_.__rcvr_, set_error_t(), static_cast<_Error&&>(__err));
}

void set_stopped() noexcept
requires __one_of<set_stopped_t(), _Sigs...>
requires __callable<__vtable_t, void*, set_stopped_t>
{
const __any_::__rcvr_vfun<set_stopped_t()>* __vfun = __env_.__vtable_;
(*__vfun->__complete_)(__env_.__rcvr_);
(*__env_.__vtable_)(__env_.__rcvr_, set_stopped_t());
}

auto get_env() const noexcept -> const __env_t& {
return __env_;
}
};

auto __test_never_stop_token(
get_stop_token_t (*)(never_stop_token (*)() noexcept)) -> __mbool<true>;
auto __test_never_stop_token(get_stop_token_t (*)(never_stop_token (*)() noexcept))
-> __mbool<true>;

template <class _Tag, class _Ret, class... _As>
auto __test_never_stop_token(_Tag (*)(_Ret (*)(_As...) noexcept)) -> __mbool<false>;
Expand Down Expand Up @@ -783,7 +780,8 @@ namespace exec {

private:
template <class _Op>
STDEXEC_MEMFN_DECL(auto __create_vtable)(this __mtype<__operation_vtable>, __mtype<_Op>) noexcept
STDEXEC_MEMFN_DECL(
auto __create_vtable)(this __mtype<__operation_vtable>, __mtype<_Op>) noexcept
-> const __operation_vtable* {
static __operation_vtable __vtable{[](void* __object_pointer) noexcept -> void {
STDEXEC_ASSERT(__object_pointer);
Expand Down Expand Up @@ -831,8 +829,7 @@ namespace exec {

template <same_as<__t> _Self, class _Item>
requires __callable<set_next_t, _Receiver&, _Item>
STDEXEC_MEMFN_DECL(
auto set_next)(this _Self& __self, _Item&& __item) noexcept
STDEXEC_MEMFN_DECL(auto set_next)(this _Self& __self, _Item&& __item) noexcept
-> __call_result_t<set_next_t, _Receiver&, _Item> {
return exec::set_next(__self.__op_->__rcvr_, static_cast<_Item&&>(__item));
}
Expand Down Expand Up @@ -961,7 +958,9 @@ namespace exec {
__immovable_operation_storage (*__connect_)(void*, __receiver_ref_t);
private:
template <sender_to<__receiver_ref_t> _Sender>
STDEXEC_MEMFN_DECL(auto __create_vtable)(this __mtype<__vtable>, __mtype<_Sender>) noexcept -> const __vtable* {
STDEXEC_MEMFN_DECL(
auto
__create_vtable)(this __mtype<__vtable>, __mtype<_Sender>) noexcept -> const __vtable* {
static const __vtable __vtable_{
{*__create_vtable(__mtype<__query_vtable<_SenderQueries>>{}, __mtype<_Sender>{})},
[](void* __object_pointer, __receiver_ref_t __receiver)
Expand Down Expand Up @@ -1083,7 +1082,9 @@ namespace exec {
}
private:
template <scheduler _Scheduler>
STDEXEC_MEMFN_DECL(auto __create_vtable)(this __mtype<__vtable>, __mtype<_Scheduler>) noexcept -> const __vtable* {
STDEXEC_MEMFN_DECL(
auto
__create_vtable)(this __mtype<__vtable>, __mtype<_Scheduler>) noexcept -> const __vtable* {
static const __vtable __vtable_{
{*__create_vtable(
__mtype<__query_vtable<_SchedulerQueries, false>>{}, __mtype<_Scheduler>{})},
Expand Down
12 changes: 8 additions & 4 deletions include/stdexec/__detail/__receiver_ref.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,16 @@ namespace stdexec { namespace __any_ {

template <class _Tag, class... _Args>
struct __rcvr_vfun<_Tag(_Args...)> {
void (*__complete_)(void*, _Args&&...) noexcept;
void (*__complete_)(void*, _Args...) noexcept;

void operator()(void* __obj, _Tag, _Args&&... __args) const noexcept {
void operator()(void* __obj, _Tag, _Args... __args) const noexcept {
__complete_(__obj, static_cast<_Args&&>(__args)...);
}
};

template <class _GetReceiver = std::identity, class _Obj, class _Tag, class... _Args>
constexpr auto __rcvr_vfun_fn(_Obj*, _Tag (*)(_Args...)) noexcept {
return +[](void* __ptr, _Args&&... __args) noexcept {
return +[](void* __ptr, _Args... __args) noexcept {
_Obj* __obj = static_cast<_Obj*>(__ptr);
_Tag()(std::move(_GetReceiver()(*__obj)), static_cast<_Args&&>(__args)...);
};
Expand Down Expand Up @@ -95,16 +95,20 @@ namespace stdexec { namespace __any_ {
}

template <class... _As>
requires __callable<__receiver_vtable_for<_Sigs, _Env>, void*, set_value_t, _As...>
void set_value(_As&&... __as) noexcept {
(*__vtable_)(__op_state_, set_value_t(), static_cast<_As&&>(__as)...);
}

template <class _Error>
requires __callable<__receiver_vtable_for<_Sigs, _Env>, void*, set_error_t, _Error>
void set_error(_Error&& __err) noexcept {
(*__vtable_)(__op_state_, set_error_t(), static_cast<_Error&&>(__err));
}

void set_stopped() noexcept {
void set_stopped() noexcept
requires __callable<__receiver_vtable_for<_Sigs, _Env>, void*, set_stopped_t>
{
(*__vtable_)(__op_state_, set_stopped_t());
}

Expand Down
25 changes: 21 additions & 4 deletions test/exec/test_any_sender.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ namespace {
template <class T>
// BUGBUG ambiguous!
requires stdexec::tag_invocable<tag_t, T>
auto operator()(T&& t) const
noexcept(stdexec::nothrow_tag_invocable<tag_t, T>) -> stdexec::tag_invoke_result_t<tag_t, T> {
auto operator()(T&& t) const noexcept(stdexec::nothrow_tag_invocable<tag_t, T>)
-> stdexec::tag_invoke_result_t<tag_t, T> {
return stdexec::tag_invoke(*this, static_cast<T&&>(t));
}
};
Expand Down Expand Up @@ -328,6 +328,23 @@ namespace {
}
}

template <class... Vals>
using my_stoppable_sender_of =
any_sender_of<set_value_t(Vals)..., set_error_t(std::exception_ptr), set_stopped_t()>;

TEST_CASE("any_sender uses overload rules for completion signatures", "[types][any_sender]") {
auto split_sender = split(just(21));
static_assert(sender_of<decltype(split_sender), set_error_t(const std::exception_ptr&)>);
static_assert(sender_of<decltype(split_sender), set_value_t(const int&)>);
my_stoppable_sender_of<int> sender = split_sender;

auto [value] = *sync_wait(std::move(sender));
CHECK(value == 42);

sender = just(21) | then([&](int) -> int { throw 420; });
CHECK_THROWS_AS(sync_wait(std::move(sender)), int);
}

class stopped_token {
private:
bool stopped_{true};
Expand Down Expand Up @@ -686,8 +703,8 @@ namespace {
return {{}, static_cast<R&&>(r)};
}

auto
query(ex::get_completion_scheduler_t<ex::set_value_t>) const noexcept -> counting_scheduler {
auto query(ex::get_completion_scheduler_t<ex::set_value_t>) const noexcept
-> counting_scheduler {
return {};
}

Expand Down

0 comments on commit 78963fe

Please sign in to comment.