xref: /openbmc/sdbusplus/include/sdbusplus/async/stdexec/__detail/__senders.hpp (revision 10d0b4b7d1498cfd5c3d37edea271a54d1984e41)
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 "__tag_invoke.hpp"
31 #include "__transform_completion_signatures.hpp"
32 #include "__transform_sender.hpp"
33 #include "__type_traits.hpp"
34 
35 namespace stdexec {
36   /////////////////////////////////////////////////////////////////////////////
37   // [execution.get_completion_signatures]
38   namespace __detail {
39     struct __dependent_completions { };
40 
41     using dependent_completions = _ERROR_<__detail::__dependent_completions>;
42 
43     template <class _Completions>
44     concept __well_formed_completions_helper = __valid_completion_signatures<_Completions>
45                                             || __same_as<_Completions, dependent_completions>;
46 
47     template <class _Completions>
48     inline constexpr bool __well_formed_completions_v =
49       __well_formed_completions_helper<_Completions>;
50 
51     template <class _Completions>
52     concept __well_formed_completions = __well_formed_completions_v<_Completions>;
53   } // namespace __detail
54 
55   using __detail::dependent_completions;
56 
57   namespace __sigs {
58     template <class _Sender, class _Env>
59     using __tfx_sender =
60       transform_sender_result_t<__late_domain_of_t<_Sender, _Env>, _Sender, _Env>;
61 
62     template <class _Sender, class... _Env>
63     using __member_result_t = decltype(__declval<_Sender>()
64                                          .get_completion_signatures(__declval<_Env>()...));
65 
66     template <class _Sender, class... _Env>
67     using __static_member_result_t = decltype(STDEXEC_REMOVE_REFERENCE(
68       _Sender)::get_completion_signatures(__declval<_Sender>(), __declval<_Env>()...));
69 
70     template <class _Sender, class... _Env>
71     concept __with_member = __mvalid<__member_result_t, _Sender, _Env...>;
72 
73     template <class _Sender, class... _Env>
74     concept __with_static_member = __mvalid<__static_member_result_t, _Sender, _Env...>;
75 
76     template <class _Sender, class... _Env>
77     concept __with_tag_invoke = tag_invocable<get_completion_signatures_t, _Sender, _Env...>;
78 
79     template <class _Sender, class... _Env>
80     concept __with_legacy_tag_invoke = (sizeof...(_Env) == 0)
81                                     && tag_invocable<get_completion_signatures_t, _Sender, env<>>;
82 
83     template <class _Sender>
84     using __member_alias_t = __decay_t<_Sender>::completion_signatures;
85 
86     template <class _Sender>
87     concept __with_member_alias = __mvalid<__member_alias_t, _Sender>;
88 
89     struct get_completion_signatures_t {
90       template <class _Sender, class... _Env>
91         requires(sizeof...(_Env) <= 1)
__implstdexec::__sigs::get_completion_signatures_t92       static auto __impl() {
93         // Compute the type of the transformed sender:
94         using __tfx_fn = __if_c<sizeof...(_Env) == 0, __mconst<_Sender>, __q<__tfx_sender>>;
95         using _TfxSender = __minvoke<__tfx_fn, _Sender, _Env...>;
96 
97         if constexpr (__merror<_TfxSender>) {
98           // Computing the type of the transformed sender returned an error type. Propagate it.
99           return static_cast<_TfxSender (*)()>(nullptr);
100         } else if constexpr (__with_member_alias<_TfxSender>) {
101           using _Result = __member_alias_t<_TfxSender>;
102           return static_cast<_Result (*)()>(nullptr);
103         } else if constexpr (__with_static_member<_TfxSender, _Env...>) {
104           using _Result = __static_member_result_t<_TfxSender, _Env...>;
105           return static_cast<_Result (*)()>(nullptr);
106         } else if constexpr (__with_member<_TfxSender, _Env...>) {
107           using _Result = __member_result_t<_TfxSender, _Env...>;
108           return static_cast<_Result (*)()>(nullptr);
109         } else if constexpr (__with_tag_invoke<_TfxSender, _Env...>) {
110           using _Result = tag_invoke_result_t<get_completion_signatures_t, _TfxSender, _Env...>;
111           return static_cast<_Result (*)()>(nullptr);
112         } else if constexpr (__with_legacy_tag_invoke<_TfxSender, _Env...>) {
113           // This branch is strictly for backwards compatibility
114           using _Result = tag_invoke_result_t<get_completion_signatures_t, _Sender, env<>>;
115           return static_cast<_Result (*)()>(nullptr);
116           // [WAR] The explicit cast to bool below is to work around a bug in nvc++ (nvbug#4707793)
117         } else if constexpr (bool(__awaitable<_TfxSender, __env::__promise<_Env>...>)) {
118           using _AwaitResult = __await_result_t<_TfxSender, __env::__promise<_Env>...>;
119           using _Result = completion_signatures<
120             // set_value_t() or set_value_t(T)
121             __minvoke<__mremove<void, __qf<set_value_t>>, _AwaitResult>,
122             set_error_t(std::exception_ptr),
123             set_stopped_t()
124           >;
125           return static_cast<_Result (*)()>(nullptr);
126         } else if constexpr (sizeof...(_Env) == 0) {
127           // It's possible this is a dependent sender.
128           return static_cast<dependent_completions (*)()>(nullptr);
129         } else if constexpr ((__is_debug_env<_Env> || ...)) {
130           // This ought to cause a hard error that indicates where the problem is.
131           using _Completions [[maybe_unused]] =
132             decltype(std::remove_reference_t<_TfxSender>::get_completion_signatures(
133               __declval<_TfxSender>(), __declval<_Env>()...));
134           return static_cast<__debug::__completion_signatures (*)()>(nullptr);
135         } else {
136           using _Result = __mexception<
137             _UNRECOGNIZED_SENDER_TYPE_<>,
138             _WITH_SENDER_<_Sender>,
139             _WITH_ENVIRONMENT_<_Env>...
140           >;
141           return static_cast<_Result (*)()>(nullptr);
142         }
143       }
144 
145       // NOT TO SPEC: if we're unable to compute the completion signatures,
146       // return an error type instead of SFINAE.
147       template <class _Sender, class... _Env>
148         requires(sizeof...(_Env) <= 1)
149       constexpr auto
operator ()stdexec::__sigs::get_completion_signatures_t150         operator()(_Sender&&, _Env&&...) const noexcept -> decltype(__impl<_Sender, _Env...>()()) {
151         return {};
152       }
153     };
154   } // namespace __sigs
155 
156   using __sigs::get_completion_signatures_t;
157   inline constexpr get_completion_signatures_t get_completion_signatures{};
158 
159   /////////////////////////////////////////////////////////////////////////////
160   // [execution.senders.connect]
161   namespace __connect {
162     template <class _Sender, class _Receiver>
163     using __tfx_sender = __mmemoize_q<
164       transform_sender_result_t,
165       __late_domain_of_t<_Sender, env_of_t<_Receiver>>,
166       _Sender,
167       env_of_t<_Receiver>
168     >;
169 
170     template <class _Sender, class _Receiver>
171     using __member_result_t = decltype(__declval<_Sender>().connect(__declval<_Receiver>()));
172 
173     template <class _Sender, class _Receiver>
174     using __static_member_result_t = decltype(STDEXEC_REMOVE_REFERENCE(
175       _Sender)::connect(__declval<_Sender>(), __declval<_Receiver>()));
176 
177     template <class _Sender, class _Receiver>
178     concept __with_member = __mvalid<__member_result_t, _Sender, _Receiver>;
179 
180     template <class _Sender, class _Receiver>
181     concept __with_static_member = __mvalid<__static_member_result_t, _Sender, _Receiver>;
182 
183     template <class _Sender, class _Receiver>
184     concept __with_tag_invoke = tag_invocable<connect_t, _Sender, _Receiver>;
185 
186     template <class _Sender, class _Receiver>
187     concept __with_co_await = __callable<__connect_awaitable_t, _Sender, _Receiver>;
188 
189     struct _NO_USABLE_CONNECT_CUSTOMIZATION_FOUND_ {
190       void operator()() const noexcept = delete;
191     };
192 
193     struct connect_t {
194       template <class _Sender, class _Receiver>
195         requires sender_in<_Sender, env_of_t<_Receiver>> && __receiver_from<_Receiver, _Sender>
STDEXEC_ATTRIBUTEstdexec::__connect::connect_t196       STDEXEC_ATTRIBUTE(always_inline)
197       static constexpr auto __type_check_arguments() -> bool {
198         if constexpr (sender_in<_Sender, env_of_t<_Receiver>>) {
199           // Instantiate __debug_sender via completion_signatures_of_t to check that the actual
200           // completions match the expected completions.
201           using __checked_signatures
202             [[maybe_unused]] = completion_signatures_of_t<_Sender, env_of_t<_Receiver>>;
203         } else {
204           __diagnose_sender_concept_failure<_Sender, env_of_t<_Receiver>>();
205         }
206         return true;
207       }
208 
209       template <class _OpState>
__check_operation_statestdexec::__connect::connect_t210       static constexpr void __check_operation_state() noexcept {
211         static_assert(operation_state<_OpState>, STDEXEC_ERROR_CANNOT_CONNECT_SENDER_TO_RECEIVER);
212       }
213 
214       template <class _Sender, class _Receiver>
__select_implstdexec::__connect::connect_t215       static constexpr auto __select_impl() noexcept {
216         using _Domain = __late_domain_of_t<_Sender, env_of_t<_Receiver>>;
217         using _TfxSender = __tfx_sender<_Sender, _Receiver>;
218         constexpr bool _NothrowTfxSender =
219           __nothrow_callable<transform_sender_t, _Domain, _Sender, env_of_t<_Receiver>>;
220 
221         static_assert(sender<_Sender>, "The first argument to stdexec::connect must be a sender");
222         static_assert(
223           receiver<_Receiver>, "The second argument to stdexec::connect must be a receiver");
224 #if STDEXEC_ENABLE_EXTRA_TYPE_CHECKING()
225         static_assert(__type_check_arguments<_TfxSender, _Receiver>());
226 #endif
227 
228         if constexpr (__with_static_member<_TfxSender, _Receiver>) {
229           using _Result = __static_member_result_t<_TfxSender, _Receiver>;
230           __check_operation_state<_Result>();
231           constexpr bool _Nothrow = _NothrowTfxSender
232                                  && noexcept(
233                                       __declval<_TfxSender>()
234                                         .connect(__declval<_TfxSender>(), __declval<_Receiver>()));
235           return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
236         } else if constexpr (__with_member<_TfxSender, _Receiver>) {
237           using _Result = __member_result_t<_TfxSender, _Receiver>;
238           __check_operation_state<_Result>();
239           constexpr bool _Nothrow = _NothrowTfxSender
240                                  && noexcept(__declval<_TfxSender>()
241                                                .connect(__declval<_Receiver>()));
242           return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
243         } else if constexpr (__with_tag_invoke<_TfxSender, _Receiver>) {
244           using _Result = tag_invoke_result_t<connect_t, _TfxSender, _Receiver>;
245           __check_operation_state<_Result>();
246           constexpr bool _Nothrow = _NothrowTfxSender
247                                  && nothrow_tag_invocable<connect_t, _TfxSender, _Receiver>;
248           return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
249         } else if constexpr (__with_co_await<_TfxSender, _Receiver>) {
250           using _Result = __call_result_t<__connect_awaitable_t, _TfxSender, _Receiver>;
251           return static_cast<_Result (*)()>(nullptr);
252         } else if constexpr (__is_debug_env<env_of_t<_Receiver>>) {
253           using _Result = __debug::__debug_operation;
254           return static_cast<_Result (*)() noexcept(_NothrowTfxSender)>(nullptr);
255         } else {
256           return _NO_USABLE_CONNECT_CUSTOMIZATION_FOUND_();
257         }
258       }
259 
260       template <class _Sender, class _Receiver>
261       using __select_impl_t = decltype(__select_impl<_Sender, _Receiver>());
262 
263       template <class _Sender, class _Receiver>
operator ()stdexec::__connect::connect_t264       auto operator()(_Sender&& __sndr, _Receiver&& __rcvr) const
265         noexcept(__nothrow_callable<__select_impl_t<_Sender, _Receiver>>)
266           -> __call_result_t<__select_impl_t<_Sender, _Receiver>> {
267 
268         static_assert(sender_in<_Sender, env_of_t<_Receiver>>);
269         static_assert(__receiver_from<_Receiver, _Sender>);
270 
271         using _TfxSender = __tfx_sender<_Sender, _Receiver>;
272         auto&& __env = get_env(__rcvr);
273         auto __domain = __get_late_domain(__sndr, __env);
274 
275         if constexpr (__with_static_member<_TfxSender, _Receiver>) {
276           auto&& __tfx_sndr = transform_sender(__domain, static_cast<_Sender&&>(__sndr), __env);
277           return __tfx_sndr
278             .connect(static_cast<_TfxSender&&>(__tfx_sndr), static_cast<_Receiver&&>(__rcvr));
279         } else if constexpr (__with_member<_TfxSender, _Receiver>) { // NOLINT(bugprone-branch-clone)
280           return transform_sender(__domain, static_cast<_Sender&&>(__sndr), __env)
281             .connect(static_cast<_Receiver&&>(__rcvr));
282         } else if constexpr (__with_tag_invoke<_TfxSender, _Receiver>) {
283           return tag_invoke(
284             connect_t(),
285             transform_sender(__domain, static_cast<_Sender&&>(__sndr), __env),
286             static_cast<_Receiver&&>(__rcvr));
287         } else if constexpr (__with_co_await<_TfxSender, _Receiver>) {
288           return __connect_awaitable(
289             transform_sender(__domain, static_cast<_Sender&&>(__sndr), __env),
290             static_cast<_Receiver&&>(__rcvr));
291         } else {
292           // This should generate an instantiation backtrace that contains useful
293           // debugging information.
294           return transform_sender(__domain, static_cast<_Sender&&>(__sndr), __env)
295             .connect(static_cast<_Receiver&&>(__rcvr));
296         }
297       }
298 
querystdexec::__connect::connect_t299       static constexpr auto query(forwarding_query_t) noexcept -> bool {
300         return false;
301       }
302     };
303   } // namespace __connect
304 
305   using __connect::connect_t;
306   inline constexpr __connect::connect_t connect{};
307 
308   /////////////////////////////////////////////////////////////////////////////
309   // [exec.snd]
310   template <class _Tag, class... _Args>
311   auto __tag_of_sig_(_Tag (*)(_Args...)) -> _Tag;
312   template <class _Sig>
313   using __tag_of_sig_t = decltype(stdexec::__tag_of_sig_(static_cast<_Sig*>(nullptr)));
314 
315   template <class _Sender, class _SetSig, class _Env = env<>>
316   concept sender_of = sender_in<_Sender, _Env>
317                    && same_as<
318                         __types<_SetSig>,
319                         __gather_completions_of<
320                           __tag_of_sig_t<_SetSig>,
321                           _Sender,
322                           _Env,
323                           __mcompose_q<__types, __qf<__tag_of_sig_t<_SetSig>>::template __f>,
324                           __mconcat<__qq<__types>>
325                         >
326                    >;
327 
328   template <class _Error>
329     requires false
330   using __nofail_t = _Error;
331 
332   template <class _Sender, class _Env = env<>>
333   concept __nofail_sender = sender_in<_Sender, _Env> && requires {
334     typename __gather_completion_signatures<
335       __completion_signatures_of_t<_Sender, _Env>,
336       set_error_t,
337       __nofail_t,
338       __sigs::__default_completion,
339       __types
340     >;
341   };
342 
343   /////////////////////////////////////////////////////////////////////////////
344   // early sender type-checking
345   template <class _Sender>
346   concept __well_formed_sender = __detail::__well_formed_completions<
347     __minvoke<__with_default_q<__completion_signatures_of_t, dependent_completions>, _Sender>
348   >;
349 } // namespace stdexec
350