Skip to content

Commit f5b9334

Browse files
committed
__completion_signatures_of_t requires that get_completion_signatures<S,E...>() is a constant expression
1 parent ba6ef88 commit f5b9334

File tree

4 files changed

+84
-58
lines changed

4 files changed

+84
-58
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
@@ -428,8 +428,8 @@ namespace experimental::execution
428428
template <class _Sequence, class... _Env>
429429
concept has_sequence_item_types = STDEXEC::sender_in<_Sequence, _Env...> //
430430
&& requires(_Sequence&& __sequence, _Env&&... __env) {
431-
get_item_types(static_cast<_Sequence&&>(__sequence),
432-
static_cast<_Env&&>(__env)...);
431+
get_item_types(static_cast<_Sequence &&>(__sequence),
432+
static_cast<_Env &&>(__env)...);
433433
};
434434

435435
template <class _Sequence, class... _Env>
@@ -520,8 +520,8 @@ namespace experimental::execution
520520
concept sequence_sender_in = sequence_sender<_Sequence, _Env...>
521521
&& requires(_Sequence&& __sequence, _Env&&... __env) {
522522
{
523-
get_item_types(static_cast<_Sequence&&>(__sequence),
524-
static_cast<_Env&&>(__env)...)
523+
get_item_types(static_cast<_Sequence &&>(__sequence),
524+
static_cast<_Env &&>(__env)...)
525525
} -> __well_formed_item_types<_Sequence>;
526526
};
527527

@@ -559,8 +559,7 @@ namespace experimental::execution
559559
#endif
560560

561561
template <class _Data, class... _What>
562-
struct __sequence_type_check_failure //
563-
: STDEXEC::__compile_time_error<__sequence_type_check_failure<_Data, _What...>>
562+
struct __sequence_type_check_failure : STDEXEC::__compile_time_error
564563
{
565564
static_assert(std::is_nothrow_move_constructible_v<_Data>,
566565
"The data member of sender_type_check_failure must be nothrow move "
@@ -572,16 +571,14 @@ namespace experimental::execution
572571
: __data_(static_cast<_Data&&>(data))
573572
{}
574573

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

581+
// public so that __sequence_type_check_failure is a structural type
585582
_Data __data_{};
586583
};
587584

@@ -849,10 +846,9 @@ namespace experimental::execution
849846
return STDEXEC::connect(static_cast<next_sender_of_t<_Receiver, __tfx_seq_t>&&>(__next),
850847
__stopped_means_break<_Receiver>{
851848
static_cast<_Receiver&&>(__rcvr)});
852-
// NOLINTNEXTLINE(bugprone-branch-clone)
853849
}
854850
else if constexpr (__subscribable_with_static_member<__tfx_seq_t, _Receiver>)
855-
{
851+
{ // NOLINT(bugprone-branch-clone)
856852
return __tfx_seq.subscribe(static_cast<__tfx_seq_t&&>(__tfx_seq),
857853
static_cast<_Receiver&&>(__rcvr));
858854
}
@@ -933,8 +929,8 @@ namespace experimental::execution
933929
template <class _Sequence, class _Receiver>
934930
concept sequence_sender_to = sequence_receiver_from<_Receiver, _Sequence>
935931
&& requires(_Sequence&& __sequence, _Receiver&& __rcvr) {
936-
subscribe(static_cast<_Sequence&&>(__sequence),
937-
static_cast<_Receiver&&>(__rcvr));
932+
subscribe(static_cast<_Sequence &&>(__sequence),
933+
static_cast<_Receiver &&>(__rcvr));
938934
};
939935

940936
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 & 43 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,39 +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 dependent_sender_error() noexcept
222-
: what_("This sender needs to know its execution environment before it can know how it will "
223-
"complete.")
224-
{}
225-
226-
constexpr explicit dependent_sender_error(char const *what) noexcept
227-
: what_(what)
228-
{}
229-
230-
private:
231-
friend struct __compile_time_error<dependent_sender_error>;
232-
233211
[[nodiscard]]
234-
constexpr auto what() const noexcept -> char const *
212+
constexpr auto what() const noexcept -> char const * // NOLINT(modernize-use-override)
235213
{
236-
return what_;
214+
return "This sender needs to know its execution environment before it can "
215+
"know how it will complete.";
237216
}
238-
239-
char const *what_;
240217
};
241218

242219
// A specialization of _ERROR_ to be used to report dependent sender. It inherits
@@ -262,11 +239,8 @@ namespace STDEXEC
262239
using __errors = _ERROR_;
263240
using __all = _ERROR_;
264241

265-
constexpr _ERROR_() noexcept
266-
: dependent_sender_error{"This sender needs to know its execution environment before it can "
267-
"know how it will "
268-
"complete."}
269-
{}
242+
constexpr _ERROR_() = default;
243+
constexpr ~_ERROR_() = default;
270244

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

@@ -279,6 +253,8 @@ namespace STDEXEC
279253
constexpr auto operator,(const _ERROR_<Other...> &) const -> _ERROR_<Other...>;
280254
};
281255

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

include/stdexec/__detail/__get_completion_signatures.hpp

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

299299
///////////////////////////////////////////////////////////////////////////////////////////////////
300300
// An minimally constrained alias for the result of get_completion_signatures:
301+
#if STDEXEC_GCC()
301302
template <class _Sender, class... _Env>
302303
requires enable_sender<__decay_t<_Sender>>
304+
&& __constant<STDEXEC::get_completion_signatures<_Sender, _Env...>()>
303305
using __completion_signatures_of_t =
304306
decltype(STDEXEC::get_completion_signatures<_Sender, _Env...>());
305307

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

0 commit comments

Comments
 (0)