• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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