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