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>
94     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)
176     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>
221     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>
238     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&& noexcept(__declval<_TfxSender>().connect(
255                     __declval<_TfxSender>(), __declval<_Receiver>()));
256             return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
257         }
258         else if constexpr (__with_member<_TfxSender, _Receiver>)
259         {
260             using _Result = __member_result_t<_TfxSender, _Receiver>;
261             constexpr bool _Nothrow = //
262                 _NothrowTfxSender&& noexcept(
263                     __declval<_TfxSender>().connect(__declval<_Receiver>()));
264             return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
265         }
266         else if constexpr (__with_tag_invoke<_TfxSender, _Receiver>)
267         {
268             using _Result =
269                 tag_invoke_result_t<connect_t, _TfxSender, _Receiver>;
270             constexpr bool _Nothrow = //
271                 _NothrowTfxSender &&
272                 nothrow_tag_invocable<connect_t, _TfxSender, _Receiver>;
273             return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
274         }
275         else if constexpr (__with_co_await<_TfxSender, _Receiver>)
276         {
277             using _Result =
278                 __call_result_t<__connect_awaitable_t, _TfxSender, _Receiver>;
279             return static_cast<_Result (*)()>(nullptr);
280         }
281         else
282         {
283             using _Result = __debug::__debug_operation;
284             return static_cast<_Result (*)() noexcept(_NothrowTfxSender)>(
285                 nullptr);
286         }
287     }
288 
289     template <class _Sender, class _Receiver>
290     using __select_impl_t = decltype(__select_impl<_Sender, _Receiver>());
291 
292     template <sender _Sender, receiver _Receiver>
293         requires __with_static_member<__tfx_sender<_Sender, _Receiver>,
294                                       _Receiver> ||
295                  __with_member<__tfx_sender<_Sender, _Receiver>, _Receiver> ||
296                  __with_tag_invoke<__tfx_sender<_Sender, _Receiver>,
297                                    _Receiver> ||
298                  __with_co_await<__tfx_sender<_Sender, _Receiver>, _Receiver> ||
299                  __is_debug_env<env_of_t<_Receiver>>
300     auto operator()(_Sender&& __sndr, _Receiver&& __rcvr) const
301         noexcept(__nothrow_callable<__select_impl_t<_Sender, _Receiver>>)
302             -> __call_result_t<__select_impl_t<_Sender, _Receiver>>
303     {
304         using _TfxSender = __tfx_sender<_Sender, _Receiver>;
305         auto&& __env = get_env(__rcvr);
306         auto __domain = __get_late_domain(__sndr, __env);
307 
308         if constexpr (__with_static_member<_TfxSender, _Receiver>)
309         {
310             static_assert(
311                 operation_state<
312                     __static_member_result_t<_TfxSender, _Receiver>>,
313                 "Sender::connect(sender, receiver) must return a type that "
314                 "satisfies the operation_state concept");
315             auto&& __tfx_sndr = transform_sender(
316                 __domain, static_cast<_Sender&&>(__sndr), __env);
317             return __tfx_sndr.connect(static_cast<_TfxSender&&>(__tfx_sndr),
318                                       static_cast<_Receiver&&>(__rcvr));
319         }
320         else if constexpr (__with_member<_TfxSender, _Receiver>)
321         {
322             static_assert(
323                 operation_state<__member_result_t<_TfxSender, _Receiver>>,
324                 "sender.connect(receiver) must return a type that "
325                 "satisfies the operation_state concept");
326             return transform_sender(__domain, static_cast<_Sender&&>(__sndr),
327                                     __env)
328                 .connect(static_cast<_Receiver&&>(__rcvr));
329         }
330         else if constexpr (__with_tag_invoke<_TfxSender, _Receiver>)
331         {
332             static_assert(
333                 operation_state<
334                     tag_invoke_result_t<connect_t, _TfxSender, _Receiver>>,
335                 "stdexec::connect(sender, receiver) must return a type that "
336                 "satisfies the operation_state concept");
337             return tag_invoke(connect_t(),
338                               transform_sender(__domain,
339                                                static_cast<_Sender&&>(__sndr),
340                                                __env),
341                               static_cast<_Receiver&&>(__rcvr));
342         }
343         else if constexpr (__with_co_await<_TfxSender, _Receiver>)
344         {
345             return __connect_awaitable( //
346                 transform_sender(__domain, static_cast<_Sender&&>(__sndr),
347                                  __env),
348                 static_cast<_Receiver&&>(__rcvr));
349         }
350         else
351         {
352             // This should generate an instantiation backtrace that contains
353             // useful debugging information.
354             using __tag_invoke::tag_invoke;
355             tag_invoke(*this,
356                        transform_sender(__domain,
357                                         static_cast<_Sender&&>(__sndr), __env),
358                        static_cast<_Receiver&&>(__rcvr));
359         }
360     }
361 
362     static constexpr auto query(forwarding_query_t) noexcept -> bool
363     {
364         return false;
365     }
366 };
367 } // namespace __connect
368 
369 using __connect::connect_t;
370 inline constexpr __connect::connect_t connect{};
371 
372 /////////////////////////////////////////////////////////////////////////////
373 // [exec.snd]
374 template <class _Sender, class _Receiver>
375 concept sender_to =                            //
376     receiver<_Receiver>                        //
377     && sender_in<_Sender, env_of_t<_Receiver>> //
378     && __receiver_from<_Receiver, _Sender>     //
379     && requires(_Sender&& __sndr, _Receiver&& __rcvr) {
380            connect(static_cast<_Sender&&>(__sndr),
381                    static_cast<_Receiver&&>(__rcvr));
382        };
383 
384 template <class _Tag, class... _Args>
385 auto __tag_of_sig_(_Tag (*)(_Args...)) -> _Tag;
386 template <class _Sig>
387 using __tag_of_sig_t =
388     decltype(stdexec::__tag_of_sig_(static_cast<_Sig*>(nullptr)));
389 
390 template <class _Sender, class _SetSig, class _Env = empty_env>
391 concept sender_of =          //
392     sender_in<_Sender, _Env> //
393     &&
394     same_as<
395         __types<_SetSig>,
396         __gather_completions_of<
397             __tag_of_sig_t<_SetSig>, _Sender, _Env,
398             __mcompose_q<__types, __qf<__tag_of_sig_t<_SetSig>>::template __f>,
399             __mconcat<__qq<__types>>>>;
400 
401 template <class _Error>
402     requires false
403 using __nofail_t = _Error;
404 
405 template <class _Sender, class _Env = empty_env>
406 concept __nofail_sender =
407     sender_in<_Sender, _Env> &&
408     requires {
409         typename __gather_completion_signatures<
410             __completion_signatures_of_t<_Sender, _Env>, set_error_t,
411             __nofail_t, __sigs::__default_completion, __types>;
412     };
413 
414 /////////////////////////////////////////////////////////////////////////////
415 // early sender type-checking
416 template <class _Sender>
417 concept __well_formed_sender = __detail::__well_formed_sender<__minvoke<
418     __with_default_q<__completion_signatures_of_t, dependent_completions>,
419     _Sender>>;
420 } // namespace stdexec
421