xref: /aosp_15_r20/external/abseil-cpp/absl/status/internal/statusor_internal.h (revision 9356374a3709195abf420251b3e825997ff56c0f)
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 template <bool Value, typename T>
127 using Equality = std::conditional_t<Value, T, absl::negation<T>>;
128 
129 template <bool Explicit, typename T, typename U, bool Lifetimebound>
130 using IsConstructionValid = absl::conjunction<
131     Equality<Lifetimebound,
132              type_traits_internal::IsLifetimeBoundAssignment<T, U>>,
133     IsDirectInitializationValid<T, U&&>, std::is_constructible<T, U&&>,
134     Equality<!Explicit, std::is_convertible<U&&, T>>,
135     absl::disjunction<
136         std::is_same<T, absl::remove_cvref_t<U>>,
137         absl::conjunction<
138             std::conditional_t<
139                 Explicit,
140                 absl::negation<std::is_constructible<absl::Status, U&&>>,
141                 absl::negation<std::is_convertible<U&&, absl::Status>>>,
142             absl::negation<
143                 internal_statusor::HasConversionOperatorToStatusOr<T, U&&>>>>>;
144 
145 template <typename T, typename U, bool Lifetimebound>
146 using IsAssignmentValid = absl::conjunction<
147     Equality<Lifetimebound,
148              type_traits_internal::IsLifetimeBoundAssignment<T, U>>,
149     std::is_constructible<T, U&&>, std::is_assignable<T&, U&&>,
150     absl::disjunction<
151         std::is_same<T, absl::remove_cvref_t<U>>,
152         absl::conjunction<
153             absl::negation<std::is_convertible<U&&, absl::Status>>,
154             absl::negation<HasConversionOperatorToStatusOr<T, U&&>>>>,
155     IsForwardingAssignmentValid<T, U&&>>;
156 
157 template <bool Explicit, typename T, typename U>
158 using IsConstructionFromStatusValid = absl::conjunction<
159     absl::negation<std::is_same<absl::StatusOr<T>, absl::remove_cvref_t<U>>>,
160     absl::negation<std::is_same<T, absl::remove_cvref_t<U>>>,
161     absl::negation<std::is_same<absl::in_place_t, absl::remove_cvref_t<U>>>,
162     Equality<!Explicit, std::is_convertible<U, absl::Status>>,
163     std::is_constructible<absl::Status, U>,
164     absl::negation<HasConversionOperatorToStatusOr<T, U>>>;
165 
166 template <bool Explicit, typename T, typename U, bool Lifetimebound,
167           typename UQ>
168 using IsConstructionFromStatusOrValid = absl::conjunction<
169     absl::negation<std::is_same<T, U>>,
170     Equality<Lifetimebound,
171              type_traits_internal::IsLifetimeBoundAssignment<T, U>>,
172     std::is_constructible<T, UQ>,
173     Equality<!Explicit, std::is_convertible<UQ, T>>,
174     absl::negation<IsConstructibleOrConvertibleFromStatusOr<T, U>>>;
175 
176 template <typename T, typename U, bool Lifetimebound>
177 using IsStatusOrAssignmentValid = absl::conjunction<
178     absl::negation<std::is_same<T, absl::remove_cvref_t<U>>>,
179     Equality<Lifetimebound,
180              type_traits_internal::IsLifetimeBoundAssignment<T, U>>,
181     std::is_constructible<T, U>, std::is_assignable<T, U>,
182     absl::negation<IsConstructibleOrConvertibleOrAssignableFromStatusOr<
183         T, absl::remove_cvref_t<U>>>>;
184 
185 class Helper {
186  public:
187   // Move type-agnostic error handling to the .cc.
188   static void HandleInvalidStatusCtorArg(absl::Nonnull<Status*>);
189   [[noreturn]] static void Crash(const absl::Status& status);
190 };
191 
192 // Construct an instance of T in `p` through placement new, passing Args... to
193 // the constructor.
194 // This abstraction is here mostly for the gcc performance fix.
195 template <typename T, typename... Args>
196 ABSL_ATTRIBUTE_NONNULL(1)
197 void PlacementNew(absl::Nonnull<void*> p, Args&&... args) {
198   new (p) T(std::forward<Args>(args)...);
199 }
200 
201 // Helper base class to hold the data and all operations.
202 // We move all this to a base class to allow mixing with the appropriate
203 // TraitsBase specialization.
204 template <typename T>
205 class StatusOrData {
206   template <typename U>
207   friend class StatusOrData;
208 
209  public:
210   StatusOrData() = delete;
211 
212   StatusOrData(const StatusOrData& other) {
213     if (other.ok()) {
214       MakeValue(other.data_);
215       MakeStatus();
216     } else {
217       MakeStatus(other.status_);
218     }
219   }
220 
221   StatusOrData(StatusOrData&& other) noexcept {
222     if (other.ok()) {
223       MakeValue(std::move(other.data_));
224       MakeStatus();
225     } else {
226       MakeStatus(std::move(other.status_));
227     }
228   }
229 
230   template <typename U>
231   explicit StatusOrData(const StatusOrData<U>& other) {
232     if (other.ok()) {
233       MakeValue(other.data_);
234       MakeStatus();
235     } else {
236       MakeStatus(other.status_);
237     }
238   }
239 
240   template <typename U>
241   explicit StatusOrData(StatusOrData<U>&& other) {
242     if (other.ok()) {
243       MakeValue(std::move(other.data_));
244       MakeStatus();
245     } else {
246       MakeStatus(std::move(other.status_));
247     }
248   }
249 
250   template <typename... Args>
251   explicit StatusOrData(absl::in_place_t, Args&&... args)
252       : data_(std::forward<Args>(args)...) {
253     MakeStatus();
254   }
255 
256   explicit StatusOrData(const T& value) : data_(value) {
257     MakeStatus();
258   }
259   explicit StatusOrData(T&& value) : data_(std::move(value)) {
260     MakeStatus();
261   }
262 
263   template <typename U,
264             absl::enable_if_t<std::is_constructible<absl::Status, U&&>::value,
265                               int> = 0>
266   explicit StatusOrData(U&& v) : status_(std::forward<U>(v)) {
267     EnsureNotOk();
268   }
269 
270   StatusOrData& operator=(const StatusOrData& other) {
271     if (this == &other) return *this;
272     if (other.ok())
273       Assign(other.data_);
274     else
275       AssignStatus(other.status_);
276     return *this;
277   }
278 
279   StatusOrData& operator=(StatusOrData&& other) {
280     if (this == &other) return *this;
281     if (other.ok())
282       Assign(std::move(other.data_));
283     else
284       AssignStatus(std::move(other.status_));
285     return *this;
286   }
287 
288   ~StatusOrData() {
289     if (ok()) {
290       status_.~Status();
291       data_.~T();
292     } else {
293       status_.~Status();
294     }
295   }
296 
297   template <typename U>
298   void Assign(U&& value) {
299     if (ok()) {
300       data_ = std::forward<U>(value);
301     } else {
302       MakeValue(std::forward<U>(value));
303       status_ = OkStatus();
304     }
305   }
306 
307   template <typename U>
308   void AssignStatus(U&& v) {
309     Clear();
310     status_ = static_cast<absl::Status>(std::forward<U>(v));
311     EnsureNotOk();
312   }
313 
314   bool ok() const { return status_.ok(); }
315 
316  protected:
317   // status_ will always be active after the constructor.
318   // We make it a union to be able to initialize exactly how we need without
319   // waste.
320   // Eg. in the copy constructor we use the default constructor of Status in
321   // the ok() path to avoid an extra Ref call.
322   union {
323     Status status_;
324   };
325 
326   // data_ is active iff status_.ok()==true
327   struct Dummy {};
328   union {
329     // When T is const, we need some non-const object we can cast to void* for
330     // the placement new. dummy_ is that object.
331     Dummy dummy_;
332     T data_;
333   };
334 
335   void Clear() {
336     if (ok()) data_.~T();
337   }
338 
339   void EnsureOk() const {
340     if (ABSL_PREDICT_FALSE(!ok())) Helper::Crash(status_);
341   }
342 
343   void EnsureNotOk() {
344     if (ABSL_PREDICT_FALSE(ok())) Helper::HandleInvalidStatusCtorArg(&status_);
345   }
346 
347   // Construct the value (ie. data_) through placement new with the passed
348   // argument.
349   template <typename... Arg>
350   void MakeValue(Arg&&... arg) {
351     internal_statusor::PlacementNew<T>(&dummy_, std::forward<Arg>(arg)...);
352   }
353 
354   // Construct the status (ie. status_) through placement new with the passed
355   // argument.
356   template <typename... Args>
357   void MakeStatus(Args&&... args) {
358     internal_statusor::PlacementNew<Status>(&status_,
359                                             std::forward<Args>(args)...);
360   }
361 };
362 
363 // Helper base classes to allow implicitly deleted constructors and assignment
364 // operators in `StatusOr`. For example, `CopyCtorBase` will explicitly delete
365 // the copy constructor when T is not copy constructible and `StatusOr` will
366 // inherit that behavior implicitly.
367 template <typename T, bool = std::is_copy_constructible<T>::value>
368 struct CopyCtorBase {
369   CopyCtorBase() = default;
370   CopyCtorBase(const CopyCtorBase&) = default;
371   CopyCtorBase(CopyCtorBase&&) = default;
372   CopyCtorBase& operator=(const CopyCtorBase&) = default;
373   CopyCtorBase& operator=(CopyCtorBase&&) = default;
374 };
375 
376 template <typename T>
377 struct CopyCtorBase<T, false> {
378   CopyCtorBase() = default;
379   CopyCtorBase(const CopyCtorBase&) = delete;
380   CopyCtorBase(CopyCtorBase&&) = default;
381   CopyCtorBase& operator=(const CopyCtorBase&) = default;
382   CopyCtorBase& operator=(CopyCtorBase&&) = default;
383 };
384 
385 template <typename T, bool = std::is_move_constructible<T>::value>
386 struct MoveCtorBase {
387   MoveCtorBase() = default;
388   MoveCtorBase(const MoveCtorBase&) = default;
389   MoveCtorBase(MoveCtorBase&&) = default;
390   MoveCtorBase& operator=(const MoveCtorBase&) = default;
391   MoveCtorBase& operator=(MoveCtorBase&&) = default;
392 };
393 
394 template <typename T>
395 struct MoveCtorBase<T, false> {
396   MoveCtorBase() = default;
397   MoveCtorBase(const MoveCtorBase&) = default;
398   MoveCtorBase(MoveCtorBase&&) = delete;
399   MoveCtorBase& operator=(const MoveCtorBase&) = default;
400   MoveCtorBase& operator=(MoveCtorBase&&) = default;
401 };
402 
403 template <typename T, bool = std::is_copy_constructible<T>::value&&
404                           std::is_copy_assignable<T>::value>
405 struct CopyAssignBase {
406   CopyAssignBase() = default;
407   CopyAssignBase(const CopyAssignBase&) = default;
408   CopyAssignBase(CopyAssignBase&&) = default;
409   CopyAssignBase& operator=(const CopyAssignBase&) = default;
410   CopyAssignBase& operator=(CopyAssignBase&&) = default;
411 };
412 
413 template <typename T>
414 struct CopyAssignBase<T, false> {
415   CopyAssignBase() = default;
416   CopyAssignBase(const CopyAssignBase&) = default;
417   CopyAssignBase(CopyAssignBase&&) = default;
418   CopyAssignBase& operator=(const CopyAssignBase&) = delete;
419   CopyAssignBase& operator=(CopyAssignBase&&) = default;
420 };
421 
422 template <typename T, bool = std::is_move_constructible<T>::value&&
423                           std::is_move_assignable<T>::value>
424 struct MoveAssignBase {
425   MoveAssignBase() = default;
426   MoveAssignBase(const MoveAssignBase&) = default;
427   MoveAssignBase(MoveAssignBase&&) = default;
428   MoveAssignBase& operator=(const MoveAssignBase&) = default;
429   MoveAssignBase& operator=(MoveAssignBase&&) = default;
430 };
431 
432 template <typename T>
433 struct MoveAssignBase<T, false> {
434   MoveAssignBase() = default;
435   MoveAssignBase(const MoveAssignBase&) = default;
436   MoveAssignBase(MoveAssignBase&&) = default;
437   MoveAssignBase& operator=(const MoveAssignBase&) = default;
438   MoveAssignBase& operator=(MoveAssignBase&&) = delete;
439 };
440 
441 [[noreturn]] void ThrowBadStatusOrAccess(absl::Status status);
442 
443 // Used to introduce jitter into the output of printing functions for
444 // `StatusOr` (i.e. `AbslStringify` and `operator<<`).
445 class StringifyRandom {
446   enum BracesType {
447     kBareParens = 0,
448     kSpaceParens,
449     kBareBrackets,
450     kSpaceBrackets,
451   };
452 
453   // Returns a random `BracesType` determined once per binary load.
454   static BracesType RandomBraces() {
455     static const BracesType kRandomBraces = static_cast<BracesType>(
456         (reinterpret_cast<uintptr_t>(&kRandomBraces) >> 4) % 4);
457     return kRandomBraces;
458   }
459 
460  public:
461   static inline absl::string_view OpenBrackets() {
462     switch (RandomBraces()) {
463       case kBareParens:
464         return "(";
465       case kSpaceParens:
466         return "( ";
467       case kBareBrackets:
468         return "[";
469       case kSpaceBrackets:
470         return "[ ";
471     }
472     return "(";
473   }
474 
475   static inline absl::string_view CloseBrackets() {
476     switch (RandomBraces()) {
477       case kBareParens:
478         return ")";
479       case kSpaceParens:
480         return " )";
481       case kBareBrackets:
482         return "]";
483       case kSpaceBrackets:
484         return " ]";
485     }
486     return ")";
487   }
488 };
489 
490 }  // namespace internal_statusor
491 ABSL_NAMESPACE_END
492 }  // namespace absl
493 
494 #endif  // ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_
495