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 "__execution_fwd.hpp"
19 
20 // include these after __execution_fwd.hpp
21 #include "__awaitable.hpp"
22 #include "__completion_signatures.hpp"
23 #include "__concepts.hpp"
24 #include "__connect_awaitable.hpp"
25 #include "__debug.hpp"
26 #include "__env.hpp"
27 #include "__operation_states.hpp"
28 #include "__receivers.hpp"
29 #include "__senders_core.hpp"
30 #include "__transform_completion_signatures.hpp"
31 #include "__transform_sender.hpp"
32 #include "__type_traits.hpp"
33 
34 namespace stdexec
35 {
36 /////////////////////////////////////////////////////////////////////////////
37 // [execution.get_completion_signatures]
38 namespace __detail
39 {
40 struct __dependent_completions
41 {};
42 
43 template <class _Completions>
44 concept __well_formed_sender =
45     __valid_completion_signatures<_Completions> ||
46     __same_as<_Completions, _ERROR_<__dependent_completions>>;
47 } // namespace __detail
48 
49 using dependent_completions = _ERROR_<__detail::__dependent_completions>;
50 
51 namespace __sigs
52 {
53 template <class _Sender, class _Env>
54 using __tfx_sender =
55     transform_sender_result_t<__late_domain_of_t<_Sender, _Env>, _Sender, _Env>;
56 
57 template <class _Sender, class... _Env>
58 using __member_result_t =
59     decltype(__declval<_Sender>().get_completion_signatures(
60         __declval<_Env>()...));
61 
62 template <class _Sender, class... _Env>
63 using __static_member_result_t =               //
64     decltype(STDEXEC_REMOVE_REFERENCE(_Sender) //
65              ::get_completion_signatures(__declval<_Sender>(),
66                                          __declval<_Env>()...));
67 
68 template <class _Sender, class... _Env>
69 concept __with_member = __mvalid<__member_result_t, _Sender, _Env...>;
70 
71 template <class _Sender, class... _Env>
72 concept __with_static_member =
73     __mvalid<__static_member_result_t, _Sender, _Env...>;
74 
75 template <class _Sender, class... _Env>
76 concept __with_tag_invoke = //
77     tag_invocable<get_completion_signatures_t, _Sender, _Env...>;
78 
79 template <class _Sender, class... _Env>
80 concept __with_legacy_tag_invoke = //
81     (sizeof...(_Env) == 0) &&
82     tag_invocable<get_completion_signatures_t, _Sender, empty_env>;
83 
84 template <class _Sender>
85 using __member_alias_t = //
86     typename __decay_t<_Sender>::completion_signatures;
87 
88 template <class _Sender>
89 concept __with_member_alias = __mvalid<__member_alias_t, _Sender>;
90 
91 struct get_completion_signatures_t
92 {
93     template <class _Sender, class... _Env>
__implstdexec::__sigs::get_completion_signatures_t94     static auto __impl()
95     {
96         // Compute the type of the transformed sender:
97         using __tfx_fn =
98             __if_c<sizeof...(_Env) == 0, __mconst<_Sender>, __q<__tfx_sender>>;
99         using _TfxSender = __minvoke<__tfx_fn, _Sender, _Env...>;
100 
101         if constexpr (__merror<_TfxSender>)
102         {
103             // Computing the type of the transformed sender returned an error
104             // type. Propagate it.
105             return static_cast<_TfxSender (*)()>(nullptr);
106         }
107         else if constexpr (__with_member_alias<_TfxSender>)
108         {
109             using _Result = __member_alias_t<_TfxSender>;
110             return static_cast<_Result (*)()>(nullptr);
111         }
112         else if constexpr (__with_static_member<_TfxSender, _Env...>)
113         {
114             using _Result = __static_member_result_t<_TfxSender, _Env...>;
115             return static_cast<_Result (*)()>(nullptr);
116         }
117         else if constexpr (__with_member<_TfxSender, _Env...>)
118         {
119             using _Result = __member_result_t<_TfxSender, _Env...>;
120             return static_cast<_Result (*)()>(nullptr);
121         }
122         else if constexpr (__with_tag_invoke<_TfxSender, _Env...>)
123         {
124             using _Result = tag_invoke_result_t<get_completion_signatures_t,
125                                                 _TfxSender, _Env...>;
126             return static_cast<_Result (*)()>(nullptr);
127         }
128         else if constexpr (__with_legacy_tag_invoke<_TfxSender, _Env...>)
129         {
130             // This branch is strictly for backwards compatibility
131             using _Result = tag_invoke_result_t<get_completion_signatures_t,
132                                                 _Sender, empty_env>;
133             return static_cast<_Result (*)()>(nullptr);
134             // [WAR] The explicit cast to bool below is to work around a bug in
135             // nvc++ (nvbug#4707793)
136         }
137         else if constexpr (bool(__awaitable<_TfxSender,
138                                             __env::__promise<_Env>...>))
139         {
140             using _AwaitResult =
141                 __await_result_t<_TfxSender, __env::__promise<_Env>...>;
142             using _Result = completion_signatures<
143                 // set_value_t() or set_value_t(T)
144                 __minvoke<__mremove<void, __qf<set_value_t>>, _AwaitResult>,
145                 set_error_t(std::exception_ptr), set_stopped_t()>;
146             return static_cast<_Result (*)()>(nullptr);
147         }
148         else if constexpr (sizeof...(_Env) == 0)
149         {
150             // It's possible this is a dependent sender.
151             return static_cast<dependent_completions (*)()>(nullptr);
152         }
153         else if constexpr ((__is_debug_env<_Env> || ...))
154         {
155             using __tag_invoke::tag_invoke;
156             // This ought to cause a hard error that indicates where the problem
157             // is.
158             using _Completions [[maybe_unused]] =
159                 tag_invoke_result_t<get_completion_signatures_t, _Sender,
160                                     _Env...>;
161             return static_cast<__debug::__completion_signatures (*)()>(nullptr);
162         }
163         else
164         {
165             using _Result = __mexception<_UNRECOGNIZED_SENDER_TYPE_<>,
166                                          _WITH_SENDER_<_Sender>,
167                                          _WITH_ENVIRONMENT_<_Env>...>;
168             return static_cast<_Result (*)()>(nullptr);
169         }
170     }
171 
172     // NOT TO SPEC: if we're unable to compute the completion signatures,
173     // return an error type instead of SFINAE.
174     template <class _Sender, class... _Env>
175         requires(sizeof...(_Env) <= 1)
operator ()stdexec::__sigs::get_completion_signatures_t176     constexpr auto operator()(_Sender&&, _Env&&...) const noexcept //
177         -> decltype(__impl<_Sender, _Env...>()())
178     {
179         return {};
180     }
181 };
182 } // namespace __sigs
183 
184 using __sigs::get_completion_signatures_t;
185 inline constexpr get_completion_signatures_t get_completion_signatures{};
186 
187 /////////////////////////////////////////////////////////////////////////////
188 // [execution.senders.connect]
189 namespace __connect
190 {
191 template <class _Sender, class _Receiver>
192 using __tfx_sender = //
193     transform_sender_result_t<__late_domain_of_t<_Sender, env_of_t<_Receiver>>,
194                               _Sender, env_of_t<_Receiver>>;
195 
196 template <class _Sender, class _Receiver>
197 using __member_result_t =
198     decltype(__declval<_Sender>().connect(__declval<_Receiver>()));
199 
200 template <class _Sender, class _Receiver>
201 using __static_member_result_t =
202     decltype(STDEXEC_REMOVE_REFERENCE(_Sender) //
203              ::connect(__declval<_Sender>(), __declval<_Receiver>()));
204 
205 template <class _Sender, class _Receiver>
206 concept __with_member = __mvalid<__member_result_t, _Sender, _Receiver>;
207 
208 template <class _Sender, class _Receiver>
209 concept __with_static_member =
210     __mvalid<__static_member_result_t, _Sender, _Receiver>;
211 
212 template <class _Sender, class _Receiver>
213 concept __with_tag_invoke = tag_invocable<connect_t, _Sender, _Receiver>;
214 
215 template <class _Sender, class _Receiver>
216 concept __with_co_await = __callable<__connect_awaitable_t, _Sender, _Receiver>;
217 
218 struct connect_t
219 {
220     template <class _Sender, class _Env>
__check_signaturesstdexec::__connect::connect_t221     static constexpr auto __check_signatures() -> bool
222     {
223         if constexpr (sender_in<_Sender, _Env>)
224         {
225             // Instantiate __debug_sender via completion_signatures_of_t to
226             // check that the actual completions match the expected completions.
227             //
228             // Instantiate completion_signatures_of_t only if sender_in is true
229             // to workaround Clang not implementing CWG#2369 yet (connect() does
230             // not have a constraint for _Sender satisfying sender_in).
231             using __checked_signatures
232                 [[maybe_unused]] = completion_signatures_of_t<_Sender, _Env>;
233         }
234         return true;
235     }
236 
237     template <class _Sender, class _Receiver>
__select_implstdexec::__connect::connect_t238     static constexpr auto __select_impl() noexcept
239     {
240         using _Domain = __late_domain_of_t<_Sender, env_of_t<_Receiver>>;
241         using _TfxSender = __tfx_sender<_Sender, _Receiver>;
242         constexpr bool _NothrowTfxSender =
243             __nothrow_callable<transform_sender_t, _Domain, _Sender,
244                                env_of_t<_Receiver>>;
245 
246 #if STDEXEC_ENABLE_EXTRA_TYPE_CHECKING()
247         static_assert(__check_signatures<_TfxSender, env_of_t<_Receiver>>());
248 #endif
249 
250         if constexpr (__with_static_member<_TfxSender, _Receiver>)
251         {
252             using _Result = __static_member_result_t<_TfxSender, _Receiver>;
253             constexpr bool _Nothrow = //
254                 _NothrowTfxSender &&
255                 noexcept(__declval<_TfxSender>().connect(
256                     __declval<_TfxSender>(), __declval<_Receiver>()));
257             return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
258         }
259         else if constexpr (__with_member<_TfxSender, _Receiver>)
260         {
261             using _Result = __member_result_t<_TfxSender, _Receiver>;
262             constexpr bool _Nothrow = //
263                 _NothrowTfxSender && noexcept(__declval<_TfxSender>().connect(
264                                          __declval<_Receiver>()));
265             return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
266         }
267         else if constexpr (__with_tag_invoke<_TfxSender, _Receiver>)
268         {
269             using _Result =
270                 tag_invoke_result_t<connect_t, _TfxSender, _Receiver>;
271             constexpr bool _Nothrow = //
272                 _NothrowTfxSender &&
273                 nothrow_tag_invocable<connect_t, _TfxSender, _Receiver>;
274             return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
275         }
276         else if constexpr (__with_co_await<_TfxSender, _Receiver>)
277         {
278             using _Result =
279                 __call_result_t<__connect_awaitable_t, _TfxSender, _Receiver>;
280             return static_cast<_Result (*)()>(nullptr);
281         }
282         else
283         {
284             using _Result = __debug::__debug_operation;
285             return static_cast<_Result (*)() noexcept(_NothrowTfxSender)>(
286                 nullptr);
287         }
288     }
289 
290     template <class _Sender, class _Receiver>
291     using __select_impl_t = decltype(__select_impl<_Sender, _Receiver>());
292 
293     template <sender _Sender, receiver _Receiver>
294         requires __with_static_member<__tfx_sender<_Sender, _Receiver>,
295                                       _Receiver> ||
296                      __with_member<__tfx_sender<_Sender, _Receiver>,
297                                    _Receiver> ||
298                      __with_tag_invoke<__tfx_sender<_Sender, _Receiver>,
299                                        _Receiver> ||
300                      __with_co_await<__tfx_sender<_Sender, _Receiver>,
301                                      _Receiver> ||
302                      __is_debug_env<env_of_t<_Receiver>>
303     auto operator()(_Sender&& __sndr, _Receiver&& __rcvr) const
304         noexcept(__nothrow_callable<__select_impl_t<_Sender, _Receiver>>)
305             -> __call_result_t<__select_impl_t<_Sender, _Receiver>>
306     {
307         using _TfxSender = __tfx_sender<_Sender, _Receiver>;
308         auto&& __env = get_env(__rcvr);
309         auto __domain = __get_late_domain(__sndr, __env);
310 
311         if constexpr (__with_static_member<_TfxSender, _Receiver>)
312         {
313             static_assert(
314                 operation_state<
315                     __static_member_result_t<_TfxSender, _Receiver>>,
316                 "Sender::connect(sender, receiver) must return a type that "
317                 "satisfies the operation_state concept");
318             auto&& __tfx_sndr = transform_sender(
319                 __domain, static_cast<_Sender&&>(__sndr), __env);
320             return __tfx_sndr.connect(static_cast<_TfxSender&&>(__tfx_sndr),
321                                       static_cast<_Receiver&&>(__rcvr));
322         }
323         else if constexpr (__with_member<_TfxSender, _Receiver>)
324         {
325             static_assert(
326                 operation_state<__member_result_t<_TfxSender, _Receiver>>,
327                 "sender.connect(receiver) must return a type that "
328                 "satisfies the operation_state concept");
329             return transform_sender(__domain, static_cast<_Sender&&>(__sndr),
330                                     __env)
331                 .connect(static_cast<_Receiver&&>(__rcvr));
332         }
333         else if constexpr (__with_tag_invoke<_TfxSender, _Receiver>)
334         {
335             static_assert(
336                 operation_state<
337                     tag_invoke_result_t<connect_t, _TfxSender, _Receiver>>,
338                 "stdexec::connect(sender, receiver) must return a type that "
339                 "satisfies the operation_state concept");
340             return tag_invoke(
341                 connect_t(),
342                 transform_sender(__domain, static_cast<_Sender&&>(__sndr),
343                                  __env),
344                 static_cast<_Receiver&&>(__rcvr));
345         }
346         else if constexpr (__with_co_await<_TfxSender, _Receiver>)
347         {
348             return __connect_awaitable( //
349                 transform_sender(__domain, static_cast<_Sender&&>(__sndr),
350                                  __env),
351                 static_cast<_Receiver&&>(__rcvr));
352         }
353         else
354         {
355             // This should generate an instantiation backtrace that contains
356             // useful debugging information.
357             using __tag_invoke::tag_invoke;
358             tag_invoke(*this,
359                        transform_sender(__domain,
360                                         static_cast<_Sender&&>(__sndr), __env),
361                        static_cast<_Receiver&&>(__rcvr));
362         }
363     }
364 
querystdexec::__connect::connect_t365     static constexpr auto query(forwarding_query_t) noexcept -> bool
366     {
367         return false;
368     }
369 };
370 } // namespace __connect
371 
372 using __connect::connect_t;
373 inline constexpr __connect::connect_t connect{};
374 
375 /////////////////////////////////////////////////////////////////////////////
376 // [exec.snd]
377 template <class _Sender, class _Receiver>
378 concept sender_to =                            //
379     receiver<_Receiver>                        //
380     && sender_in<_Sender, env_of_t<_Receiver>> //
381     && __receiver_from<_Receiver, _Sender>     //
382     && requires(_Sender&& __sndr, _Receiver&& __rcvr) {
383            connect(static_cast<_Sender&&>(__sndr),
384                    static_cast<_Receiver&&>(__rcvr));
385        };
386 
387 template <class _Tag, class... _Args>
388 auto __tag_of_sig_(_Tag (*)(_Args...)) -> _Tag;
389 template <class _Sig>
390 using __tag_of_sig_t =
391     decltype(stdexec::__tag_of_sig_(static_cast<_Sig*>(nullptr)));
392 
393 template <class _Sender, class _SetSig, class _Env = empty_env>
394 concept sender_of =          //
395     sender_in<_Sender, _Env> //
396     &&
397     same_as<
398         __types<_SetSig>,
399         __gather_completions_of<
400             __tag_of_sig_t<_SetSig>, _Sender, _Env,
401             __mcompose_q<__types, __qf<__tag_of_sig_t<_SetSig>>::template __f>,
402             __mconcat<__qq<__types>>>>;
403 
404 template <class _Error>
405     requires false
406 using __nofail_t = _Error;
407 
408 template <class _Sender, class _Env = empty_env>
409 concept __nofail_sender =
410     sender_in<_Sender, _Env> &&
411     requires {
412         typename __gather_completion_signatures<
413             __completion_signatures_of_t<_Sender, _Env>, set_error_t,
414             __nofail_t, __sigs::__default_completion, __types>;
415     };
416 
417 /////////////////////////////////////////////////////////////////////////////
418 // early sender type-checking
419 template <class _Sender>
420 concept __well_formed_sender = __detail::__well_formed_sender<__minvoke<
421     __with_default_q<__completion_signatures_of_t, dependent_completions>,
422     _Sender>>;
423 } // namespace stdexec
424