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