Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make completion signatures depend on the Receiver type #1337

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
8 changes: 4 additions & 4 deletions include/stdexec/__detail/__ensure_started.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,11 @@ namespace stdexec {
struct __ensure_started_t { };

struct ensure_started_t {
template <class _CvrefSender, class _Env>
using __receiver_t = __t<__meval<__receiver, __cvref_id<_CvrefSender>, __id<_Env>>>;

template <sender _Sender, class _Env = empty_env>
requires sender_in<_Sender, _Env> && __decay_copyable<env_of_t<_Sender>>
requires sender_in<_Sender, __receiver_t<_Sender, _Env>> && __decay_copyable<env_of_t<_Sender>>
[[nodiscard]]
auto
operator()(_Sender&& __sndr, _Env&& __env = {}) const -> __well_formed_sender auto {
Expand Down Expand Up @@ -70,9 +73,6 @@ namespace stdexec {
_Sender),
tag_invoke_t(ensure_started_t, _Sender)>;

template <class _CvrefSender, class _Env>
using __receiver_t = __t<__meval<__receiver, __cvref_id<_CvrefSender>, __id<_Env>>>;

template <class _Sender>
static auto transform_sender(_Sender&& __sndr) {
using _Receiver = __receiver_t<__child_of<_Sender>, __decay_t<__data_of<_Sender>>>;
Expand Down
9 changes: 9 additions & 0 deletions include/stdexec/__detail/__execution_fwd.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,18 @@ namespace stdexec {
using __t = empty_env;
using __id = empty_env;
};

struct empty_receiver
{
using __t = empty_receiver;
using __id = empty_receiver;
using receiver_concept = receiver_t;
auto get_env() const noexcept -> empty_env { return {}; }
};
} // namespace __env

using __env::empty_env;
using __env::empty_receiver;

template <class _EnvProvider>
using env_of_t = __call_result_t<get_env_t, _EnvProvider>;
Expand Down
2 changes: 1 addition & 1 deletion include/stdexec/__detail/__receivers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ namespace stdexec {

template <class _Receiver, class _Sender>
concept __receiver_from =
receiver_of<_Receiver, __completion_signatures_of_t<_Sender, env_of_t<_Receiver>>>;
receiver_of<_Receiver, __completion_signatures_of_t<_Sender, _Receiver>>;

/// A utility for calling set_value with the result of a function invocation:
template <bool _CanThrow = false, class _Receiver, class _Fun, class... _As>
Expand Down
63 changes: 32 additions & 31 deletions include/stdexec/__detail/__senders.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,53 +35,54 @@ namespace stdexec {
/////////////////////////////////////////////////////////////////////////////
// [execution.get_completion_signatures]
namespace __compl_sigs {
template <class _Sender, class _Env>
template <class _Sender, class _Receiver>
using __tfx_sender =
transform_sender_result_t<__late_domain_of_t<_Sender, _Env>, _Sender, _Env>;
transform_sender_result_t<__late_domain_of_t<_Sender, env_of_t<_Receiver>>, _Sender, env_of_t<_Receiver>>;

template <class _Sender, class _Env>
template <class _Sender, class _Receiver>
concept __with_tag_invoke = //
tag_invocable<get_completion_signatures_t, __tfx_sender<_Sender, _Env>, _Env>;
tag_invocable<get_completion_signatures_t, __tfx_sender<_Sender, _Receiver>, _Receiver>;

template <class _Sender, class _Env>
template <class _Sender, class _Receiver>
using __member_alias_t = //
typename __decay_t<__tfx_sender<_Sender, _Env>>::completion_signatures;
typename __decay_t<__tfx_sender<_Sender, _Receiver>>::completion_signatures;

template <class _Sender, class _Env = empty_env>
concept __with_member_alias = __mvalid<__member_alias_t, _Sender, _Env>;
template <class _Sender, class _Receiver = empty_receiver>
concept __with_member_alias = __mvalid<__member_alias_t, _Sender, _Receiver>;

struct get_completion_signatures_t {
template <__same_as<get_completion_signatures_t> _Self, sender _Sender, class... _Env>
constexpr friend auto tag_invoke(_Self, _Sender&& __sndr, _Env&&... __env) noexcept
template <__same_as<get_completion_signatures_t> _Self, sender _Sender, class... _Receiver>
constexpr friend auto tag_invoke(_Self, _Sender&& __sndr, _Receiver&&... __rcvr) noexcept
-> decltype(static_cast<_Sender&&>(__sndr).get_completion_signatures(
static_cast<_Env&&>(__env)...)) {
static_cast<_Receiver&&>(__rcvr)...)) {
return {};
}

template <__same_as<get_completion_signatures_t> _Self, sender _Sender, class... _Env>
constexpr friend auto tag_invoke(_Self, _Sender&& __sndr, _Env&&... __env) noexcept
template <__same_as<get_completion_signatures_t> _Self, sender _Sender, class... _Receiver>
constexpr friend auto tag_invoke(_Self, _Sender&& __sndr, _Receiver&&... __rcvr) noexcept
-> decltype(__decay_t<_Sender>::get_completion_signatures(
static_cast<_Sender&&>(__sndr),
static_cast<_Env&&>(__env)...)) {
static_cast<_Receiver&&>(__rcvr)...)) {
return {};
}

template <class _Sender, class _Env>
template <class _Sender, class _Receiver>
static auto __impl() {
static_assert(sizeof(_Sender), "Incomplete type used with get_completion_signatures");
static_assert(sizeof(_Env), "Incomplete type used with get_completion_signatures");
static_assert(sizeof(_Receiver), "Incomplete type used with get_completion_signatures");

// Compute the type of the transformed sender:
using _TfxSender = __tfx_sender<_Sender, _Env>;
using _TfxSender = __tfx_sender<_Sender, _Receiver>;
using _Env = env_of_t<_Receiver>;

if constexpr (__merror<_TfxSender>) {
// Computing the type of the transformed sender returned an error type. Propagate it.
return static_cast<_TfxSender (*)()>(nullptr);
} else if constexpr (__with_tag_invoke<_Sender, _Env>) {
using _Result = tag_invoke_result_t<get_completion_signatures_t, _TfxSender, _Env>;
} else if constexpr (__with_tag_invoke<_Sender, _Receiver>) {
using _Result = tag_invoke_result_t<get_completion_signatures_t, _TfxSender, _Receiver>;
return static_cast<_Result (*)()>(nullptr);
} else if constexpr (__with_member_alias<_Sender, _Env>) {
using _Result = __member_alias_t<_Sender, _Env>;
} else if constexpr (__with_member_alias<_Sender, _Receiver>) {
using _Result = __member_alias_t<_Sender, _Receiver>;
return static_cast<_Result (*)()>(nullptr);
} else if constexpr (__awaitable<_Sender, __env::__promise<_Env>>) {
using _AwaitResult = __await_result_t<_Sender, __env::__promise<_Env>>;
Expand All @@ -95,22 +96,22 @@ namespace stdexec {
using __tag_invoke::tag_invoke;
// This ought to cause a hard error that indicates where the problem is.
using _Completions
[[maybe_unused]] = tag_invoke_result_t<get_completion_signatures_t, _Sender, _Env>;
[[maybe_unused]] = tag_invoke_result_t<get_completion_signatures_t, _Sender, _Receiver>;
return static_cast<__debug::__completion_signatures (*)()>(nullptr);
} else {
using _Result = __mexception<
_UNRECOGNIZED_SENDER_TYPE_<>,
_WITH_SENDER_<_Sender>,
_WITH_ENVIRONMENT_<_Env>>;
_WITH_RECEIVER_<_Receiver>>;
return static_cast<_Result (*)()>(nullptr);
}
}

// NOT TO SPEC: if we're unable to compute the completion signatures,
// return an error type instead of SFINAE.
template <class _Sender, class _Env = empty_env>
constexpr auto operator()(_Sender&&, _Env&& = {}) const noexcept //
-> decltype(__impl<_Sender, _Env>()()) {
template <class _Sender, receiver _Receiver = empty_receiver>
constexpr auto operator()(_Sender&&, _Receiver&& = {}) const noexcept //
-> decltype(__impl<_Sender, _Receiver>()()) {
return {};
}
};
Expand All @@ -132,7 +133,7 @@ namespace stdexec {
template <class _Sender, class _Receiver>
concept __connectable_with_tag_invoke_ = //
receiver<_Receiver> //
&& sender_in<_Sender, env_of_t<_Receiver>> //
&& sender_in<_Sender, _Receiver> //
&& __receiver_from<_Receiver, _Sender> //
&& tag_invocable<connect_t, _Sender, _Receiver>;

Expand Down Expand Up @@ -236,7 +237,7 @@ namespace stdexec {
template <class _Sender, class _Receiver>
concept sender_to = //
receiver<_Receiver> //
&& sender_in<_Sender, env_of_t<_Receiver>> //
&& sender_in<_Sender, _Receiver> //
&& __receiver_from<_Receiver, _Sender> //
&& requires(_Sender&& __sndr, _Receiver&& __rcvr) {
connect(static_cast<_Sender&&>(__sndr), static_cast<_Receiver&&>(__rcvr));
Expand All @@ -247,15 +248,15 @@ namespace stdexec {
template <class _Sig>
using __tag_of_sig_t = decltype(stdexec::__tag_of_sig_(static_cast<_Sig*>(nullptr)));

template <class _Sender, class _SetSig, class _Env = empty_env>
template <class _Sender, class _SetSig, class _Receiver = empty_receiver>
concept sender_of = //
sender_in<_Sender, _Env> //
sender_in<_Sender, _Receiver> //
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the sender_in concept is no longer needed, right? sender_to is enough.

&& same_as<
__types<_SetSig>,
__gather_completions_for<
__tag_of_sig_t<_SetSig>,
_Sender,
_Env,
_Receiver,
__qf<__tag_of_sig_t<_SetSig>>,
__q<__types>>>;
} // namespace stdexec
8 changes: 4 additions & 4 deletions include/stdexec/__detail/__senders_core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ namespace stdexec {
&& move_constructible<__decay_t<_Sender>> //
&& constructible_from<__decay_t<_Sender>, _Sender>;

template <class _Sender, class... _Env>
template <class _Sender, class... _Receiver>
concept sender_in =
(sizeof...(_Env) <= 1) //
(sizeof...(_Receiver) <= 1) //
&& sender<_Sender> //
&& requires(_Sender&& __sndr, _Env&&... __env) {
&& requires(_Sender&& __sndr, _Receiver&&... __rcvr) {
{
get_completion_signatures(static_cast<_Sender&&>(__sndr), static_cast<_Env&&>(__env)...)
get_completion_signatures(static_cast<_Sender&&>(__sndr), static_cast<_Receiver&&>(__rcvr)...)
} -> __valid_completion_signatures;
};

Expand Down
8 changes: 4 additions & 4 deletions include/stdexec/__detail/__split.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,11 @@ namespace stdexec {
struct __split_t { };

struct split_t {
template <class _CvrefSender, class _Env>
using __receiver_t = __t<__meval<__receiver, __cvref_id<_CvrefSender>, __id<_Env>>>;

template <sender _Sender, class _Env = empty_env>
requires sender_in<_Sender, _Env> && __decay_copyable<env_of_t<_Sender>>
requires sender_in<_Sender, __receiver_t<_Sender, _Env>> && __decay_copyable<env_of_t<_Sender>>
auto operator()(_Sender&& __sndr, _Env&& __env = {}) const -> __well_formed_sender auto {
auto __domain = __get_late_domain(__sndr, __env);
return stdexec::transform_sender(
Expand All @@ -63,9 +66,6 @@ namespace stdexec {
_Sender),
tag_invoke_t(split_t, _Sender)>;

template <class _CvrefSender, class _Env>
using __receiver_t = __t<__meval<__receiver, __cvref_id<_CvrefSender>, __id<_Env>>>;

template <class _Sender>
static auto transform_sender(_Sender&& __sndr) {
using _Receiver = __receiver_t<__child_of<_Sender>, __decay_t<__data_of<_Sender>>>;
Expand Down