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