Skip to content
Open
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
59 changes: 11 additions & 48 deletions include/exec/function.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -314,10 +314,15 @@ namespace experimental::execution
concept __completion_domain_matches_impl =
__same_as<_ExpectedDomain, __common_domain_t<_ActualDomain, _ExpectedDomain>>;

//! A function makes no claim about where it completes with a tag for which
//! it declares no completion domain, so only declared domains are checked.
//! (Asked with an environment, get_completion_domain answers default_domain
//! for attributes that declare nothing, so the check can't rely on that.)
template <class _Tag, class _ActualAttrs, class _ExpectedAttrs, class... _Env>
concept __completion_domain_matches =
__completion_domain_matches_impl<__completion_domain_t<_Tag, _ActualAttrs, _Env...>,
__completion_domain_t<_Tag, _ExpectedAttrs, _Env...>>;
!__queryable_with<_ExpectedAttrs, get_completion_domain_t<_Tag>>
|| __completion_domain_matches_impl<__completion_domain_t<_Tag, _ActualAttrs, _Env...>,
__completion_domain_t<_Tag, _ExpectedAttrs, _Env...>>;

template <class _ActualAttrs, class _ExpectedAttrs, class... _Env>
concept __completion_domains_match_impl =
Expand Down Expand Up @@ -1117,46 +1122,6 @@ namespace experimental::execution
completion_signatures<__single_value_sig_t<__return_type_t<_Ty>>, set_stopped_t()>,
__eptr_completion_unless_t<__mbool<__is_noexcept<_Ty>>>>>;

//! maps a completion signature to the default completion domain query
struct __domain_query_from_sig
{
template <class _Tag, class... _Args>
consteval auto operator()(_Tag (*)(_Args...)) const noexcept //
-> default_domain (*)(get_completion_domain_t<_Tag>) noexcept
{
return nullptr;
}
};

//! maps a pack of domain queries produced by __domain_query_from_sig to the
//! corresponding attrs<_Attrs...> type
class __attrs_from_domain_queries
{
template <class _Tag>
using __query_sig = default_domain (*)(get_completion_domain_t<_Tag>) noexcept;

public:
template <class... _Tag>
consteval auto operator()(__query_sig<_Tag>...) const noexcept //
-> __canonical_t<attrs<default_domain(get_completion_domain_t<_Tag>) noexcept...>>
{
return {};
}
};

//! computes the set of get_completion_domain queries that must be supported
//! by any sender that might be erased by the corresponding function
//!
//! we should support get_completion_domain<Tag> only if _Sigs contains a
//! completion of type Tag
//!
//! the query form should be
//!
//! default_domain(get_completion_domain_t<Tag>)
template <class _Sigs>
using __default_attrs = decltype(_Sigs::__transform_reduce(__domain_query_from_sig(),
__attrs_from_domain_queries()));

//! Map a variety of function<...> specifications into the canonical type-erased
//! contract represented by the user-provided specification.
//!
Expand Down Expand Up @@ -1191,13 +1156,11 @@ namespace experimental::execution
//! signatures since they are derived from _Signature.

template <__is_not_sender_tag_function _Signature>
struct __make_function<_Signature>
: __make_function<_Signature, queries<>, __default_attrs<__completion_sigs_from<_Signature>>>
struct __make_function<_Signature> : __make_function<_Signature, queries<>, attrs<>>
{};

template <__is_not_sender_tag_function _Signature, __is_instance_of<queries> _Queries>
struct __make_function<_Signature, _Queries>
: __make_function<_Signature, _Queries, __default_attrs<__completion_sigs_from<_Signature>>>
struct __make_function<_Signature, _Queries> : __make_function<_Signature, _Queries, attrs<>>
{};

template <__is_not_sender_tag_function _Signature, __is_instance_of<attrs> _Attrs>
Expand Down Expand Up @@ -1232,14 +1195,14 @@ namespace experimental::execution
template <__is_sender_tag_function _Signature,
__is_instance_of<completion_signatures> _ComplSigs>
struct __make_function<_Signature, _ComplSigs>
: __make_function<_Signature, _ComplSigs, queries<>, __default_attrs<_ComplSigs>>
: __make_function<_Signature, _ComplSigs, queries<>, attrs<>>
{};

template <__is_sender_tag_function _Signature,
__is_instance_of<completion_signatures> _ComplSigs,
__is_instance_of<queries> _Queries>
struct __make_function<_Signature, _ComplSigs, _Queries>
: __make_function<_Signature, _ComplSigs, _Queries, __default_attrs<_ComplSigs>>
: __make_function<_Signature, _ComplSigs, _Queries, attrs<>>
{};

template <__is_sender_tag_function _Signature,
Expand Down
130 changes: 58 additions & 72 deletions test/exec/test_function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1144,75 +1144,39 @@ namespace
inline constexpr auto get_completion_domain =
ex::__first_callable{ex::get_completion_domain<Completion>, ex::__always{none_such()}};

TEST_CASE("function reports a default completion domain by default", "[types][function]")
{
SECTION("throwing function reports a completion domain for all three channels")
{
exec::function<void()> fn(ex::just);
auto attrs = ex::get_env(fn);
auto value_domain = get_completion_domain<ex::set_value_t>(attrs);
auto error_domain = get_completion_domain<ex::set_error_t>(attrs);
auto stop_domain = get_completion_domain<ex::set_stopped_t>(attrs);

STATIC_REQUIRE(std::same_as<ex::default_domain, decltype(value_domain)>);
STATIC_REQUIRE(std::same_as<ex::default_domain, decltype(error_domain)>);
STATIC_REQUIRE(std::same_as<ex::default_domain, decltype(stop_domain)>);
}

SECTION("no-throw function reports a completion domain for value and stop channels only")
{
exec::function<void() noexcept> fn(ex::just);
auto attrs = ex::get_env(fn);
auto value_domain = get_completion_domain<ex::set_value_t>(attrs);
auto error_domain = get_completion_domain<ex::set_error_t>(attrs);
auto stop_domain = get_completion_domain<ex::set_stopped_t>(attrs);
template <class Tag>
using default_domain_for = exec::attrs<ex::default_domain(ex::get_completion_domain_t<Tag>)>;

STATIC_REQUIRE(std::same_as<ex::default_domain, decltype(value_domain)>);
STATIC_REQUIRE(std::same_as<none_such, decltype(error_domain)>);
STATIC_REQUIRE(std::same_as<ex::default_domain, decltype(stop_domain)>);
}
template <class Completion, class Function>
using completion_domain_of_t = std::remove_cvref_t<decltype(get_completion_domain<Completion>(
ex::get_env(std::declval<Function const &>())))>;

SECTION("infallible function reports a completion domain for value channel only")
{
exec::function<ex::sender_tag(), ex::completion_signatures<ex::set_value_t()>> fn(ex::just);
auto attrs = ex::get_env(fn);
auto value_domain = get_completion_domain<ex::set_value_t>(attrs);
auto error_domain = get_completion_domain<ex::set_error_t>(attrs);
auto stop_domain = get_completion_domain<ex::set_stopped_t>(attrs);

STATIC_REQUIRE(std::same_as<ex::default_domain, decltype(value_domain)>);
STATIC_REQUIRE(std::same_as<none_such, decltype(error_domain)>);
STATIC_REQUIRE(std::same_as<none_such, decltype(stop_domain)>);
}

SECTION("just_error function reports a completion domain for error channel only")
{
exec::function<ex::sender_tag(int), ex::completion_signatures<ex::set_error_t(int)>> fn(
42,
ex::just_error);
auto attrs = ex::get_env(fn);
auto value_domain = get_completion_domain<ex::set_value_t>(attrs);
auto error_domain = get_completion_domain<ex::set_error_t>(attrs);
auto stop_domain = get_completion_domain<ex::set_stopped_t>(attrs);

STATIC_REQUIRE(std::same_as<none_such, decltype(value_domain)>);
STATIC_REQUIRE(std::same_as<ex::default_domain, decltype(error_domain)>);
STATIC_REQUIRE(std::same_as<none_such, decltype(stop_domain)>);
}
template <class Function>
inline constexpr bool reports_no_completion_domain =
std::same_as<none_such, completion_domain_of_t<ex::set_value_t, Function>>
&& std::same_as<none_such, completion_domain_of_t<ex::set_error_t, Function>>
&& std::same_as<none_such, completion_domain_of_t<ex::set_stopped_t, Function>>;

SECTION("just_stopped function reports a completion domain for stop channel only")
{
exec::function<ex::sender_tag(), ex::completion_signatures<ex::set_stopped_t()>> fn(
ex::just_stopped);
auto attrs = ex::get_env(fn);
auto value_domain = get_completion_domain<ex::set_value_t>(attrs);
auto error_domain = get_completion_domain<ex::set_error_t>(attrs);
auto stop_domain = get_completion_domain<ex::set_stopped_t>(attrs);
TEST_CASE("function reports no completion domain it doesn't declare", "[types][function]")
{
STATIC_REQUIRE(reports_no_completion_domain<exec::function<void()>>);
STATIC_REQUIRE(reports_no_completion_domain<exec::function<void() noexcept>>);
STATIC_REQUIRE(reports_no_completion_domain<
exec::function<ex::sender_tag(), ex::completion_signatures<ex::set_value_t()>>>);
STATIC_REQUIRE(reports_no_completion_domain<
exec::function<ex::sender_tag(),
ex::completion_signatures<ex::set_value_t(), ex::set_stopped_t()>,
exec::queries<>,
exec::attrs<>>>);

using declares_value = exec::function<void(), default_domain_for<ex::set_value_t>>;

STATIC_REQUIRE(std::same_as<none_such, decltype(value_domain)>);
STATIC_REQUIRE(std::same_as<none_such, decltype(error_domain)>);
STATIC_REQUIRE(std::same_as<ex::default_domain, decltype(stop_domain)>);
}
STATIC_REQUIRE(
std::same_as<ex::default_domain, completion_domain_of_t<ex::set_value_t, declares_value>>);
STATIC_REQUIRE(
std::same_as<none_such, completion_domain_of_t<ex::set_error_t, declares_value>>);
STATIC_REQUIRE(
std::same_as<none_such, completion_domain_of_t<ex::set_stopped_t, declares_value>>);
}

struct domain : ex::default_domain
Expand All @@ -1224,8 +1188,10 @@ namespace

SECTION("the constraint applies to set_value")
{
using function =
exec::function<ex::sender_tag(), ex::completion_signatures<ex::set_value_t()>, queries>;
using function = exec::function<ex::sender_tag(),
ex::completion_signatures<ex::set_value_t()>,
queries,
default_domain_for<ex::set_value_t>>;

STATIC_REQUIRE(std::constructible_from<function, ex::just_t>);

Expand All @@ -1244,7 +1210,8 @@ namespace
{
using function = exec::function<ex::sender_tag(int),
ex::completion_signatures<ex::set_error_t(int)>,
queries>;
queries,
default_domain_for<ex::set_error_t>>;

STATIC_REQUIRE(std::constructible_from<function, int, ex::just_error_t>);

Expand All @@ -1261,8 +1228,10 @@ namespace

SECTION("the constraint applies to set_stopped")
{
using function =
exec::function<ex::sender_tag(), ex::completion_signatures<ex::set_stopped_t()>, queries>;
using function = exec::function<ex::sender_tag(),
ex::completion_signatures<ex::set_stopped_t()>,
queries,
default_domain_for<ex::set_stopped_t>>;

STATIC_REQUIRE(std::constructible_from<function, ex::just_stopped_t>);

Expand Down Expand Up @@ -1308,7 +1277,7 @@ namespace
STATIC_REQUIRE(std::same_as<ex::default_domain, decltype(fn_domain)>);
}

SECTION("a forwarded domain is accepted under the default only if it derives from it")
SECTION("a function that declares no completion domain doesn't check one")
{
struct unrelated_domain
{};
Expand All @@ -1318,7 +1287,24 @@ namespace
exec::function<void(), exec::queries<unrelated_domain(ex::get_domain_t) noexcept>>;

STATIC_REQUIRE(std::constructible_from<derived, ex::just_t>);
STATIC_REQUIRE(!std::constructible_from<unrelated, ex::just_t>);
STATIC_REQUIRE(std::constructible_from<unrelated, ex::just_t>);
}

SECTION("a function checks only the completion domains it declares")
{
struct unrelated_domain
{};

using function =
exec::function<ex::sender_tag(),
ex::completion_signatures<ex::set_value_t(), ex::set_stopped_t()>,
exec::queries<>,
default_domain_for<ex::set_value_t>>;

STATIC_REQUIRE(
std::constructible_from<function, domain_sender_t<ex::set_stopped_t, unrelated_domain>>);
STATIC_REQUIRE(
!std::constructible_from<function, domain_sender_t<ex::set_value_t, unrelated_domain>>);
}

SECTION("declaring the forwarded domain reports it")
Expand Down