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