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 "__basic_sender.hpp"
22 #include "__concepts.hpp"
23 #include "__diagnostics.hpp"
24 #include "__domain.hpp"
25 #include "__env.hpp"
26 #include "__meta.hpp"
27 #include "__sender_introspection.hpp"
28 #include "__type_traits.hpp"
29 
30 STDEXEC_PRAGMA_PUSH()
31 STDEXEC_PRAGMA_IGNORE_EDG(type_qualifiers_ignored_on_reference)
32 
33 namespace stdexec
34 {
35 /////////////////////////////////////////////////////////////////////////////
36 // dependent_domain
37 struct dependent_domain
38 {
39     template <class _Sender, class _Env>
40     static constexpr auto __is_nothrow_transform_sender() noexcept -> bool;
41 
42     template <sender_expr _Sender, class _Env>
43         requires same_as<__early_domain_of_t<_Sender>, dependent_domain>
44     STDEXEC_ATTRIBUTE((always_inline))
45     decltype(auto) transform_sender(_Sender&& __sndr, const _Env& __env) const
46         noexcept(__is_nothrow_transform_sender<_Sender, _Env>());
47 };
48 
49 /////////////////////////////////////////////////////////////////////////////
50 // [execution.transform_sender]
51 namespace __domain
52 {
53 struct __transform_env
54 {
55     template <class _Domain, class _Sender, class _Env>
56     STDEXEC_ATTRIBUTE((always_inline))
57     /*constexpr*/
operator ()stdexec::__domain::__transform_env58     decltype(auto) operator()(_Domain __dom, _Sender&& __sndr,
59                               _Env&& __env) const noexcept
60     {
61         if constexpr (__domain::__has_transform_env<_Domain, _Sender, _Env>)
62         {
63             return __dom.transform_env(static_cast<_Sender&&>(__sndr),
64                                        static_cast<_Env&&>(__env));
65         }
66         else
67         {
68             return default_domain().transform_env(
69                 static_cast<_Sender&&>(__sndr), static_cast<_Env&&>(__env));
70         }
71     }
72 };
73 
74 struct __transform_sender_1
75 {
76     template <class _Domain, class _Sender, class... _Env>
77     STDEXEC_ATTRIBUTE((always_inline))
__is_nothrowstdexec::__domain::__transform_sender_178     static constexpr bool __is_nothrow() noexcept
79     {
80         if constexpr (__domain::__has_transform_sender<_Domain, _Sender,
81                                                        _Env...>)
82         {
83             return noexcept(__declval<_Domain&>().transform_sender(
84                 __declval<_Sender>(), __declval<const _Env&>()...));
85         }
86         else
87         {
88             return //
89                 noexcept(default_domain().transform_sender(
90                     __declval<_Sender>(), __declval<const _Env&>()...));
91         }
92     }
93 
94     template <class _Domain, class _Sender, class... _Env>
95     STDEXEC_ATTRIBUTE((always_inline))
96     /*constexpr*/
operator ()stdexec::__domain::__transform_sender_197     decltype(auto) operator()(_Domain __dom, _Sender&& __sndr,
98                               const _Env&... __env) const
99         noexcept(__is_nothrow<_Domain, _Sender, const _Env&...>())
100     {
101         if constexpr (__domain::__has_transform_sender<_Domain, _Sender,
102                                                        _Env...>)
103         {
104             return __dom.transform_sender(static_cast<_Sender&&>(__sndr),
105                                           __env...);
106         }
107         else
108         {
109             return default_domain().transform_sender(
110                 static_cast<_Sender&&>(__sndr), __env...);
111         }
112     }
113 };
114 
115 template <class _Ty, class _Uy>
116 concept __decay_same_as = same_as<__decay_t<_Ty>, __decay_t<_Uy>>;
117 
118 struct __transform_sender
119 {
120     template <class _Self = __transform_sender, class _Domain, class _Sender,
121               class... _Env>
122     STDEXEC_ATTRIBUTE((always_inline))
123     /*constexpr*/
operator ()stdexec::__domain::__transform_sender124     decltype(auto) operator()(_Domain __dom, _Sender&& __sndr,
125                               const _Env&... __env) const
126         noexcept(__nothrow_callable<__transform_sender_1, _Domain, _Sender,
127                                     const _Env&...>)
128     {
129         using _Sender2 = __call_result_t<__transform_sender_1, _Domain, _Sender,
130                                          const _Env&...>;
131         // If the transformation doesn't change the sender's type, then do not
132         // apply the transform recursively.
133         if constexpr (__decay_same_as<_Sender, _Sender2>)
134         {
135             return __transform_sender_1()(__dom, static_cast<_Sender&&>(__sndr),
136                                           __env...);
137         }
138         else
139         {
140             // We transformed the sender and got back a different sender.
141             // Transform that one too.
142             return _Self()(__dom,
143                            __transform_sender_1()(
144                                __dom, static_cast<_Sender&&>(__sndr), __env...),
145                            __env...);
146         }
147     }
148 };
149 
150 struct __transform_dependent_sender
151 {
152     // If we are doing a lazy customization of a type whose domain is
153     // value-dependent (e.g., let_value), first transform the sender to
154     // determine the domain. Then continue transforming the sender with the
155     // requested domain.
156     template <class _Domain, sender_expr _Sender, class _Env>
157         requires same_as<__early_domain_of_t<_Sender>, dependent_domain>
158     /*constexpr*/ auto
operator ()stdexec::__domain::__transform_dependent_sender159         operator()(_Domain __dom, _Sender&& __sndr, const _Env& __env) const
160         noexcept(noexcept(__transform_sender()(
161             __dom,
162             dependent_domain().transform_sender(static_cast<_Sender&&>(__sndr),
163                                                 __env),
164             __env))) -> decltype(auto)
165     {
166         static_assert(__none_of<_Domain, dependent_domain>);
167         return __transform_sender()(__dom,
168                                     dependent_domain().transform_sender(
169                                         static_cast<_Sender&&>(__sndr), __env),
170                                     __env);
171     }
172 };
173 } // namespace __domain
174 
175 /////////////////////////////////////////////////////////////////////////////
176 // [execution.transform_sender]
177 inline constexpr struct transform_sender_t :
178     __domain::__transform_sender,
179     __domain::__transform_dependent_sender
180 {
181     using __domain::__transform_sender::operator();
182     using __domain::__transform_dependent_sender::operator();
183 } transform_sender{};
184 
185 template <class _Domain, class _Sender, class... _Env>
186 using transform_sender_result_t =
187     __call_result_t<transform_sender_t, _Domain, _Sender, _Env...>;
188 
189 inline constexpr __domain::__transform_env transform_env{};
190 
191 struct _CHILD_SENDERS_WITH_DIFFERENT_DOMAINS_
192 {};
193 
194 template <class _Sender, class _Env>
195 constexpr auto
__is_nothrow_transform_sender()196     dependent_domain::__is_nothrow_transform_sender() noexcept -> bool
197 {
198     using _Env2 = __call_result_t<__domain::__transform_env, dependent_domain&,
199                                   _Sender, _Env>;
200     return __v<decltype(__sexpr_apply(
201         __declval<_Sender>(), []<class _Tag, class _Data, class... _Childs>(
202                                   _Tag, _Data&&, _Childs&&...) {
203             constexpr bool __first_transform_is_nothrow =
204                 noexcept(__make_sexpr<_Tag>(
205                     __declval<_Data>(),
206                     __domain::__transform_sender()(
207                         __declval<dependent_domain&>(), __declval<_Childs>(),
208                         __declval<const _Env2&>())...));
209             using _Sender2 = decltype(__make_sexpr<_Tag>(
210                 __declval<_Data>(),
211                 __domain::__transform_sender()(__declval<dependent_domain&>(),
212                                                __declval<_Childs>(),
213                                                __declval<const _Env2&>())...));
214             using _Domain2 = decltype(__sexpr_apply(
215                 __declval<_Sender2&>(), __domain::__common_domain_fn()));
216             constexpr bool __second_transform_is_nothrow =
217                 noexcept(__domain::__transform_sender()(
218                     __declval<_Domain2&>(), __declval<_Sender2>(),
219                     __declval<const _Env&>()));
220             return __mbool < __first_transform_is_nothrow &&
221                    __second_transform_is_nothrow > ();
222         }))>;
223 }
224 
225 template <sender_expr _Sender, class _Env>
226     requires same_as<__early_domain_of_t<_Sender>, dependent_domain>
transform_sender(_Sender && __sndr,const _Env & __env) const227 auto dependent_domain::transform_sender(_Sender&& __sndr,
228                                         const _Env& __env) const
229     noexcept(__is_nothrow_transform_sender<_Sender, _Env>()) -> decltype(auto)
230 {
231     // apply any algorithm-specific transformation to the environment
232     const auto& __env2 =
233         transform_env(*this, static_cast<_Sender&&>(__sndr), __env);
234 
235     // recursively transform the sender to determine the domain
236     return __sexpr_apply(
237         static_cast<_Sender&&>(__sndr),
238         [&]<class _Tag, class _Data, class... _Childs>(_Tag, _Data&& __data,
239                                                        _Childs&&... __childs) {
240             // TODO: propagate meta-exceptions here:
241             auto __sndr2 = __make_sexpr<_Tag>(
242                 static_cast<_Data&&>(__data),
243                 __domain::__transform_sender()(
244                     *this, static_cast<_Childs&&>(__childs), __env2)...);
245             using _Sender2 = decltype(__sndr2);
246 
247             auto __domain2 =
248                 __sexpr_apply(__sndr2, __domain::__common_domain_fn());
249             using _Domain2 = decltype(__domain2);
250 
251             if constexpr (same_as<_Domain2, __none_such>)
252             {
253                 return __mexception<_CHILD_SENDERS_WITH_DIFFERENT_DOMAINS_,
254                                     _WITH_SENDER_<_Sender2>>();
255             }
256             else
257             {
258                 return __domain::__transform_sender()(
259                     __domain2, std::move(__sndr2), __env);
260             }
261         });
262 }
263 
264 /////////////////////////////////////////////////////////////////////////////
265 template <class _Tag, class _Domain, class _Sender, class... _Args>
266 concept __has_implementation_for =
267     __domain::__has_apply_sender<_Domain, _Tag, _Sender, _Args...> ||
268     __domain::__has_apply_sender<default_domain, _Tag, _Sender, _Args...>;
269 
270 /////////////////////////////////////////////////////////////////////////////
271 // [execution.apply_sender]
272 inline constexpr struct apply_sender_t
273 {
274     template <class _Domain, class _Tag, class _Sender, class... _Args>
275         requires __has_implementation_for<_Tag, _Domain, _Sender, _Args...>
276     STDEXEC_ATTRIBUTE((always_inline))
277     /*constexpr*/
operator ()stdexec::apply_sender_t278     decltype(auto) operator()(_Domain __dom, _Tag, _Sender&& __sndr,
279                               _Args&&... __args) const
280     {
281         if constexpr (__domain::__has_apply_sender<_Domain, _Tag, _Sender,
282                                                    _Args...>)
283         {
284             return __dom.apply_sender(_Tag(), static_cast<_Sender&&>(__sndr),
285                                       static_cast<_Args&&>(__args)...);
286         }
287         else
288         {
289             return default_domain().apply_sender(
290                 _Tag(), static_cast<_Sender&&>(__sndr),
291                 static_cast<_Args&&>(__args)...);
292         }
293     }
294 } apply_sender{};
295 
296 template <class _Domain, class _Tag, class _Sender, class... _Args>
297 using apply_sender_result_t =
298     __call_result_t<apply_sender_t, _Domain, _Tag, _Sender, _Args...>;
299 
300 /////////////////////////////////////////////////////////////////////////////
301 template <class _Sender, class _Scheduler, class _Tag = set_value_t>
302 concept __completes_on = __decays_to<
303     __call_result_t<get_completion_scheduler_t<_Tag>, env_of_t<_Sender>>,
304     _Scheduler>;
305 
306 /////////////////////////////////////////////////////////////////////////////
307 template <class _Sender, class _Scheduler, class _Env>
308 concept __starts_on =
309     __decays_to<__call_result_t<get_scheduler_t, _Env>, _Scheduler>;
310 } // namespace stdexec
311 
312 STDEXEC_PRAGMA_POP()
313