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