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