xref: /aosp_15_r20/external/pigweed/pw_async2/public/pw_async2/internal/poll_internal.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2024 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15 
16 #include <type_traits>
17 #include <utility>
18 
19 #include "lib/stdcompat/type_traits.h"
20 
21 namespace pw::async2 {
22 
23 template <typename T>
24 class [[nodiscard]] Poll;
25 
26 struct [[nodiscard]] PendingType;
27 
28 namespace internal_poll {
29 
30 using ::cpp20::remove_cvref_t;
31 
32 // Detects whether `U` has conversion operator to `Poll<T>`, i.e. `operator
33 // Poll<T>()`.
34 template <typename T, typename U, typename = void>
35 struct HasConversionOperatorToPoll : std::false_type {};
36 
37 template <typename T, typename U>
38 void test(char (*)[sizeof(std::declval<U>().operator Poll<T>())]);
39 
40 template <typename T, typename U>
41 struct HasConversionOperatorToPoll<T, U, decltype(test<T, U>(0))>
42     : std::true_type {};
43 
44 // Detects whether `T` is constructible or convertible from `Poll<U>`.
45 template <typename T, typename U>
46 using IsConstructibleOrConvertibleFromPoll =
47     std::disjunction<std::is_constructible<T, Poll<U>&>,
48                      std::is_constructible<T, const Poll<U>&>,
49                      std::is_constructible<T, Poll<U>&&>,
50                      std::is_constructible<T, const Poll<U>&&>,
51                      std::is_convertible<Poll<U>&, T>,
52                      std::is_convertible<const Poll<U>&, T>,
53                      std::is_convertible<Poll<U>&&, T>,
54                      std::is_convertible<const Poll<U>&&, T>>;
55 
56 // `UQualified` here may be a type like `const U&` or `U&&`.
57 // That is, the qualified type of ``U``, not the type "unqualified" ;)
58 template <typename T, typename UQualified>
59 using EnableIfImplicitlyConvertible = std::enable_if_t<
60     std::conjunction<
61         std::negation<std::is_same<T, remove_cvref_t<UQualified>>>,
62         std::is_constructible<T, UQualified>,
63         std::is_convertible<UQualified, T>,
64         std::negation<internal_poll::IsConstructibleOrConvertibleFromPoll<
65             T,
66             remove_cvref_t<UQualified>>>>::value,
67     int>;
68 
69 // `UQualified` here may be a type like `const U&` or `U&&`.
70 // That is, the qualified type of ``U``, not the type "unqualified" ;)
71 template <typename T, typename UQualified>
72 using EnableIfExplicitlyConvertible = std::enable_if_t<
73     std::conjunction<
74         std::negation<std::is_same<T, remove_cvref_t<UQualified>>>,
75         std::is_constructible<T, UQualified>,
76         std::negation<std::is_convertible<UQualified, T>>,
77         std::negation<internal_poll::IsConstructibleOrConvertibleFromPoll<
78             T,
79             remove_cvref_t<UQualified>>>>::value,
80     int>;
81 
82 // Detects whether `T` is constructible or convertible or assignable from
83 // `Poll<U>`.
84 template <typename T, typename U>
85 using IsConstructibleOrConvertibleOrAssignableFromPoll =
86     std::disjunction<IsConstructibleOrConvertibleFromPoll<T, U>,
87                      std::is_assignable<T&, Poll<U>&>,
88                      std::is_assignable<T&, const Poll<U>&>,
89                      std::is_assignable<T&, Poll<U>&&>,
90                      std::is_assignable<T&, const Poll<U>&&>>;
91 
92 // Detects whether direct initializing `Poll<T>` from `U` is ambiguous, i.e.
93 // when `U` is `Poll<V>` and `T` is constructible or convertible from `V`.
94 template <typename T, typename U>
95 struct IsDirectInitializationAmbiguous
96     : public std::conditional_t<
97           std::is_same<std::remove_cv_t<std::remove_reference_t<U>>, U>::value,
98           std::false_type,
99           IsDirectInitializationAmbiguous<
100               T,
101               std::remove_cv_t<std::remove_reference_t<U>>>> {};
102 
103 template <typename T, typename V>
104 struct IsDirectInitializationAmbiguous<T, Poll<V>>
105     : public IsConstructibleOrConvertibleFromPoll<T, V> {};
106 
107 // Checks against the constraints of the direction initialization, i.e. when
108 // `Poll<T>::Poll(U&&)` should participate in overload resolution.
109 template <typename T, typename U>
110 using IsDirectInitializationValid = std::disjunction<
111     // Short circuits if T is basically U.
112     std::is_same<T, std::remove_cv_t<std::remove_reference_t<U>>>,
113     std::negation<std::disjunction<
114         std::is_same<Poll<T>, std::remove_cv_t<std::remove_reference_t<U>>>,
115         std::is_same<PendingType, std::remove_cv_t<std::remove_reference_t<U>>>,
116         std::is_same<std::in_place_t,
117                      std::remove_cv_t<std::remove_reference_t<U>>>,
118         IsDirectInitializationAmbiguous<T, U>>>>;
119 
120 template <typename T, typename U>
121 using EnableIfImplicitlyInitializable = std::enable_if_t<
122     std::conjunction<
123         internal_poll::IsDirectInitializationValid<T, U&&>,
124         std::is_constructible<T, U&&>,
125         std::is_convertible<U&&, T>,
126         std::disjunction<
127             std::is_same<std::remove_cv_t<std::remove_reference_t<U>>, T>,
128             std::conjunction<
129                 std::negation<std::is_convertible<U&&, PendingType>>,
130                 std::negation<
131                     internal_poll::HasConversionOperatorToPoll<T, U&&>>>>>::
132         value,
133     int>;
134 
135 template <typename T, typename U>
136 using EnableIfExplicitlyInitializable = std::enable_if_t<
137     std::conjunction<
138         internal_poll::IsDirectInitializationValid<T, U&&>,
139         std::disjunction<
140             std::is_same<std::remove_cv_t<std::remove_reference_t<U>>, T>,
141             std::conjunction<
142                 std::negation<std::is_constructible<PendingType, U&&>>,
143                 std::negation<
144                     internal_poll::HasConversionOperatorToPoll<T, U&&>>>>,
145         std::is_constructible<T, U&&>,
146         std::negation<std::is_convertible<U&&, T>>>::value,
147     int>;
148 
149 // This trait detects whether `Poll<T>::operator=(U&&)` is ambiguous, which
150 // is equivalent to whether all the following conditions are met:
151 // 1. `U` is `Poll<V>`.
152 // 2. `T` is constructible and assignable from `V`.
153 // 3. `T` is constructible and assignable from `U` (i.e. `Poll<V>`).
154 template <typename T, typename U>
155 struct IsForwardingAssignmentAmbiguous
156     : public std::conditional_t<
157           std::is_same<std::remove_cv_t<std::remove_reference_t<U>>, U>::value,
158           std::false_type,
159           IsForwardingAssignmentAmbiguous<
160               T,
161               std::remove_cv_t<std::remove_reference_t<U>>>> {};
162 
163 template <typename T, typename U>
164 struct IsForwardingAssignmentAmbiguous<T, Poll<U>>
165     : public IsConstructibleOrConvertibleOrAssignableFromPoll<T, U> {};
166 
167 // Checks against the constraints of the forwarding assignment, i.e. whether
168 // `Poll<T>::operator(U&&)` should participate in overload resolution.
169 template <typename T, typename U>
170 using IsForwardingAssignmentValid = std::disjunction<
171     // Short circuits if T is basically U.
172     std::is_same<T, std::remove_cv_t<std::remove_reference_t<U>>>,
173     std::negation<std::disjunction<
174         std::is_same<Poll<T>, std::remove_cv_t<std::remove_reference_t<U>>>,
175         std::is_same<PendingType, std::remove_cv_t<std::remove_reference_t<U>>>,
176         std::is_same<std::in_place_t,
177                      std::remove_cv_t<std::remove_reference_t<U>>>,
178         IsForwardingAssignmentAmbiguous<T, U>>>>;
179 
180 // This trait is for determining if a given type is a Poll.
181 template <typename T>
182 constexpr bool IsPoll = false;
183 template <typename T>
184 constexpr bool IsPoll<Poll<T>> = true;
185 
186 }  // namespace internal_poll
187 }  // namespace pw::async2
188