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