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
26 changes: 11 additions & 15 deletions include/exec/sequence_senders.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ namespace experimental::execution
{
template <class _Receiver, class _Item>
concept __has_set_next_member = requires(_Receiver& __rcvr, _Item&& __item) {
__rcvr.set_next(static_cast<_Item&&>(__item));
__rcvr.set_next(static_cast<_Item &&>(__item));
};

// This is a sequence-receiver CPO that is used to apply algorithms on an input sender and it
Expand Down Expand Up @@ -426,8 +426,8 @@ namespace experimental::execution
template <class _Sequence, class... _Env>
concept has_sequence_item_types = STDEXEC::sender_in<_Sequence, _Env...> //
&& requires(_Sequence&& __sequence, _Env&&... __env) {
get_item_types(static_cast<_Sequence&&>(__sequence),
static_cast<_Env&&>(__env)...);
get_item_types(static_cast<_Sequence &&>(__sequence),
static_cast<_Env &&>(__env)...);
};

template <class _Sequence, class... _Env>
Expand Down Expand Up @@ -518,8 +518,8 @@ namespace experimental::execution
concept sequence_sender_in = sequence_sender<_Sequence, _Env...>
&& requires(_Sequence&& __sequence, _Env&&... __env) {
{
get_item_types(static_cast<_Sequence&&>(__sequence),
static_cast<_Env&&>(__env)...)
get_item_types(static_cast<_Sequence &&>(__sequence),
static_cast<_Env &&>(__env)...)
} -> __well_formed_item_types<_Sequence>;
};

Expand Down Expand Up @@ -557,8 +557,7 @@ namespace experimental::execution
#endif

template <class _Data, class... _What>
struct __sequence_type_check_failure //
: STDEXEC::__compile_time_error<__sequence_type_check_failure<_Data, _What...>>
struct __sequence_type_check_failure : STDEXEC::__compile_time_error
{
static_assert(std::is_nothrow_move_constructible_v<_Data>,
"The data member of sender_type_check_failure must be nothrow move "
Expand All @@ -570,16 +569,14 @@ namespace experimental::execution
: __data_(static_cast<_Data&&>(data))
{}

private:
friend struct STDEXEC::__compile_time_error<__sequence_type_check_failure>;

[[nodiscard]]
constexpr auto what() const noexcept -> char const *
constexpr auto what() const noexcept -> char const * // NOLINT(modernize-use-override)
{
return "This sequence sender is not well-formed. It does not meet the requirements of a "
"sequence sender type.";
}

// public so that __sequence_type_check_failure is a structural type
_Data __data_{};
};

Expand Down Expand Up @@ -845,10 +842,9 @@ namespace experimental::execution
return STDEXEC::connect(static_cast<next_sender_of_t<_Receiver, __tfx_seq_t>&&>(__next),
__stopped_means_break<_Receiver>{
static_cast<_Receiver&&>(__rcvr)});
// NOLINTNEXTLINE(bugprone-branch-clone)
}
else if constexpr (__subscribable_with_static_member<__tfx_seq_t, _Receiver>)
{
{ // NOLINT(bugprone-branch-clone)
return __tfx_seq.subscribe(static_cast<__tfx_seq_t&&>(__tfx_seq),
static_cast<_Receiver&&>(__rcvr));
}
Expand Down Expand Up @@ -929,8 +925,8 @@ namespace experimental::execution
template <class _Sequence, class _Receiver>
concept sequence_sender_to = sequence_receiver_from<_Receiver, _Sequence>
&& requires(_Sequence&& __sequence, _Receiver&& __rcvr) {
subscribe(static_cast<_Sequence&&>(__sequence),
static_cast<_Receiver&&>(__rcvr));
subscribe(static_cast<_Sequence &&>(__sequence),
static_cast<_Receiver &&>(__rcvr));
};

template <class _Receiver>
Expand Down
16 changes: 16 additions & 0 deletions include/stdexec/__detail/__concepts.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,22 @@ namespace STDEXEC
template <class _Ay, template <class...> class _Ty>
concept __is_not_instance_of = !__is_instance_of<_Ay, _Ty>;

template <auto>
concept __constant = true;

namespace __detail
{
template <auto>
using __is_nttp = void;
template <class _Ty, template <_Ty> class>
using __nttp_test = void;
} // namespace __detail

template <class _Ty>
concept __structural = requires { typename __detail::__nttp_test<_Ty, __detail::__is_nttp>; };

static_assert(__structural<int>);

namespace __std
{

Expand Down
57 changes: 19 additions & 38 deletions include/stdexec/__detail/__diagnostics.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,13 +144,14 @@ namespace STDEXEC
_WITH_PRETTY_SENDER_<_Sender>,
_WITH_ENVIRONMENT_(_Env)...>;

#if __cpp_lib_constexpr_exceptions \
>= 2025'02L // constexpr exception types, https://wg21.link/p3378
#if __cpp_lib_constexpr_exceptions >= 2025'02L

// constexpr stdlib exception types, https://wg21.link/p3378
using __exception = ::std::exception;

#elif __cpp_constexpr >= 2024'11L // constexpr virtual functions
#elif __cpp_constexpr >= 2019'07L

// constexpr virtual functions
struct __exception
{
constexpr __exception() noexcept = default;
Expand All @@ -163,8 +164,9 @@ namespace STDEXEC
}
};

#else // no constexpr virtual functions:
#else

// no constexpr virtual functions
struct __exception
{
constexpr __exception() noexcept = default;
Expand All @@ -176,23 +178,13 @@ namespace STDEXEC
}
};

#endif // __cpp_lib_constexpr_exceptions >= 2025'02L
#endif

template <class _Derived>
struct __compile_time_error : __exception
{
constexpr __compile_time_error() = default; // NOLINT (bugprone-crtp-constructor-accessibility)

[[nodiscard]]
constexpr auto what() const noexcept -> char const *
{
return static_cast<_Derived const *>(this)->what();
}
};
{};

template <class _Data, class... _What>
struct __sender_type_check_failure //
: __compile_time_error<__sender_type_check_failure<_Data, _What...>>
struct __sender_type_check_failure : __compile_time_error
{
static_assert(std::is_nothrow_move_constructible_v<_Data>,
"The data member of sender_type_check_failure must be nothrow move "
Expand All @@ -204,34 +196,24 @@ namespace STDEXEC
: __data_(static_cast<_Data &&>(data))
{}

private:
friend struct __compile_time_error<__sender_type_check_failure>;

[[nodiscard]]
constexpr auto what() const noexcept -> char const *
constexpr auto what() const noexcept -> char const * // NOLINT(modernize-use-override)
{
return "This sender is not well-formed. It does not meet the requirements of a sender type.";
}

// public so that __sender_type_check_failure is a structural type
_Data __data_{};
};

struct dependent_sender_error : __compile_time_error<dependent_sender_error>
struct dependent_sender_error : __compile_time_error
{
constexpr explicit dependent_sender_error(char const *what) noexcept
: what_(what)
{}

private:
friend struct __compile_time_error<dependent_sender_error>;

[[nodiscard]]
constexpr auto what() const noexcept -> char const *
constexpr auto what() const noexcept -> char const * // NOLINT(modernize-use-override)
{
return what_;
return "This sender needs to know its execution environment before it can "
"know how it will complete.";
}

char const *what_;
};

// A specialization of _ERROR_ to be used to report dependent sender. It inherits
Expand All @@ -257,11 +239,8 @@ namespace STDEXEC
using __errors = _ERROR_;
using __all = _ERROR_;

constexpr _ERROR_() noexcept
: dependent_sender_error{"This sender needs to know its execution environment before it can "
"know how it will "
"complete."}
{}
constexpr _ERROR_() = default;
constexpr ~_ERROR_() = default;

STDEXEC_ATTRIBUTE(host, device) constexpr auto operator+() const -> _ERROR_;

Expand All @@ -274,6 +253,8 @@ namespace STDEXEC
constexpr auto operator,(const _ERROR_<Other...> &) const -> _ERROR_<Other...>;
};

static_assert(__structural<_ERROR_<dependent_sender_error>>);

// By making __dependent_sender_error_t an alias for _ERROR_<...>, we ensure that
// it will get propagated correctly through various metafunctions.
template <class _Sender>
Expand Down
38 changes: 38 additions & 0 deletions include/stdexec/__detail/__get_completion_signatures.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -302,11 +302,49 @@ namespace STDEXEC

///////////////////////////////////////////////////////////////////////////////////////////////////
// An minimally constrained alias for the result of get_completion_signatures:
#if STDEXEC_GCC()
template <class _Sender, class... _Env>
requires enable_sender<__decay_t<_Sender>>
&& __constant<STDEXEC::get_completion_signatures<_Sender, _Env...>()>
using __completion_signatures_of_t =
decltype(STDEXEC::get_completion_signatures<_Sender, _Env...>());

#elif STDEXEC_EDG()

namespace __detail
{
template <class _Sender, class... _Env>
using __cmplsigs_of_t =
std::integral_constant<decltype(STDEXEC::get_completion_signatures<_Sender, _Env...>()),
STDEXEC::get_completion_signatures<_Sender, _Env...>()>::value_type;
} // namespace __detail

template <class _Sender, class... _Env>
requires enable_sender<__decay_t<_Sender>>
&& __minvocable_q<__detail::__cmplsigs_of_t, _Sender, _Env...>
using __completion_signatures_of_t =
decltype(STDEXEC::get_completion_signatures<_Sender, _Env...>());

#elif STDEXEC_MSVC()

// MSVC cannot handle a __completion_signatures_of_t alias template that requires
// get_completion_signatures to be a constant expression, even if we wrap the call to
// get_completion_signatures in an integral_constant like we do for EDG. So we skip
// checking the requirement.

template <class _Sender, class... _Env>
requires enable_sender<__decay_t<_Sender>>
using __completion_signatures_of_t =
decltype(STDEXEC::get_completion_signatures<_Sender, _Env...>());

#else

template <class _Sender, class... _Env>
requires enable_sender<__decay_t<_Sender>>
using __completion_signatures_of_t =
__mtypeof<STDEXEC::get_completion_signatures<_Sender, _Env...>()>;
#endif

///////////////////////////////////////////////////////////////////////////////////////////////////
// __get_child_completion_signatures
template <class _Parent, class _Child, class... _Env>
Expand Down
Loading