1 /*
2 * Copyright (c) 2021-2024 NVIDIA Corporation
3 *
4 * Licensed under the Apache License Version 2.0 with LLVM Exceptions
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * https://llvm.org/LICENSE.txt
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #pragma once
17
18 #include "__concepts.hpp"
19 #include "__cpo.hpp"
20 #include "__diagnostics.hpp"
21 #include "__env.hpp"
22 #include "__execution_fwd.hpp"
23 #include "__meta.hpp"
24 #include "__tag_invoke.hpp"
25
26 namespace stdexec
27 {
28 /////////////////////////////////////////////////////////////////////////////
29 // Some utilities for debugging senders
30 namespace __debug
31 {
32 struct __is_debug_env_t
33 {
querystdexec::__debug::__is_debug_env_t34 static constexpr auto query(forwarding_query_t) noexcept -> bool
35 {
36 return true;
37 }
38 template <class _Env>
39 requires tag_invocable<__is_debug_env_t, const _Env&>
40 auto operator()(const _Env&) const noexcept
41 -> tag_invoke_result_t<__is_debug_env_t, const _Env&>;
42 };
43
44 template <class _Env>
45 using __debug_env_t = env<prop<__is_debug_env_t, bool>, _Env>;
46
47 template <class _Env>
48 concept __is_debug_env = tag_invocable<__is_debug_env_t, _Env>;
49
50 struct __completion_signatures
51 {};
52
53 #if STDEXEC_MSVC()
54 // MSVCBUG
55 // https://developercommunity.visualstudio.com/t/Explicit-variable-template-specialisatio/10360032
56 // MSVCBUG
57 // https://developercommunity.visualstudio.com/t/Non-function-type-interpreted-as-functio/10447831
58
59 template <class _Sig>
60 struct __normalize_sig;
61
62 template <class _Tag, class... _Args>
63 struct __normalize_sig<_Tag(_Args...)>
64 {
65 using __type = _Tag (*)(_Args&&...);
66 };
67
68 template <class _Sig>
69 using __normalize_sig_t = typename __normalize_sig<_Sig>::__type;
70 #else
71 template <class _Sig>
72 extern int __normalize_sig;
73
74 template <class _Tag, class... _Args>
75 extern _Tag (*__normalize_sig<_Tag(_Args...)>)(_Args&&...);
76
77 template <class _Sig>
78 using __normalize_sig_t = decltype(__normalize_sig<_Sig>);
79 #endif
80
81 template <class... _Sigs>
82 struct __valid_completions
83 {
84 template <class... _Args>
85 requires __one_of<set_value_t (*)(_Args&&...), _Sigs...>
86 STDEXEC_ATTRIBUTE((host, device))
set_valuestdexec::__debug::__valid_completions87 void set_value(_Args&&...) noexcept
88 {
89 STDEXEC_TERMINATE();
90 }
91
92 template <class _Error>
93 requires __one_of<set_error_t (*)(_Error&&), _Sigs...>
94 STDEXEC_ATTRIBUTE((host, device))
set_errorstdexec::__debug::__valid_completions95 void set_error(_Error&&) noexcept
96 {
97 STDEXEC_TERMINATE();
98 }
99
100 STDEXEC_ATTRIBUTE((host, device))
set_stoppedstdexec::__debug::__valid_completions101 void set_stopped() noexcept
102 requires __one_of<set_stopped_t (*)(), _Sigs...>
103 {
104 STDEXEC_TERMINATE();
105 }
106 };
107
108 template <class _CvrefSenderId, class _Env, class _Completions>
109 struct __debug_receiver
110 {
111 using __t = __debug_receiver;
112 using __id = __debug_receiver;
113 using receiver_concept = receiver_t;
114 };
115
116 template <class _CvrefSenderId, class _Env, class... _Sigs>
117 struct __debug_receiver<_CvrefSenderId, _Env,
118 completion_signatures<_Sigs...>> //
119 : __valid_completions<__normalize_sig_t<_Sigs>...>
120 {
121 using __t = __debug_receiver;
122 using __id = __debug_receiver;
123 using receiver_concept = receiver_t;
124
125 STDEXEC_ATTRIBUTE((host, device))
get_envstdexec::__debug::__debug_receiver126 auto get_env() const noexcept -> __debug_env_t<_Env>
127 {
128 STDEXEC_TERMINATE();
129 }
130 };
131
132 struct _COMPLETION_SIGNATURES_MISMATCH_
133 {};
134
135 template <class _Sig>
136 struct _COMPLETION_SIGNATURE_
137 {};
138
139 template <class... _Sigs>
140 struct _IS_NOT_ONE_OF_
141 {};
142
143 template <class _Sender>
144 struct _SIGNAL_SENT_BY_SENDER_
145 {};
146
147 template <class _Warning>
148 [[deprecated(
149 "The sender claims to send a particular set of completions,"
150 " but in actual fact it completes with a result that is not"
151 " one of the declared completion signatures.")]] STDEXEC_ATTRIBUTE((host,
_ATTENTION_()152 device)) void _ATTENTION_() noexcept
153 {}
154
155 template <class _Sig>
156 struct __invalid_completion
157 {
158 struct __t
159 {
160 template <class _CvrefSenderId, class _Env, class... _Sigs>
161 // BUGBUG this works around a recently (aug 2023) introduced regression
162 // in nvc++
163 requires(!__one_of<_Sig, _Sigs...>)
__tstdexec::__debug::__invalid_completion::__t164 __t(__debug_receiver<_CvrefSenderId, _Env,
165 completion_signatures<_Sigs...>>&&) noexcept
166 {
167 using _SenderId = __decay_t<_CvrefSenderId>;
168 using _Sender = stdexec::__t<_SenderId>;
169 using _What = //
170 _WARNING_< //
171 _COMPLETION_SIGNATURES_MISMATCH_,
172 _COMPLETION_SIGNATURE_<_Sig>, _IS_NOT_ONE_OF_<_Sigs...>,
173 _SIGNAL_SENT_BY_SENDER_<__name_of<_Sender>>>;
174 __debug::_ATTENTION_<_What>();
175 }
176 };
177 };
178
179 template <__completion_tag _Tag, class... _Args>
180 STDEXEC_ATTRIBUTE((host, device))
tag_invoke(_Tag,__t<__invalid_completion<_Tag (_Args...)>>,_Args &&...)181 void tag_invoke(_Tag, __t<__invalid_completion<_Tag(_Args...)>>,
182 _Args&&...) noexcept
183 {}
184
185 struct __debug_operation
186 {
startstdexec::__debug::__debug_operation187 void start() & noexcept {}
188 };
189
190 ////////////////////////////////////////////////////////////////////////////
191 // `__debug_sender`
192 // ===============
193 //
194 // Understanding why a particular sender doesn't connect to a particular
195 // receiver is nigh impossible in the current design due to limitations in
196 // how the compiler reports overload resolution failure in the presence of
197 // constraints. `__debug_sender` is a utility to assist with the process. It
198 // gives you the deep template instantiation backtrace that you need to
199 // understand where in a chain of senders the problem is occurring.
200 //
201 // ```c++
202 // template <class _Sigs, class _Env = empty_env, class _Sender>
203 // void __debug_sender(_Sender&& __sndr, _Env = {});
204 //
205 // template <class _Env = empty_env, class _Sender>
206 // void __debug_sender(_Sender&& __sndr, _Env = {});
207 // ```
208 //
209 // **Usage:**
210 //
211 // To find out where in a chain of senders a sender is failing to connect
212 // to a receiver, pass it to `__debug_sender`, optionally with an
213 // environment argument; e.g. `__debug_sender(sndr [, env])`
214 //
215 // To find out why a sender will not connect to a receiver of a particular
216 // signature, specify the set of completion signatures as an explicit template
217 // argument that names an instantiation of `completion_signatures`; e.g.:
218 // `__debug_sender<completion_signatures<set_value_t(int)>>(sndr [, env])`.
219 //
220 // **How it works:**
221 //
222 // The `__debug_sender` function `connect`'s the sender to a
223 // `__debug_receiver`, whose environment is augmented with a special
224 // `__is_debug_env_t` query. An additional fall-back overload is added to
225 // the `connect` CPO that recognizes receivers whose environments respond to
226 // that query and lets them through. Then in a non-immediate context, it
227 // looks for a `tag_invoke(connect_t...)` overload for the input sender and
228 // receiver. This will recurse until it hits the `tag_invoke` call that is
229 // causing the failure.
230 //
231 // At least with clang, this gives me a nice backtrace, at the bottom of
232 // which is the faulty `tag_invoke` overload with a mention of the
233 // constraint that failed.
234 template <class _Sigs, class _Env = empty_env, class _Sender>
__debug_sender(_Sender && __sndr,const _Env &={})235 void __debug_sender(_Sender&& __sndr, const _Env& = {})
236 {
237 if constexpr (!__is_debug_env<_Env>)
238 {
239 if (sizeof(_Sender) == ~0u)
240 { // never true
241 using _Receiver =
242 __debug_receiver<__cvref_id<_Sender>, _Env, _Sigs>;
243 using _Operation = connect_result_t<_Sender, _Receiver>;
244 // static_assert(receiver_of<_Receiver, _Sigs>);
245 if constexpr (!same_as<_Operation, __debug_operation>)
246 {
247 auto __op =
248 connect(static_cast<_Sender&&>(__sndr), _Receiver{});
249 stdexec::start(__op);
250 }
251 }
252 }
253 }
254
255 template <class _Env = empty_env, class _Sender>
__debug_sender(_Sender && __sndr,const _Env &={})256 void __debug_sender(_Sender&& __sndr, const _Env& = {})
257 {
258 if constexpr (!__is_debug_env<_Env>)
259 {
260 if (sizeof(_Sender) == ~0ul)
261 { // never true
262 using _Sigs =
263 __completion_signatures_of_t<_Sender, __debug_env_t<_Env>>;
264 if constexpr (!same_as<_Sigs, __debug::__completion_signatures>)
265 {
266 using _Receiver =
267 __debug_receiver<__cvref_id<_Sender>, _Env, _Sigs>;
268 using _Operation = connect_result_t<_Sender, _Receiver>;
269 // static_assert(receiver_of<_Receiver, _Sigs>);
270 if constexpr (!same_as<_Operation, __debug_operation>)
271 {
272 auto __op =
273 connect(static_cast<_Sender&&>(__sndr), _Receiver{});
274 stdexec::start(__op);
275 }
276 }
277 }
278 }
279 }
280 } // namespace __debug
281
282 using __debug::__debug_sender;
283 using __debug::__is_debug_env;
284 } // namespace stdexec
285