1 /*
2  * Copyright (c) 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 #if __cpp_concepts < 201907L
19 #error This library requires support for C++20 concepts
20 #endif
21 
22 #include "__config.hpp"
23 #include "__type_traits.hpp"
24 
25 #include <version>
26 
27 // Perhaps the stdlib lacks support for concepts though:
28 #if __has_include(<concepts>) && __cpp_lib_concepts >= 202002
29 #define STDEXEC_HAS_STD_CONCEPTS_HEADER() 1
30 #else
31 #define STDEXEC_HAS_STD_CONCEPTS_HEADER() 0
32 #endif
33 
34 #if STDEXEC_HAS_STD_CONCEPTS_HEADER()
35 #include <concepts>
36 #else
37 #include <type_traits>
38 #endif
39 
40 namespace stdexec
41 {
42 //////////////////////////////////////////////////////////////////////////////////////////////////
43 template <class _Fun, class... _As>
44 concept __callable =                                             //
45     requires(_Fun&& __fun, _As&&... __as) {                      //
46         static_cast<_Fun&&>(__fun)(static_cast<_As&&>(__as)...); //
47     };
48 template <class _Fun, class... _As>
49 concept __nothrow_callable =    //
50     __callable<_Fun, _As...> && //
51     requires(_Fun&& __fun, _As&&... __as) {
52         { static_cast<_Fun&&>(__fun)(static_cast<_As&&>(__as)...) } noexcept;
53     };
54 
55 //////////////////////////////////////////////////////////////////////////////////////////////////
56 template <class...>
57 struct __types;
58 
59 template <class... _Ts>
60 concept __typename = requires { typename __types<_Ts...>; };
61 
62 //////////////////////////////////////////////////////////////////////////////////////////////////
63 template <class _Ap, class _Bp>
64 concept __same_as = STDEXEC_IS_SAME(_Ap, _Bp);
65 
66 // Handy concepts
67 template <class _Ty, class _Up>
68 concept __decays_to = __same_as<__decay_t<_Ty>, _Up>;
69 
70 template <class _Ty, class _Up>
71 concept __not_decays_to = !__decays_to<_Ty, _Up>;
72 
73 template <bool _TrueOrFalse>
74 concept __satisfies = _TrueOrFalse;
75 
76 template <class...>
77 concept __true = true;
78 
79 template <class _Cp>
80 concept __class = __true<int _Cp::*> && (!__same_as<const _Cp, _Cp>);
81 
82 template <class _Ty, class... _As>
83 concept __one_of = (__same_as<_Ty, _As> || ...);
84 
85 template <class _Ty, class... _Us>
86 concept __all_of = (__same_as<_Ty, _Us> && ...);
87 
88 template <class _Ty, class... _Us>
89 concept __none_of = ((!__same_as<_Ty, _Us>) && ...);
90 
91 template <class, template <class...> class>
92 constexpr bool __is_instance_of_ = false;
93 template <class... _As, template <class...> class _Ty>
94 constexpr bool __is_instance_of_<_Ty<_As...>, _Ty> = true;
95 
96 template <class _Ay, template <class...> class _Ty>
97 concept __is_instance_of = __is_instance_of_<_Ay, _Ty>;
98 
99 template <class _Ay, template <class...> class _Ty>
100 concept __is_not_instance_of = !__is_instance_of<_Ay, _Ty>;
101 } // namespace stdexec
102 
103 namespace stdexec::__std_concepts
104 {
105 // Make sure we're using a same_as concept that doesn't instantiate std::is_same
106 template <class _Ap, class _Bp>
107 concept same_as = __same_as<_Ap, _Bp> && __same_as<_Bp, _Ap>;
108 
109 #if STDEXEC_HAS_STD_CONCEPTS_HEADER()
110 
111 using std::convertible_to;
112 using std::derived_from;
113 using std::equality_comparable;
114 using std::integral;
115 
116 #else
117 
118 template <class T>
119 concept integral = std::is_integral_v<T>;
120 
121 template <class _Ap, class _Bp>
122 concept derived_from =              //
123     STDEXEC_IS_BASE_OF(_Bp, _Ap) && //
124     STDEXEC_IS_CONVERTIBLE_TO(const volatile _Ap*, const volatile _Bp*);
125 
126 template <class _From, class _To>
127 concept convertible_to =                     //
128     STDEXEC_IS_CONVERTIBLE_TO(_From, _To) && //
129     requires(_From (&__fun)()) { static_cast<_To>(__fun()); };
130 
131 template <class _Ty>
132 concept equality_comparable = //
133     requires(__cref_t<_Ty> __t) {
134         { __t == __t } -> convertible_to<bool>;
135         { __t != __t } -> convertible_to<bool>;
136     };
137 #endif
138 } // namespace stdexec::__std_concepts
139 
140 namespace stdexec
141 {
142 using namespace __std_concepts;
143 
144 // Avoid using libstdc++'s object concepts because they instantiate a
145 // lot of templates.
146 #if STDEXEC_HAS_BUILTIN(__is_nothrow_destructible) || STDEXEC_MSVC()
147 template <class _Ty>
148 concept destructible = __is_nothrow_destructible(_Ty);
149 #else
150 template <class _Ty>
151 inline constexpr bool __destructible_ = //
152     requires(_Ty && (&__fn)() noexcept) {
153         { __fn().~_Ty() } noexcept;
154     };
155 template <class _Ty>
156 inline constexpr bool __destructible_<_Ty&> = true;
157 template <class _Ty>
158 inline constexpr bool __destructible_<_Ty&&> = true;
159 template <class _Ty, std::size_t _Np>
160 inline constexpr bool __destructible_<_Ty[_Np]> = __destructible_<_Ty>;
161 
162 template <class T>
163 concept destructible = __destructible_<T>;
164 #endif
165 
166 template <class _Ty, class... _As>
167 concept constructible_from = //
168     destructible<_Ty> &&     //
169     STDEXEC_IS_CONSTRUCTIBLE(_Ty, _As...);
170 
171 template <class _Ty>
172 concept default_initializable = //
173     constructible_from<_Ty> &&  //
174     requires { _Ty{}; } &&      //
175     requires { ::new _Ty; };
176 
177 template <class _Ty>
178 concept move_constructible = //
179     constructible_from<_Ty, _Ty>;
180 
181 template <class _Ty>
182 concept copy_constructible = //
183     move_constructible<_Ty>  //
184     && constructible_from<_Ty, const _Ty&>;
185 
186 template <class _LHS, class _RHS>
187 concept assignable_from =   //
188     same_as<_LHS, _LHS&> && //
189     // std::common_reference_with<
190     //   const std::remove_reference_t<_LHS>&,
191     //   const std::remove_reference_t<_RHS>&> &&
192     requires(_LHS __lhs, _RHS&& __rhs) {
193         { __lhs = static_cast<_RHS&&>(__rhs) } -> same_as<_LHS>;
194     };
195 
196 namespace __swap
197 {
198 using std::swap;
199 
200 template <class _Ty, class _Uy>
201 concept swappable_with =             //
202     requires(_Ty&& __t, _Uy&& __u) { //
203         swap(static_cast<_Ty&&>(__t), static_cast<_Uy&&>(__u));
204     };
205 
206 inline constexpr const auto __fn =                               //
207     []<class _Ty, swappable_with<_Ty> _Uy>(_Ty&& __t, _Uy&& __u) //
208     noexcept(noexcept(swap(static_cast<_Ty&&>(__t), static_cast<_Uy&&>(__u)))) {
209         swap(static_cast<_Ty&&>(__t), static_cast<_Uy&&>(__u));
210     };
211 } // namespace __swap
212 
213 using __swap::swappable_with;
214 inline constexpr const auto& swap = __swap::__fn;
215 
216 template <class _Ty>
217 concept swappable = //
218     requires(_Ty& a, _Ty& b) { swap(a, b); };
219 
220 template <class _Ty>
221 concept movable =                 //
222     std::is_object_v<_Ty> &&      //
223     move_constructible<_Ty> &&    //
224     assignable_from<_Ty&, _Ty> && //
225     swappable<_Ty>;
226 
227 template <class _Ty>
228 concept copyable =                       //
229     copy_constructible<_Ty> &&           //
230     movable<_Ty> &&                      //
231     assignable_from<_Ty&, _Ty&> &&       //
232     assignable_from<_Ty&, const _Ty&> && //
233     assignable_from<_Ty&, const _Ty>;
234 
235 template <class _Ty>
236 concept semiregular = //
237     copyable<_Ty> &&  //
238     default_initializable<_Ty>;
239 
240 template <class _Ty>
241 concept regular =       //
242     semiregular<_Ty> && //
243     equality_comparable<_Ty>;
244 
245 // Not exactly right, but close.
246 template <class _Ty>
247 concept __boolean_testable_ = convertible_to<_Ty, bool>;
248 
249 template <class T, class U>
250 concept __partially_ordered_with = //
251     requires(__cref_t<T> t, __cref_t<U> u) {
252         { t < u } -> __boolean_testable_;
253         { t > u } -> __boolean_testable_;
254         { t <= u } -> __boolean_testable_;
255         { t >= u } -> __boolean_testable_;
256         { u < t } -> __boolean_testable_;
257         { u > t } -> __boolean_testable_;
258         { u <= t } -> __boolean_testable_;
259         { u >= t } -> __boolean_testable_;
260     };
261 
262 template <class _Ty>
263 concept totally_ordered =       //
264     equality_comparable<_Ty> && //
265     __partially_ordered_with<_Ty, _Ty>;
266 
267 template <class _Ty>
268 concept __movable_value =                 //
269     move_constructible<__decay_t<_Ty>> && //
270     constructible_from<__decay_t<_Ty>, _Ty>;
271 
272 template <class _Ty>
273 concept __nothrow_movable_value = //
274     __movable_value<_Ty> &&       //
275     requires(_Ty&& __t) {
276         { __decay_t<_Ty>{__decay_t<_Ty>{static_cast<_Ty&&>(__t)}} } noexcept;
277     };
278 
279 template <class _Ty, class... _As>
280 concept __nothrow_constructible_from =
281     constructible_from<_Ty, _As...> &&
282     STDEXEC_IS_NOTHROW_CONSTRUCTIBLE(_Ty, _As...);
283 
284 template <class _Ty>
285 concept __nothrow_move_constructible = __nothrow_constructible_from<_Ty, _Ty>;
286 
287 template <class _Ty>
288 concept __nothrow_copy_constructible =
289     __nothrow_constructible_from<_Ty, const _Ty&>;
290 
291 template <class... _Ts>
292 concept __decay_copyable = (constructible_from<__decay_t<_Ts>, _Ts> && ...);
293 
294 template <class... _Ts>
295 concept __nothrow_decay_copyable =
296     (__nothrow_constructible_from<__decay_t<_Ts>, _Ts> && ...);
297 
298 template <class _Ty, class _Up>
299 concept __decays_to_derived_from = derived_from<__decay_t<_Ty>, _Up>;
300 } // namespace stdexec
301