Skip to content

Commit 85aaed4

Browse files
committed
__completion_signatures_of_t requires that get_completion_signatures<S,E...>() is a constant expression
1 parent 61707d2 commit 85aaed4

File tree

4 files changed

+84
-53
lines changed

4 files changed

+84
-53
lines changed

include/exec/sequence_senders.hpp

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ namespace experimental::execution
124124
{
125125
template <class _Receiver, class _Item>
126126
concept __has_set_next_member = requires(_Receiver& __rcvr, _Item&& __item) {
127-
__rcvr.set_next(static_cast<_Item&&>(__item));
127+
__rcvr.set_next(static_cast<_Item &&>(__item));
128128
};
129129

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

433433
template <class _Sequence, class... _Env>
@@ -518,8 +518,8 @@ namespace experimental::execution
518518
concept sequence_sender_in = sequence_sender<_Sequence, _Env...>
519519
&& requires(_Sequence&& __sequence, _Env&&... __env) {
520520
{
521-
get_item_types(static_cast<_Sequence&&>(__sequence),
522-
static_cast<_Env&&>(__env)...)
521+
get_item_types(static_cast<_Sequence &&>(__sequence),
522+
static_cast<_Env &&>(__env)...)
523523
} -> __well_formed_item_types<_Sequence>;
524524
};
525525

@@ -557,8 +557,7 @@ namespace experimental::execution
557557
#endif
558558

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

573-
private:
574-
friend struct STDEXEC::__compile_time_error<__sequence_type_check_failure>;
575-
576572
[[nodiscard]]
577-
constexpr auto what() const noexcept -> char const *
573+
constexpr auto what() const noexcept -> char const * // NOLINT(modernize-use-override)
578574
{
579575
return "This sequence sender is not well-formed. It does not meet the requirements of a "
580576
"sequence sender type.";
581577
}
582578

579+
// public so that __sequence_type_check_failure is a structural type
583580
_Data __data_{};
584581
};
585582

@@ -845,10 +842,9 @@ namespace experimental::execution
845842
return STDEXEC::connect(static_cast<next_sender_of_t<_Receiver, __tfx_seq_t>&&>(__next),
846843
__stopped_means_break<_Receiver>{
847844
static_cast<_Receiver&&>(__rcvr)});
848-
// NOLINTNEXTLINE(bugprone-branch-clone)
849845
}
850846
else if constexpr (__subscribable_with_static_member<__tfx_seq_t, _Receiver>)
851-
{
847+
{ // NOLINT(bugprone-branch-clone)
852848
return __tfx_seq.subscribe(static_cast<__tfx_seq_t&&>(__tfx_seq),
853849
static_cast<_Receiver&&>(__rcvr));
854850
}
@@ -929,8 +925,8 @@ namespace experimental::execution
929925
template <class _Sequence, class _Receiver>
930926
concept sequence_sender_to = sequence_receiver_from<_Receiver, _Sequence>
931927
&& requires(_Sequence&& __sequence, _Receiver&& __rcvr) {
932-
subscribe(static_cast<_Sequence&&>(__sequence),
933-
static_cast<_Receiver&&>(__rcvr));
928+
subscribe(static_cast<_Sequence &&>(__sequence),
929+
static_cast<_Receiver &&>(__rcvr));
934930
};
935931

936932
template <class _Receiver>

include/stdexec/__detail/__concepts.hpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,22 @@ namespace STDEXEC
9797
template <class _Ay, template <class...> class _Ty>
9898
concept __is_not_instance_of = !__is_instance_of<_Ay, _Ty>;
9999

100+
template <auto>
101+
concept __constant = true;
102+
103+
namespace __detail
104+
{
105+
template <auto>
106+
using __is_nttp = void;
107+
template <class _Ty, template <_Ty> class>
108+
using __nttp_test = void;
109+
} // namespace __detail
110+
111+
template <class _Ty>
112+
concept __structural = requires { typename __detail::__nttp_test<_Ty, __detail::__is_nttp>; };
113+
114+
static_assert(__structural<int>);
115+
100116
namespace __std
101117
{
102118

include/stdexec/__detail/__diagnostics.hpp

Lines changed: 19 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -144,13 +144,14 @@ namespace STDEXEC
144144
_WITH_PRETTY_SENDER_<_Sender>,
145145
_WITH_ENVIRONMENT_(_Env)...>;
146146

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

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

152-
#elif __cpp_constexpr >= 2024'11L // constexpr virtual functions
152+
#elif __cpp_constexpr >= 2019'07L
153153

154+
// constexpr virtual functions
154155
struct __exception
155156
{
156157
constexpr __exception() noexcept = default;
@@ -163,8 +164,9 @@ namespace STDEXEC
163164
}
164165
};
165166

166-
#else // no constexpr virtual functions:
167+
#else
167168

169+
// no constexpr virtual functions
168170
struct __exception
169171
{
170172
constexpr __exception() noexcept = default;
@@ -176,23 +178,13 @@ namespace STDEXEC
176178
}
177179
};
178180

179-
#endif // __cpp_lib_constexpr_exceptions >= 2025'02L
181+
#endif
180182

181-
template <class _Derived>
182183
struct __compile_time_error : __exception
183-
{
184-
constexpr __compile_time_error() = default; // NOLINT (bugprone-crtp-constructor-accessibility)
185-
186-
[[nodiscard]]
187-
constexpr auto what() const noexcept -> char const *
188-
{
189-
return static_cast<_Derived const *>(this)->what();
190-
}
191-
};
184+
{};
192185

193186
template <class _Data, class... _What>
194-
struct __sender_type_check_failure //
195-
: __compile_time_error<__sender_type_check_failure<_Data, _What...>>
187+
struct __sender_type_check_failure : __compile_time_error
196188
{
197189
static_assert(std::is_nothrow_move_constructible_v<_Data>,
198190
"The data member of sender_type_check_failure must be nothrow move "
@@ -204,34 +196,24 @@ namespace STDEXEC
204196
: __data_(static_cast<_Data &&>(data))
205197
{}
206198

207-
private:
208-
friend struct __compile_time_error<__sender_type_check_failure>;
209-
210199
[[nodiscard]]
211-
constexpr auto what() const noexcept -> char const *
200+
constexpr auto what() const noexcept -> char const * // NOLINT(modernize-use-override)
212201
{
213202
return "This sender is not well-formed. It does not meet the requirements of a sender type.";
214203
}
215204

205+
// public so that __sender_type_check_failure is a structural type
216206
_Data __data_{};
217207
};
218208

219-
struct dependent_sender_error : __compile_time_error<dependent_sender_error>
209+
struct dependent_sender_error : __compile_time_error
220210
{
221-
constexpr explicit dependent_sender_error(char const *what) noexcept
222-
: what_(what)
223-
{}
224-
225-
private:
226-
friend struct __compile_time_error<dependent_sender_error>;
227-
228211
[[nodiscard]]
229-
constexpr auto what() const noexcept -> char const *
212+
constexpr auto what() const noexcept -> char const * // NOLINT(modernize-use-override)
230213
{
231-
return what_;
214+
return "This sender needs to know its execution environment before it can "
215+
"know how it will complete.";
232216
}
233-
234-
char const *what_;
235217
};
236218

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

260-
constexpr _ERROR_() noexcept
261-
: dependent_sender_error{"This sender needs to know its execution environment before it can "
262-
"know how it will "
263-
"complete."}
264-
{}
242+
constexpr _ERROR_() = default;
243+
constexpr ~_ERROR_() = default;
265244

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

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

256+
static_assert(__structural<_ERROR_<dependent_sender_error>>);
257+
277258
// By making __dependent_sender_error_t an alias for _ERROR_<...>, we ensure that
278259
// it will get propagated correctly through various metafunctions.
279260
template <class _Sender>

include/stdexec/__detail/__get_completion_signatures.hpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,11 +302,49 @@ namespace STDEXEC
302302

303303
///////////////////////////////////////////////////////////////////////////////////////////////////
304304
// An minimally constrained alias for the result of get_completion_signatures:
305+
#if STDEXEC_GCC()
305306
template <class _Sender, class... _Env>
306307
requires enable_sender<__decay_t<_Sender>>
308+
&& __constant<STDEXEC::get_completion_signatures<_Sender, _Env...>()>
307309
using __completion_signatures_of_t =
308310
decltype(STDEXEC::get_completion_signatures<_Sender, _Env...>());
309311

312+
#elif STDEXEC_EDG()
313+
314+
namespace __detail
315+
{
316+
template <class _Sender, class... _Env>
317+
using __cmplsigs_of_t =
318+
std::integral_constant<decltype(STDEXEC::get_completion_signatures<_Sender, _Env...>()),
319+
STDEXEC::get_completion_signatures<_Sender, _Env...>()>::value_type;
320+
} // namespace __detail
321+
322+
template <class _Sender, class... _Env>
323+
requires enable_sender<__decay_t<_Sender>>
324+
&& __minvocable_q<__detail::__cmplsigs_of_t, _Sender, _Env...>
325+
using __completion_signatures_of_t =
326+
decltype(STDEXEC::get_completion_signatures<_Sender, _Env...>());
327+
328+
#elif STDEXEC_MSVC()
329+
330+
// MSVC cannot handle a __completion_signatures_of_t alias template that requires
331+
// get_completion_signatures to be a constant expression, even if we wrap the call to
332+
// get_completion_signatures in an integral_constant like we do for EDG. So we skip
333+
// checking the requirement.
334+
335+
template <class _Sender, class... _Env>
336+
requires enable_sender<__decay_t<_Sender>>
337+
using __completion_signatures_of_t =
338+
decltype(STDEXEC::get_completion_signatures<_Sender, _Env...>());
339+
340+
#else
341+
342+
template <class _Sender, class... _Env>
343+
requires enable_sender<__decay_t<_Sender>>
344+
using __completion_signatures_of_t =
345+
__mtypeof<STDEXEC::get_completion_signatures<_Sender, _Env...>()>;
346+
#endif
347+
310348
///////////////////////////////////////////////////////////////////////////////////////////////////
311349
// __get_child_completion_signatures
312350
template <class _Parent, class _Child, class... _Env>

0 commit comments

Comments
 (0)