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