Skip to content

Commit b40a6a8

Browse files
committed
make diagnostics describe how to fix the problem
1 parent ac2d378 commit b40a6a8

5 files changed

Lines changed: 185 additions & 43 deletions

File tree

include/stdexec/__detail/__diagnostics.hpp

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,160 @@ namespace stdexec {
103103
__mexception<_NOT_CALLABLE_<_Context>, _WITH_FUNCTION_<_Fun>, _WITH_ARGUMENTS_<_Args...>>;
104104
};
105105
} // namespace stdexec
106+
107+
////////////////////////////////////////////////////////////////////////////////
108+
#define STDEXEC_ERROR_ENABLE_SENDER_IS_FALSE \
109+
R"(((
110+
111+
The given type is not a sender because stdexec::enable_sender<Sender> is false. Either:
112+
113+
1. Give the type a nested `::sender_concept` type that is an alias for `stdexec::sender_t`,
114+
as in:
115+
116+
class MySender
117+
{
118+
public:
119+
using sender_concept = stdexec::sender_t;
120+
...
121+
};
122+
123+
or,
124+
125+
2. Specialize the `stdexec::enable_sender` boolean trait for this type to true, as follows:
126+
127+
class MySender
128+
{
129+
...
130+
};
131+
132+
template <>
133+
inline constexpr bool stdexec::enable_sender<MySender> = true;
134+
)))"
135+
136+
////////////////////////////////////////////////////////////////////////////////
137+
#define STDEXEC_ERROR_CANNOT_COMPUTE_COMPLETION_SIGNATURES \
138+
R"(((
139+
140+
The sender type was not able to report its completion signatures when asked.
141+
This is either because it lacks the necessary member function, or because the
142+
member function was ill-formed.
143+
144+
A sender can declare its completion signatures in one of two ways:
145+
146+
1. By defining a nested type alias named `completion_signatures` that is a
147+
specialization of `stdexec::completion_signatures<...>`, as follows:
148+
149+
class MySender
150+
{
151+
public:
152+
using sender_concept = stdexec::sender_t;
153+
using completion_signatures = stdexec::completion_signatures<
154+
// This sender can complete successfully with an int and a float...
155+
stdexec::set_value_t(int, float),
156+
// ... or in error with an exception_ptr
157+
stdexec::set_error_t(std::exception_ptr)>;
158+
...
159+
};
160+
161+
or,
162+
163+
2. By defining a member function named `get_completion_signatures` that returns
164+
a specialization of `stdexec::completion_signatures<...>`, as follows:
165+
166+
class MySender
167+
{
168+
public:
169+
using sender_concept = stdexec::sender_t;
170+
171+
template <class... _Env>
172+
auto get_completion_signatures(_Env&&...) -> stdexec::completion_signatures<
173+
// This sender can complete successfully with an int and a float...
174+
stdexec::set_value_t(int, float),
175+
// ... or in error with a std::exception_ptr.
176+
stdexec::set_error_t(std::exception_ptr)>
177+
{
178+
return {};
179+
}
180+
...
181+
};
182+
)))"
183+
184+
////////////////////////////////////////////////////////////////////////////////
185+
#define STDEXEC_ERROR_GET_COMPLETION_SIGNATURES_RETURNED_AN_ERROR \
186+
R"(((
187+
188+
Trying to compute the sender's completion signatures resulted in an error. See
189+
the rest of the compiler diagnostic for clues. Look for the string "_ERROR_".
190+
)))"
191+
192+
#define STDEXEC_ERROR_GET_COMPLETION_SIGNATURES_HAS_INVALID_RETURN_TYPE \
193+
R"(((
194+
195+
The member function `get_completion_signatures` of the sender returned an
196+
invalid type.
197+
198+
A sender's `get_completion_signatures` function must return a specialization of
199+
`stdexec::completion_signatures<...>`, as follows:
200+
201+
class MySender
202+
{
203+
public:
204+
using sender_concept = stdexec::sender_t;
205+
206+
template <class... _Env>
207+
auto get_completion_signatures(_Env&&...) -> stdexec::completion_signatures<
208+
// This sender can complete successfully with an int and a float...
209+
stdexec::set_value_t(int, float),
210+
// ... or in error with a std::exception_ptr.
211+
stdexec::set_error_t(std::exception_ptr)>
212+
{
213+
return {};
214+
}
215+
...
216+
};
217+
)))"
218+
219+
////////////////////////////////////////////////////////////////////////////////
220+
#define STDEXEC_ERROR_CANNOT_CONNECT_SENDER_TO_RECEIVER \
221+
R"(((
222+
A sender must provide a `connect` member function that takes a receiver as an
223+
argument and returns an object whose type satisfies `stdexec::operation_state`,
224+
as shown below:
225+
226+
class MySender
227+
{
228+
public:
229+
using sender_concept = stdexec::sender_t;
230+
using completion_signatures = stdexec::completion_signatures<stdexec::set_value_t()>;
231+
232+
template <class Receiver>
233+
struct MyOpState
234+
{
235+
using operation_state_concept = stdexec::operation_state_t;
236+
237+
void start() noexcept
238+
{
239+
// Start the operation, which will eventually complete and send its
240+
// result to rcvr_;
241+
}
242+
243+
Receiver rcvr_;
244+
};
245+
246+
template <stdexec::receiver Receiver>
247+
auto connect(Receiver rcvr) -> MyOpState<Receiver>
248+
{
249+
return MyOpState<Receiver>{std::move(rcvr)};
250+
}
251+
252+
...
253+
};
254+
)))"
255+
256+
////////////////////////////////////////////////////////////////////////////////
257+
#define STDEXEC_ERROR_SYNC_WAIT_CANNOT_CONNECT_SENDER_TO_RECEIVER \
258+
R"(((
259+
260+
The sender passed to `stdexec::sync_wait()` does not have a `connect`
261+
member function that accepts sync_wait's receiver.
262+
)))" STDEXEC_ERROR_CANNOT_CONNECT_SENDER_TO_RECEIVER

include/stdexec/__detail/__receivers.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,12 +178,12 @@ namespace stdexec {
178178
auto __try_completions(completion_signatures<_Sigs...> *) -> decltype((
179179
__msuccess(),
180180
...,
181-
__detail::__try_completion<_Receiver>(static_cast<_Sigs *>(nullptr))));
181+
__detail::__try_completion<__decay_t<_Receiver>>(static_cast<_Sigs *>(nullptr))));
182182
} // namespace __detail
183183

184184
template <class _Receiver, class _Completions>
185185
concept receiver_of = receiver<_Receiver> && requires(_Completions *__completions) {
186-
{ __detail::__try_completions<__decay_t<_Receiver>>(__completions) } -> __ok;
186+
{ __detail::__try_completions<_Receiver>(__completions) } -> __ok;
187187
};
188188

189189
template <class _Receiver, class _Sender>

include/stdexec/__detail/__senders.hpp

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,11 @@ namespace stdexec {
206206
return true;
207207
}
208208

209+
template <class _OpState>
210+
static constexpr void __check_operation_state() noexcept {
211+
static_assert(operation_state<_OpState>, STDEXEC_ERROR_CANNOT_CONNECT_SENDER_TO_RECEIVER);
212+
}
213+
209214
template <class _Sender, class _Receiver>
210215
static constexpr auto __select_impl() noexcept {
211216
using _Domain = __late_domain_of_t<_Sender, env_of_t<_Receiver>>;
@@ -222,31 +227,22 @@ namespace stdexec {
222227

223228
if constexpr (__with_static_member<_TfxSender, _Receiver>) {
224229
using _Result = __static_member_result_t<_TfxSender, _Receiver>;
225-
static_assert(
226-
operation_state<_Result>,
227-
"Sender::connect(sender, receiver) must return a type that "
228-
"satisfies the operation_state concept");
230+
__check_operation_state<_Result>();
229231
constexpr bool _Nothrow = _NothrowTfxSender
230232
&& noexcept(
231233
__declval<_TfxSender>()
232234
.connect(__declval<_TfxSender>(), __declval<_Receiver>()));
233235
return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
234236
} else if constexpr (__with_member<_TfxSender, _Receiver>) {
235237
using _Result = __member_result_t<_TfxSender, _Receiver>;
236-
static_assert(
237-
operation_state<_Result>,
238-
"sender.connect(receiver) must return a type that "
239-
"satisfies the operation_state concept");
238+
__check_operation_state<_Result>();
240239
constexpr bool _Nothrow = _NothrowTfxSender
241240
&& noexcept(__declval<_TfxSender>()
242241
.connect(__declval<_Receiver>()));
243242
return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
244243
} else if constexpr (__with_tag_invoke<_TfxSender, _Receiver>) {
245244
using _Result = tag_invoke_result_t<connect_t, _TfxSender, _Receiver>;
246-
static_assert(
247-
operation_state<_Result>,
248-
"stdexec::connect(sender, receiver) must return a type that "
249-
"satisfies the operation_state concept");
245+
__check_operation_state<_Result>();
250246
constexpr bool _Nothrow = _NothrowTfxSender
251247
&& nothrow_tag_invocable<connect_t, _TfxSender, _Receiver>;
252248
return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);

include/stdexec/__detail/__senders_core.hpp

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "__awaitable.hpp"
2222
#include "__completion_signatures.hpp"
2323
#include "__concepts.hpp"
24+
#include "__diagnostics.hpp"
2425
#include "__domain.hpp"
2526
#include "__env.hpp"
2627
#include "__receivers.hpp"
@@ -51,7 +52,7 @@ namespace stdexec {
5152

5253
template <class _Sender, class... _Env>
5354
concept sender_in =
54-
(sizeof...(_Env) <= 1) && sender<_Sender> && requires(_Sender&& __sndr, _Env&&... __env) {
55+
(sizeof...(_Env) <= 1) && sender<_Sender> && requires(_Sender &&__sndr, _Env &&...__env) {
5556
{
5657
get_completion_signatures(static_cast<_Sender &&>(__sndr), static_cast<_Env &&>(__env)...)
5758
} -> __valid_completion_signatures;
@@ -62,26 +63,24 @@ namespace stdexec {
6263
template <class _Sender, class _Receiver>
6364
concept sender_to = receiver<_Receiver> && sender_in<_Sender, env_of_t<_Receiver>>
6465
&& __receiver_from<_Receiver, _Sender>
65-
&& requires(_Sender&& __sndr, _Receiver&& __rcvr) {
66+
&& requires(_Sender &&__sndr, _Receiver &&__rcvr) {
6667
connect(static_cast<_Sender &&>(__sndr), static_cast<_Receiver &&>(__rcvr));
6768
};
6869

6970
template <class _Sender, class _Receiver>
7071
using connect_result_t = __call_result_t<connect_t, _Sender, _Receiver>;
7172

72-
// Used to report a meaningful error message when the sender_in<Sndr, Env> concept check fails.
73+
// Used to report a meaningful error message when the sender_in<Sndr, Env>
74+
// concept check fails.
7375
template <class _Sender, class... _Env>
7476
auto __diagnose_sender_concept_failure() {
7577
if constexpr (!enable_sender<__decay_t<_Sender>>) {
76-
static_assert(
77-
enable_sender<_Sender>,
78-
"The given type is not a sender because stdexec::enable_sender<Sender> is false. Either "
79-
"give the type a nested ::sender_concept typedef that is an alias for stdexec::sender_t, "
80-
"or else specialize the stdexec::enable_sender boolean trait for this type to true.");
78+
static_assert(enable_sender<_Sender>, STDEXEC_ERROR_ENABLE_SENDER_IS_FALSE);
8179
} else if constexpr (!__detail::__consistent_completion_domains<_Sender>) {
8280
static_assert(
8381
__detail::__consistent_completion_domains<_Sender>,
84-
"The completion schedulers of the sender do not have consistent domains. This is likely a "
82+
"The completion schedulers of the sender do not have "
83+
"consistent domains. This is likely a "
8584
"bug in the sender implementation.");
8685
} else if constexpr (!move_constructible<__decay_t<_Sender>>) {
8786
static_assert(
@@ -93,28 +92,18 @@ namespace stdexec {
9392
} else {
9493
using _Completions = __completion_signatures_of_t<_Sender, _Env...>;
9594
if constexpr (__same_as<_Completions, __unrecognized_sender_error<_Sender, _Env...>>) {
96-
static_assert(
97-
__mnever<_Completions>,
98-
"The sender type was not able to report its completion signatures when asked. This is "
99-
"either because it lacks the necessary member functions, or because the member functions "
100-
"were ill-formed.\n\nA sender can declare its completion signatures in one of two ways:\n"
101-
" 1. By defining a nested type alias named `completion_signatures` that is a\n"
102-
" specialization of stdexec::completion_signatures<...>.\n"
103-
" 2. By defining a member function named `get_completion_signatures` that returns a\n"
104-
" specialization of stdexec::completion_signatures<...>.");
95+
static_assert(__mnever<_Completions>, STDEXEC_ERROR_CANNOT_COMPUTE_COMPLETION_SIGNATURES);
10596
} else if constexpr (__merror<_Completions>) {
10697
static_assert(
107-
!__merror<_Completions>,
108-
"Trying to compute the sender's completion signatures resulted in an error. See the rest "
109-
"of the compiler diagnostic for clues. Look for the string \"_ERROR_\".");
98+
!__merror<_Completions>, STDEXEC_ERROR_GET_COMPLETION_SIGNATURES_RETURNED_AN_ERROR);
11099
} else {
111100
static_assert(
112101
__valid_completion_signatures<_Completions>,
113-
"The stdexec::sender_in<Sender, Environment> concept check has failed. This is likely a "
114-
"bug in the sender implementation.");
102+
STDEXEC_ERROR_GET_COMPLETION_SIGNATURES_HAS_INVALID_RETURN_TYPE);
115103
}
116104
#if STDEXEC_MSVC() || STDEXEC_NVHPC()
117-
// MSVC and NVHPC need more encouragement to print the type of the error.
105+
// MSVC and NVHPC need more encouragement to print the type of the
106+
// error.
118107
_Completions __what = 0;
119108
#endif
120109
}

include/stdexec/__detail/__sync_wait.hpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,10 @@ namespace stdexec {
6464
return true;
6565
}
6666

67-
static constexpr auto query(__debug::__is_debug_env_t) noexcept -> bool {
68-
return true;
69-
}
67+
// static constexpr auto query(__debug::__is_debug_env_t) noexcept -> bool
68+
// {
69+
// return true;
70+
// }
7071
};
7172

7273
// What should sync_wait(just_stopped()) return?
@@ -230,8 +231,7 @@ namespace stdexec {
230231
} else if constexpr (__no_custom_sync_wait) {
231232
static_assert(
232233
sender_to<_Sender, __sync_wait_receiver>,
233-
"The sender passed to stdexec::sync_wait() does not have a .connect(<receiver>) "
234-
"member function that accepts sync_wait's receiver.");
234+
STDEXEC_ERROR_SYNC_WAIT_CANNOT_CONNECT_SENDER_TO_RECEIVER);
235235
} else if constexpr (!__has_implementation_for<sync_wait_t, __domain_t, _Sender>) {
236236
static_assert(
237237
__has_implementation_for<sync_wait_t, __domain_t, _Sender>,

0 commit comments

Comments
 (0)