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