1 /*
2  * Copyright (c) 2021-2023 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 "../functional.hpp"
19 #include "../stop_token.hpp"
20 #include "__concepts.hpp"
21 #include "__config.hpp"
22 #include "__execution_fwd.hpp"
23 #include "__meta.hpp"
24 
25 #include <concepts>
26 #include <exception>
27 #include <type_traits>
28 
29 STDEXEC_PRAGMA_PUSH()
30 STDEXEC_PRAGMA_IGNORE_EDG(probable_guiding_friend)
31 STDEXEC_PRAGMA_IGNORE_EDG(type_qualifiers_ignored_on_reference)
32 
33 namespace stdexec
34 {
35 // [exec.queries.queryable]
36 template <class T>
37 concept queryable = destructible<T>;
38 
39 template <class Tag>
40 struct __query
41 {
42     template <class Sig>
43     static inline constexpr Tag (*signature)(Sig) = nullptr;
44 };
45 
46 //////////////////////////////////////////////////////////////////////////////////////////////////
47 // [exec.queries]
48 namespace __queries
49 {
50 struct forwarding_query_t
51 {
52     template <class _Query>
53     constexpr bool operator()(_Query __query) const noexcept
54     {
55         if constexpr (tag_invocable<forwarding_query_t, _Query>)
56         {
57             return tag_invoke(*this, (_Query&&)__query);
58         }
59         else if constexpr (std::derived_from<_Query, forwarding_query_t>)
60         {
61             return true;
62         }
63         else
64         {
65             return false;
66         }
67     }
68 };
69 
70 struct query_or_t
71 {
72     template <class _Query, class _Queryable, class _Default>
73     constexpr auto operator()(_Query, _Queryable&&, _Default&& __default) const
74         noexcept(__nothrow_constructible_from<_Default, _Default&&>) -> _Default
75     {
76         return (_Default&&)__default;
77     }
78 
79     template <class _Query, class _Queryable, class _Default>
80         requires __callable<_Query, _Queryable>
81     constexpr auto operator()(_Query __query, _Queryable&& __queryable,
82                               _Default&&) const
83         noexcept(__nothrow_callable<_Query, _Queryable>)
84             -> __call_result_t<_Query, _Queryable>
85     {
86         return ((_Query&&)__query)((_Queryable&&)__queryable);
87     }
88 };
89 
90 struct execute_may_block_caller_t : __query<execute_may_block_caller_t>
91 {
92     template <class _Tp>
93         requires tag_invocable<execute_may_block_caller_t, __cref_t<_Tp>>
94     constexpr bool operator()(_Tp&& __t) const noexcept
95     {
96         static_assert(
97             same_as<bool, tag_invoke_result_t<execute_may_block_caller_t,
98                                               __cref_t<_Tp>>>);
99         static_assert(
100             nothrow_tag_invocable<execute_may_block_caller_t, __cref_t<_Tp>>);
101         return tag_invoke(execute_may_block_caller_t{}, std::as_const(__t));
102     }
103 
104     constexpr bool operator()(auto&&) const noexcept
105     {
106         return true;
107     }
108 };
109 
110 struct get_forward_progress_guarantee_t :
111     __query<get_forward_progress_guarantee_t>
112 {
113     template <class _Tp>
114         requires tag_invocable<get_forward_progress_guarantee_t, __cref_t<_Tp>>
115     constexpr auto operator()(_Tp&& __t) const noexcept(
116         nothrow_tag_invocable<get_forward_progress_guarantee_t, __cref_t<_Tp>>)
117         -> tag_invoke_result_t<get_forward_progress_guarantee_t, __cref_t<_Tp>>
118     {
119         return tag_invoke(get_forward_progress_guarantee_t{},
120                           std::as_const(__t));
121     }
122 
123     constexpr stdexec::forward_progress_guarantee
124         operator()(auto&&) const noexcept
125     {
126         return stdexec::forward_progress_guarantee::weakly_parallel;
127     }
128 };
129 
130 struct __has_algorithm_customizations_t :
131     __query<__has_algorithm_customizations_t>
132 {
133     template <class _Tp>
134     using __result_t =
135         tag_invoke_result_t<__has_algorithm_customizations_t, __cref_t<_Tp>>;
136 
137     template <class _Tp>
138         requires tag_invocable<__has_algorithm_customizations_t, __cref_t<_Tp>>
139     constexpr __result_t<_Tp> operator()(_Tp&&) const
140         noexcept(noexcept(__result_t<_Tp>{}))
141     {
142         using _Boolean = tag_invoke_result_t<__has_algorithm_customizations_t,
143                                              __cref_t<_Tp>>;
144         static_assert(_Boolean{}
145                           ? true
146                           : true); // must be contextually convertible to bool
147         return _Boolean{};
148     }
149 
150     constexpr std::false_type operator()(auto&&) const noexcept
151     {
152         return {};
153     }
154 };
155 
156 // TODO: implement allocator concept
157 template <class _T0>
158 concept __allocator_c = true;
159 
160 struct get_scheduler_t : __query<get_scheduler_t>
161 {
162     friend constexpr bool tag_invoke(forwarding_query_t,
163                                      const get_scheduler_t&) noexcept
164     {
165         return true;
166     }
167 
168     template <class _Env>
169         requires tag_invocable<get_scheduler_t, const _Env&>
170     auto operator()(const _Env& __env) const noexcept
171         -> tag_invoke_result_t<get_scheduler_t, const _Env&>;
172 
173     auto operator()() const noexcept;
174 };
175 
176 struct get_delegatee_scheduler_t : __query<get_delegatee_scheduler_t>
177 {
178     friend constexpr bool tag_invoke(forwarding_query_t,
179                                      const get_delegatee_scheduler_t&) noexcept
180     {
181         return true;
182     }
183 
184     template <class _Env>
185         requires tag_invocable<get_delegatee_scheduler_t, const _Env&>
186     auto operator()(const _Env& __t) const noexcept
187         -> tag_invoke_result_t<get_delegatee_scheduler_t, const _Env&>;
188 
189     auto operator()() const noexcept;
190 };
191 
192 struct get_allocator_t : __query<get_allocator_t>
193 {
194     friend constexpr bool tag_invoke(forwarding_query_t,
195                                      const get_allocator_t&) noexcept
196     {
197         return true;
198     }
199 
200     template <class _Env>
201         requires tag_invocable<get_allocator_t, const _Env&>
202     auto operator()(const _Env& __env) const noexcept
203         -> tag_invoke_result_t<get_allocator_t, const _Env&>
204     {
205         static_assert(nothrow_tag_invocable<get_allocator_t, const _Env&>);
206         static_assert(
207             __allocator_c<tag_invoke_result_t<get_allocator_t, const _Env&>>);
208         return tag_invoke(get_allocator_t{}, __env);
209     }
210 
211     auto operator()() const noexcept;
212 };
213 
214 struct get_stop_token_t : __query<get_stop_token_t>
215 {
216     friend constexpr bool tag_invoke(forwarding_query_t,
217                                      const get_stop_token_t&) noexcept
218     {
219         return true;
220     }
221 
222     template <class _Env>
223     never_stop_token operator()(const _Env&) const noexcept
224     {
225         return {};
226     }
227 
228     template <class _Env>
229         requires tag_invocable<get_stop_token_t, const _Env&>
230     auto operator()(const _Env& __env) const noexcept
231         -> tag_invoke_result_t<get_stop_token_t, const _Env&>
232     {
233         static_assert(nothrow_tag_invocable<get_stop_token_t, const _Env&>);
234         static_assert(stoppable_token<
235                       tag_invoke_result_t<get_stop_token_t, const _Env&>>);
236         return tag_invoke(get_stop_token_t{}, __env);
237     }
238 
239     auto operator()() const noexcept;
240 };
241 
242 template <class _Queryable, class _CPO>
243 concept __has_completion_scheduler_for =
244     queryable<_Queryable> && //
245     tag_invocable<get_completion_scheduler_t<_CPO>, const _Queryable&>;
246 
247 template <__completion_tag _CPO>
248 struct get_completion_scheduler_t : __query<get_completion_scheduler_t<_CPO>>
249 {
250     friend constexpr bool
251         tag_invoke(forwarding_query_t,
252                    const get_completion_scheduler_t<_CPO>&) noexcept
253     {
254         return true;
255     }
256 
257     template <__has_completion_scheduler_for<_CPO> _Queryable>
258     auto operator()(const _Queryable& __queryable) const noexcept
259         -> tag_invoke_result_t<get_completion_scheduler_t<_CPO>,
260                                const _Queryable&>;
261 };
262 
263 struct get_domain_t
264 {
265     template <class _Ty>
266         requires tag_invocable<get_domain_t, const _Ty&>
267     constexpr auto operator()(const _Ty& __ty) const noexcept
268         -> tag_invoke_result_t<get_domain_t, const _Ty&>
269     {
270         static_assert(nothrow_tag_invocable<get_domain_t, const _Ty&>,
271                       "Customizations of get_domain must be noexcept.");
272         static_assert(__class<tag_invoke_result_t<get_domain_t, const _Ty&>>,
273                       "Customizations of get_domain must return a class type.");
274         return tag_invoke(get_domain_t{}, __ty);
275     }
276 
277     friend constexpr bool tag_invoke(forwarding_query_t, get_domain_t) noexcept
278     {
279         return true;
280     }
281 };
282 } // namespace __queries
283 
284 using __queries::__has_algorithm_customizations_t;
285 using __queries::execute_may_block_caller_t;
286 using __queries::forwarding_query_t;
287 using __queries::get_allocator_t;
288 using __queries::get_completion_scheduler_t;
289 using __queries::get_delegatee_scheduler_t;
290 using __queries::get_domain_t;
291 using __queries::get_forward_progress_guarantee_t;
292 using __queries::get_scheduler_t;
293 using __queries::get_stop_token_t;
294 using __queries::query_or_t;
295 
296 inline constexpr forwarding_query_t forwarding_query{};
297 inline constexpr query_or_t query_or{}; // NOT TO SPEC
298 inline constexpr execute_may_block_caller_t execute_may_block_caller{};
299 inline constexpr __has_algorithm_customizations_t
300     __has_algorithm_customizations{};
301 inline constexpr get_forward_progress_guarantee_t
302     get_forward_progress_guarantee{};
303 inline constexpr get_scheduler_t get_scheduler{};
304 inline constexpr get_delegatee_scheduler_t get_delegatee_scheduler{};
305 inline constexpr get_allocator_t get_allocator{};
306 inline constexpr get_stop_token_t get_stop_token{};
307 #if !STDEXEC_GCC() || defined(__OPTIMIZE_SIZE__)
308 template <__completion_tag _CPO>
309 inline constexpr get_completion_scheduler_t<_CPO> get_completion_scheduler{};
310 #else
311 template <>
312 inline constexpr get_completion_scheduler_t<set_value_t>
313     get_completion_scheduler<set_value_t>{};
314 template <>
315 inline constexpr get_completion_scheduler_t<set_error_t>
316     get_completion_scheduler<set_error_t>{};
317 template <>
318 inline constexpr get_completion_scheduler_t<set_stopped_t>
319     get_completion_scheduler<set_stopped_t>{};
320 #endif
321 
322 template <class _Tag>
323 concept __forwarding_query = forwarding_query(_Tag{});
324 
325 inline constexpr get_domain_t get_domain{};
326 
327 template <class _Tag, class _Queryable, class _Default>
328 using __query_result_or_t =
329     __call_result_t<query_or_t, _Tag, _Queryable, _Default>;
330 
331 /////////////////////////////////////////////////////////////////////////////
332 namespace __env
333 {
334 // For getting an execution environment from a receiver,
335 // or the attributes from a sender.
336 struct get_env_t
337 {
338     template <class _EnvProvider>
339         requires tag_invocable<get_env_t, const _EnvProvider&>
340     STDEXEC_ATTRIBUTE((always_inline)) //
341         constexpr auto
342         operator()(const _EnvProvider& __env_provider) const noexcept
343         -> tag_invoke_result_t<get_env_t, const _EnvProvider&>
344     {
345         static_assert(
346             queryable<tag_invoke_result_t<get_env_t, const _EnvProvider&>>);
347         static_assert(nothrow_tag_invocable<get_env_t, const _EnvProvider&>);
348         return tag_invoke(*this, __env_provider);
349     }
350 
351     template <class _EnvProvider>
352     constexpr empty_env operator()(const _EnvProvider&) const noexcept
353     {
354         return {};
355     }
356 };
357 
358 // To be kept in sync with the promise type used in __connect_awaitable
359 template <class _Env>
360 struct __promise
361 {
362     template <class _Ty>
363     _Ty&& await_transform(_Ty&& __value) noexcept
364     {
365         return (_Ty&&)__value;
366     }
367 
368     template <class _Ty>
369         requires tag_invocable<as_awaitable_t, _Ty, __promise&>
370     auto await_transform(_Ty&& __value) //
371         noexcept(nothrow_tag_invocable<as_awaitable_t, _Ty, __promise&>)
372             -> tag_invoke_result_t<as_awaitable_t, _Ty, __promise&>
373     {
374         return tag_invoke(as_awaitable, (_Ty&&)__value, *this);
375     }
376 
377     template <same_as<get_env_t> _Tag>
378     friend auto tag_invoke(_Tag, const __promise&) noexcept -> const _Env&
379     {
380         std::terminate();
381     }
382 };
383 
384 template <class _Value, class _Tag, class... _Tags>
385 struct __with
386 {
387     using __t = __with;
388     using __id = __with;
389     STDEXEC_ATTRIBUTE((no_unique_address)) _Value __value_;
390 
391     __with() = default;
392 
393     constexpr explicit __with(_Value __value) noexcept(
394         __nothrow_decay_copyable<_Value>) :
395         __value_((_Value&&)__value)
396     {}
397 
398     constexpr explicit __with(_Value __value, _Tag, _Tags...) noexcept(
399         __nothrow_decay_copyable<_Value>) :
400         __value_((_Value&&)__value)
401     {}
402 
403     template <__one_of<_Tag, _Tags...> _Key>
404     friend auto tag_invoke(_Key, const __with& __self) //
405         noexcept(__nothrow_decay_copyable<_Value>) -> _Value
406     {
407         return __self.__value_;
408     }
409 };
410 
411 template <class _Value, class _Tag, class... _Tags>
412 __with(_Value, _Tag, _Tags...) -> __with<_Value, _Tag, _Tags...>;
413 
414 template <class _Env>
415 struct __fwd
416 {
417     static_assert(__nothrow_move_constructible<_Env>);
418     using __t = __fwd;
419     using __id = __fwd;
420     STDEXEC_ATTRIBUTE((no_unique_address)) _Env __env_;
421 
422     template <__forwarding_query _Tag>
423         requires tag_invocable<_Tag, const _Env&>
424     friend auto tag_invoke(_Tag, const __fwd& __self) //
425         noexcept(nothrow_tag_invocable<_Tag, const _Env&>)
426             -> tag_invoke_result_t<_Tag, const _Env&>
427     {
428         return _Tag()(__self.__env_);
429     }
430 };
431 
432 template <class _Env>
433 __fwd(_Env&&) -> __fwd<_Env>;
434 
435 template <class _Env>
436 struct __ref
437 {
438     using __t = __ref;
439     using __id = __ref;
440     const _Env& __env_;
441 
442     template <class _Tag>
443         requires tag_invocable<_Tag, const _Env&>
444     friend auto tag_invoke(_Tag, const __ref& __self) //
445         noexcept(nothrow_tag_invocable<_Tag, const _Env&>)
446             -> tag_invoke_result_t<_Tag, const _Env&>
447     {
448         return _Tag()(__self.__env_);
449     }
450 };
451 
452 template <class _Env>
453 __ref(_Env&) -> __ref<_Env>;
454 
455 struct __ref_fn
456 {
457     template <class _Env>
458     constexpr auto operator()(_Env&& __env) const
459     {
460         if constexpr (same_as<_Env, _Env&>)
461         {
462             return __ref{(_Env&&)__env};
463         }
464         else
465         {
466             return (_Env&&)__env;
467         }
468     }
469 };
470 
471 template <class _Env, class _Tag, class... _Tags>
472 struct __without_ : _Env
473 {
474     static_assert(__nothrow_move_constructible<_Env>);
475     using __t = __without_;
476     using __id = __without_;
477 
478     constexpr explicit __without_(_Env&& __env, _Tag, _Tags...) noexcept :
479         _Env((_Env&&)__env)
480     {}
481 
482     template <__one_of<_Tag, _Tags...> _Key, class _Self>
483         requires(std::is_base_of_v<__without_, __decay_t<_Self>>)
484     friend auto tag_invoke(_Key, _Self&&) noexcept = delete;
485 };
486 
487 struct __without_fn
488 {
489     template <class _Env, class _Tag, class... _Tags>
490     constexpr decltype(auto) operator()(_Env&& __env, _Tag,
491                                         _Tags...) const noexcept
492     {
493         if constexpr (tag_invocable<_Tag, _Env> ||
494                       (tag_invocable<_Tags, _Env> || ...))
495         {
496             return __without_{__ref_fn()((_Env&&)__env), _Tag(), _Tags()...};
497         }
498         else
499         {
500             return static_cast<_Env>((_Env&&)__env);
501         }
502     }
503 };
504 
505 inline constexpr __without_fn __without{};
506 
507 template <class _Env, class _Tag, class... _Tags>
508 using __without_t = __result_of<__without, _Env, _Tag, _Tags...>;
509 
510 template <class _Second, class _First>
511 struct __joined : _Second
512 {
513     static_assert(__nothrow_move_constructible<_First>);
514     static_assert(__nothrow_move_constructible<_Second>);
515     using __t = __joined;
516     using __id = __joined;
517 
518     STDEXEC_ATTRIBUTE((no_unique_address)) _First __env_;
519 
520     template <class _Tag>
521         requires tag_invocable<_Tag, const _First&>
522     friend auto tag_invoke(_Tag, const __joined& __self) //
523         noexcept(nothrow_tag_invocable<_Tag, const _First&>)
524             -> tag_invoke_result_t<_Tag, const _First&>
525     {
526         return _Tag()(__self.__env_);
527     }
528 };
529 
530 template <class _Second, class _First>
531 __joined(_Second&&, _First&&) -> __joined<_Second, _First>;
532 
533 template <__nothrow_move_constructible _Fun>
534 struct __from
535 {
536     using __t = __from;
537     using __id = __from;
538     STDEXEC_ATTRIBUTE((no_unique_address)) _Fun __fun_;
539 
540     template <class _Tag>
541         requires __callable<const _Fun&, _Tag>
542     friend auto tag_invoke(_Tag, const __from& __self) //
543         noexcept(__nothrow_callable<const _Fun&, _Tag>)
544             -> __call_result_t<const _Fun&, _Tag>
545     {
546         return __self.__fun_(_Tag());
547     }
548 };
549 
550 template <class _Fun>
551 __from(_Fun) -> __from<_Fun>;
552 
553 struct __fwd_fn
554 {
555     template <class Env>
556     auto operator()(Env&& env) const
557     {
558         return __fwd{(Env&&)env};
559     }
560 
561     empty_env operator()(empty_env) const
562     {
563         return {};
564     }
565 };
566 
567 struct __join_fn
568 {
569     empty_env operator()() const
570     {
571         return {};
572     }
573 
574     template <class _Env>
575     _Env operator()(_Env&& __env) const
576     {
577         return (_Env&&)__env;
578     }
579 
580     empty_env operator()(empty_env) const
581     {
582         return {};
583     }
584 
585     template <class _Env>
586     _Env operator()(_Env&& __env, empty_env) const
587     {
588         return (_Env&&)__env;
589     }
590 
591     empty_env operator()(empty_env, empty_env) const
592     {
593         return {};
594     }
595 
596     template <class... Rest>
597     decltype(auto) operator()(empty_env, Rest&&... rest) const
598     {
599         return __fwd_fn()(__join_fn()((Rest&&)rest...));
600     }
601 
602     template <class First, class... Rest>
603     decltype(auto) operator()(First&& first, Rest&&... rest) const
604     {
605         return __joined{__fwd_fn()(__join_fn()((Rest&&)rest...)),
606                         (First&&)first};
607     }
608 };
609 
610 inline constexpr __join_fn __join{};
611 
612 template <class... _Envs>
613 using __join_t = __result_of<__join, _Envs...>;
614 } // namespace __env
615 
616 using __env::empty_env;
617 using __env::get_env_t;
618 inline constexpr get_env_t get_env{};
619 
620 template <class _EnvProvider>
621 concept environment_provider = //
622     requires(_EnvProvider& __ep) {
623         {
624             get_env(std::as_const(__ep))
625         } -> queryable;
626     };
627 } // namespace stdexec
628 
629 STDEXEC_PRAGMA_POP()
630