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