1 // Copyright 2020 The Abseil Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of 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, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 #ifndef ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_ 15 #define ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_ 16 17 #include <type_traits> 18 #include <utility> 19 20 #include "absl/meta/type_traits.h" 21 #include "absl/status/status.h" 22 #include "absl/utility/utility.h" 23 24 namespace absl { 25 ABSL_NAMESPACE_BEGIN 26 27 template <typename T> 28 class ABSL_MUST_USE_RESULT StatusOr; 29 30 namespace internal_statusor { 31 32 // Detects whether `U` has conversion operator to `StatusOr<T>`, i.e. `operator 33 // StatusOr<T>()`. 34 template <typename T, typename U, typename = void> 35 struct HasConversionOperatorToStatusOr : std::false_type {}; 36 37 template <typename T, typename U> 38 void test(char (*)[sizeof(std::declval<U>().operator absl::StatusOr<T>())]); 39 40 template <typename T, typename U> 41 struct HasConversionOperatorToStatusOr<T, U, decltype(test<T, U>(0))> 42 : std::true_type {}; 43 44 // Detects whether `T` is constructible or convertible from `StatusOr<U>`. 45 template <typename T, typename U> 46 using IsConstructibleOrConvertibleFromStatusOr = 47 absl::disjunction<std::is_constructible<T, StatusOr<U>&>, 48 std::is_constructible<T, const StatusOr<U>&>, 49 std::is_constructible<T, StatusOr<U>&&>, 50 std::is_constructible<T, const StatusOr<U>&&>, 51 std::is_convertible<StatusOr<U>&, T>, 52 std::is_convertible<const StatusOr<U>&, T>, 53 std::is_convertible<StatusOr<U>&&, T>, 54 std::is_convertible<const StatusOr<U>&&, T>>; 55 56 // Detects whether `T` is constructible or convertible or assignable from 57 // `StatusOr<U>`. 58 template <typename T, typename U> 59 using IsConstructibleOrConvertibleOrAssignableFromStatusOr = 60 absl::disjunction<IsConstructibleOrConvertibleFromStatusOr<T, U>, 61 std::is_assignable<T&, StatusOr<U>&>, 62 std::is_assignable<T&, const StatusOr<U>&>, 63 std::is_assignable<T&, StatusOr<U>&&>, 64 std::is_assignable<T&, const StatusOr<U>&&>>; 65 66 // Detects whether direct initializing `StatusOr<T>` from `U` is ambiguous, i.e. 67 // when `U` is `StatusOr<V>` and `T` is constructible or convertible from `V`. 68 template <typename T, typename U> 69 struct IsDirectInitializationAmbiguous 70 : public absl::conditional_t< 71 std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>, 72 U>::value, 73 std::false_type, 74 IsDirectInitializationAmbiguous< 75 T, absl::remove_cv_t<absl::remove_reference_t<U>>>> {}; 76 77 template <typename T, typename V> 78 struct IsDirectInitializationAmbiguous<T, absl::StatusOr<V>> 79 : public IsConstructibleOrConvertibleFromStatusOr<T, V> {}; 80 81 // Checks against the constraints of the direction initialization, i.e. when 82 // `StatusOr<T>::StatusOr(U&&)` should participate in overload resolution. 83 template <typename T, typename U> 84 using IsDirectInitializationValid = absl::disjunction< 85 // Short circuits if T is basically U. 86 std::is_same<T, absl::remove_cv_t<absl::remove_reference_t<U>>>, 87 absl::negation<absl::disjunction< 88 std::is_same<absl::StatusOr<T>, 89 absl::remove_cv_t<absl::remove_reference_t<U>>>, 90 std::is_same<absl::Status, 91 absl::remove_cv_t<absl::remove_reference_t<U>>>, 92 std::is_same<absl::in_place_t, 93 absl::remove_cv_t<absl::remove_reference_t<U>>>, 94 IsDirectInitializationAmbiguous<T, U>>>>; 95 96 // This trait detects whether `StatusOr<T>::operator=(U&&)` is ambiguous, which 97 // is equivalent to whether all the following conditions are met: 98 // 1. `U` is `StatusOr<V>`. 99 // 2. `T` is constructible and assignable from `V`. 100 // 3. `T` is constructible and assignable from `U` (i.e. `StatusOr<V>`). 101 // For example, the following code is considered ambiguous: 102 // (`T` is `bool`, `U` is `StatusOr<bool>`, `V` is `bool`) 103 // StatusOr<bool> s1 = true; // s1.ok() && s1.ValueOrDie() == true 104 // StatusOr<bool> s2 = false; // s2.ok() && s2.ValueOrDie() == false 105 // s1 = s2; // ambiguous, `s1 = s2.ValueOrDie()` or `s1 = bool(s2)`? 106 template <typename T, typename U> 107 struct IsForwardingAssignmentAmbiguous 108 : public absl::conditional_t< 109 std::is_same<absl::remove_cv_t<absl::remove_reference_t<U>>, 110 U>::value, 111 std::false_type, 112 IsForwardingAssignmentAmbiguous< 113 T, absl::remove_cv_t<absl::remove_reference_t<U>>>> {}; 114 115 template <typename T, typename U> 116 struct IsForwardingAssignmentAmbiguous<T, absl::StatusOr<U>> 117 : public IsConstructibleOrConvertibleOrAssignableFromStatusOr<T, U> {}; 118 119 // Checks against the constraints of the forwarding assignment, i.e. whether 120 // `StatusOr<T>::operator(U&&)` should participate in overload resolution. 121 template <typename T, typename U> 122 using IsForwardingAssignmentValid = absl::disjunction< 123 // Short circuits if T is basically U. 124 std::is_same<T, absl::remove_cv_t<absl::remove_reference_t<U>>>, 125 absl::negation<absl::disjunction< 126 std::is_same<absl::StatusOr<T>, 127 absl::remove_cv_t<absl::remove_reference_t<U>>>, 128 std::is_same<absl::Status, 129 absl::remove_cv_t<absl::remove_reference_t<U>>>, 130 std::is_same<absl::in_place_t, 131 absl::remove_cv_t<absl::remove_reference_t<U>>>, 132 IsForwardingAssignmentAmbiguous<T, U>>>>; 133 134 class Helper { 135 public: 136 // Move type-agnostic error handling to the .cc. 137 static void HandleInvalidStatusCtorArg(Status*); 138 static void Crash(const absl::Status& status); 139 }; 140 141 // Construct an instance of T in `p` through placement new, passing Args... to 142 // the constructor. 143 // This abstraction is here mostly for the gcc performance fix. 144 template <typename T, typename... Args> 145 void PlacementNew(void* p, Args&&... args) { 146 #if defined(__GNUC__) && !defined(__clang__) 147 // Teach gcc that 'p' cannot be null, fixing code size issues. 148 if (p == nullptr) __builtin_unreachable(); 149 #endif 150 new (p) T(std::forward<Args>(args)...); 151 } 152 153 // Helper base class to hold the data and all operations. 154 // We move all this to a base class to allow mixing with the appropriate 155 // TraitsBase specialization. 156 template <typename T> 157 class StatusOrData { 158 template <typename U> 159 friend class StatusOrData; 160 161 public: 162 StatusOrData() = delete; 163 164 StatusOrData(const StatusOrData& other) { 165 if (other.ok()) { 166 MakeValue(other.data_); 167 MakeStatus(); 168 } else { 169 MakeStatus(other.status_); 170 } 171 } 172 173 StatusOrData(StatusOrData&& other) noexcept { 174 if (other.ok()) { 175 MakeValue(std::move(other.data_)); 176 MakeStatus(); 177 } else { 178 MakeStatus(std::move(other.status_)); 179 } 180 } 181 182 template <typename U> 183 explicit StatusOrData(const StatusOrData<U>& other) { 184 if (other.ok()) { 185 MakeValue(other.data_); 186 MakeStatus(); 187 } else { 188 MakeStatus(other.status_); 189 } 190 } 191 192 template <typename U> 193 explicit StatusOrData(StatusOrData<U>&& other) { 194 if (other.ok()) { 195 MakeValue(std::move(other.data_)); 196 MakeStatus(); 197 } else { 198 MakeStatus(std::move(other.status_)); 199 } 200 } 201 202 template <typename... Args> 203 explicit StatusOrData(absl::in_place_t, Args&&... args) 204 : data_(std::forward<Args>(args)...) { 205 MakeStatus(); 206 } 207 208 explicit StatusOrData(const T& value) : data_(value) { 209 MakeStatus(); 210 } 211 explicit StatusOrData(T&& value) : data_(std::move(value)) { 212 MakeStatus(); 213 } 214 215 template <typename U, 216 absl::enable_if_t<std::is_constructible<absl::Status, U&&>::value, 217 int> = 0> 218 explicit StatusOrData(U&& v) : status_(v) { 219 EnsureNotOk(); 220 } 221 222 StatusOrData& operator=(const StatusOrData& other) { 223 if (this == &other) return *this; 224 if (other.ok()) 225 Assign(other.data_); 226 else 227 AssignStatus(other.status_); 228 return *this; 229 } 230 231 StatusOrData& operator=(StatusOrData&& other) { 232 if (this == &other) return *this; 233 if (other.ok()) 234 Assign(std::move(other.data_)); 235 else 236 AssignStatus(std::move(other.status_)); 237 return *this; 238 } 239 240 ~StatusOrData() { 241 if (ok()) { 242 status_.~Status(); 243 data_.~T(); 244 } else { 245 status_.~Status(); 246 } 247 } 248 249 template <typename U> 250 void Assign(U&& value) { 251 if (ok()) { 252 data_ = std::forward<U>(value); 253 } else { 254 MakeValue(std::forward<U>(value)); 255 status_ = OkStatus(); 256 } 257 } 258 259 template <typename U> 260 void AssignStatus(U&& v) { 261 Clear(); 262 status_ = static_cast<absl::Status>(std::forward<U>(v)); 263 EnsureNotOk(); 264 } 265 266 bool ok() const { return status_.ok(); } 267 268 protected: 269 // status_ will always be active after the constructor. 270 // We make it a union to be able to initialize exactly how we need without 271 // waste. 272 // Eg. in the copy constructor we use the default constructor of Status in 273 // the ok() path to avoid an extra Ref call. 274 union { 275 Status status_; 276 }; 277 278 // data_ is active iff status_.ok()==true 279 struct Dummy {}; 280 union { 281 // When T is const, we need some non-const object we can cast to void* for 282 // the placement new. dummy_ is that object. 283 Dummy dummy_; 284 T data_; 285 }; 286 287 void Clear() { 288 if (ok()) data_.~T(); 289 } 290 291 void EnsureOk() const { 292 if (ABSL_PREDICT_FALSE(!ok())) Helper::Crash(status_); 293 } 294 295 void EnsureNotOk() { 296 if (ABSL_PREDICT_FALSE(ok())) Helper::HandleInvalidStatusCtorArg(&status_); 297 } 298 299 // Construct the value (ie. data_) through placement new with the passed 300 // argument. 301 template <typename... Arg> 302 void MakeValue(Arg&&... arg) { 303 internal_statusor::PlacementNew<T>(&dummy_, std::forward<Arg>(arg)...); 304 } 305 306 // Construct the status (ie. status_) through placement new with the passed 307 // argument. 308 template <typename... Args> 309 void MakeStatus(Args&&... args) { 310 internal_statusor::PlacementNew<Status>(&status_, 311 std::forward<Args>(args)...); 312 } 313 }; 314 315 // Helper base classes to allow implicitly deleted constructors and assignment 316 // operators in `StatusOr`. For example, `CopyCtorBase` will explicitly delete 317 // the copy constructor when T is not copy constructible and `StatusOr` will 318 // inherit that behavior implicitly. 319 template <typename T, bool = std::is_copy_constructible<T>::value> 320 struct CopyCtorBase { 321 CopyCtorBase() = default; 322 CopyCtorBase(const CopyCtorBase&) = default; 323 CopyCtorBase(CopyCtorBase&&) = default; 324 CopyCtorBase& operator=(const CopyCtorBase&) = default; 325 CopyCtorBase& operator=(CopyCtorBase&&) = default; 326 }; 327 328 template <typename T> 329 struct CopyCtorBase<T, false> { 330 CopyCtorBase() = default; 331 CopyCtorBase(const CopyCtorBase&) = delete; 332 CopyCtorBase(CopyCtorBase&&) = default; 333 CopyCtorBase& operator=(const CopyCtorBase&) = default; 334 CopyCtorBase& operator=(CopyCtorBase&&) = default; 335 }; 336 337 template <typename T, bool = std::is_move_constructible<T>::value> 338 struct MoveCtorBase { 339 MoveCtorBase() = default; 340 MoveCtorBase(const MoveCtorBase&) = default; 341 MoveCtorBase(MoveCtorBase&&) = default; 342 MoveCtorBase& operator=(const MoveCtorBase&) = default; 343 MoveCtorBase& operator=(MoveCtorBase&&) = default; 344 }; 345 346 template <typename T> 347 struct MoveCtorBase<T, false> { 348 MoveCtorBase() = default; 349 MoveCtorBase(const MoveCtorBase&) = default; 350 MoveCtorBase(MoveCtorBase&&) = delete; 351 MoveCtorBase& operator=(const MoveCtorBase&) = default; 352 MoveCtorBase& operator=(MoveCtorBase&&) = default; 353 }; 354 355 template <typename T, bool = std::is_copy_constructible<T>::value&& 356 std::is_copy_assignable<T>::value> 357 struct CopyAssignBase { 358 CopyAssignBase() = default; 359 CopyAssignBase(const CopyAssignBase&) = default; 360 CopyAssignBase(CopyAssignBase&&) = default; 361 CopyAssignBase& operator=(const CopyAssignBase&) = default; 362 CopyAssignBase& operator=(CopyAssignBase&&) = default; 363 }; 364 365 template <typename T> 366 struct CopyAssignBase<T, false> { 367 CopyAssignBase() = default; 368 CopyAssignBase(const CopyAssignBase&) = default; 369 CopyAssignBase(CopyAssignBase&&) = default; 370 CopyAssignBase& operator=(const CopyAssignBase&) = delete; 371 CopyAssignBase& operator=(CopyAssignBase&&) = default; 372 }; 373 374 template <typename T, bool = std::is_move_constructible<T>::value&& 375 std::is_move_assignable<T>::value> 376 struct MoveAssignBase { 377 MoveAssignBase() = default; 378 MoveAssignBase(const MoveAssignBase&) = default; 379 MoveAssignBase(MoveAssignBase&&) = default; 380 MoveAssignBase& operator=(const MoveAssignBase&) = default; 381 MoveAssignBase& operator=(MoveAssignBase&&) = default; 382 }; 383 384 template <typename T> 385 struct MoveAssignBase<T, false> { 386 MoveAssignBase() = default; 387 MoveAssignBase(const MoveAssignBase&) = default; 388 MoveAssignBase(MoveAssignBase&&) = default; 389 MoveAssignBase& operator=(const MoveAssignBase&) = default; 390 MoveAssignBase& operator=(MoveAssignBase&&) = delete; 391 }; 392 393 ABSL_ATTRIBUTE_NORETURN void ThrowBadStatusOrAccess(absl::Status status); 394 395 } // namespace internal_statusor 396 ABSL_NAMESPACE_END 397 } // namespace absl 398 399 #endif // ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_ 400