1 //===- llvm/Support/ErrorOr.h - Error Smart Pointer -------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 ///
11 /// Provides ErrorOr<T> smart pointer.
12 ///
13 //===----------------------------------------------------------------------===//
14 
15 #ifndef LLVM_SUPPORT_ERROROR_H
16 #define LLVM_SUPPORT_ERROROR_H
17 
18 #include "llvm/Support/AlignOf.h"
19 #include <cassert>
20 #include <system_error>
21 #include <type_traits>
22 #include <utility>
23 
24 namespace llvm {
25 
26 /// Represents either an error or a value T.
27 ///
28 /// ErrorOr<T> is a pointer-like class that represents the result of an
29 /// operation. The result is either an error, or a value of type T. This is
30 /// designed to emulate the usage of returning a pointer where nullptr indicates
31 /// failure. However instead of just knowing that the operation failed, we also
32 /// have an error_code and optional user data that describes why it failed.
33 ///
34 /// It is used like the following.
35 /// \code
36 ///   ErrorOr<Buffer> getBuffer();
37 ///
38 ///   auto buffer = getBuffer();
39 ///   if (error_code ec = buffer.getError())
40 ///     return ec;
41 ///   buffer->write("adena");
42 /// \endcode
43 ///
44 ///
45 /// Implicit conversion to bool returns true if there is a usable value. The
46 /// unary * and -> operators provide pointer like access to the value. Accessing
47 /// the value when there is an error has undefined behavior.
48 ///
49 /// When T is a reference type the behavior is slightly different. The reference
50 /// is held in a std::reference_wrapper<std::remove_reference<T>::type>, and
51 /// there is special handling to make operator -> work as if T was not a
52 /// reference.
53 ///
54 /// T cannot be a rvalue reference.
55 template<class T>
56 class ErrorOr {
57   template <class OtherT> friend class ErrorOr;
58 
59   static constexpr bool isRef = std::is_reference<T>::value;
60 
61   using wrap = std::reference_wrapper<std::remove_reference_t<T>>;
62 
63 public:
64   using storage_type = std::conditional_t<isRef, wrap, T>;
65 
66 private:
67   using reference = std::remove_reference_t<T> &;
68   using const_reference = const std::remove_reference_t<T> &;
69   using pointer = std::remove_reference_t<T> *;
70   using const_pointer = const std::remove_reference_t<T> *;
71 
72 public:
73   template <class E>
74   ErrorOr(E ErrorCode,
75           std::enable_if_t<std::is_error_code_enum<E>::value ||
76                                std::is_error_condition_enum<E>::value,
77                            void *> = nullptr)
78       : HasError(true) {
79     new (getErrorStorage()) std::error_code(make_error_code(ErrorCode));
80   }
81 
82   ErrorOr(std::error_code EC) : HasError(true) {
83     new (getErrorStorage()) std::error_code(EC);
84   }
85 
86   template <class OtherT>
87   ErrorOr(OtherT &&Val,
88           std::enable_if_t<std::is_convertible<OtherT, T>::value> * = nullptr)
89       : HasError(false) {
90     new (getStorage()) storage_type(std::forward<OtherT>(Val));
91   }
92 
93   ErrorOr(const ErrorOr &Other) {
94     copyConstruct(Other);
95   }
96 
97   template <class OtherT>
98   ErrorOr(const ErrorOr<OtherT> &Other,
99           std::enable_if_t<std::is_convertible<OtherT, T>::value> * = nullptr) {
100     copyConstruct(Other);
101   }
102 
103   template <class OtherT>
104   explicit ErrorOr(
105       const ErrorOr<OtherT> &Other,
106       std::enable_if_t<!std::is_convertible<OtherT, const T &>::value> * =
107           nullptr) {
108     copyConstruct(Other);
109   }
110 
111   ErrorOr(ErrorOr &&Other) {
112     moveConstruct(std::move(Other));
113   }
114 
115   template <class OtherT>
116   ErrorOr(ErrorOr<OtherT> &&Other,
117           std::enable_if_t<std::is_convertible<OtherT, T>::value> * = nullptr) {
118     moveConstruct(std::move(Other));
119   }
120 
121   // This might eventually need SFINAE but it's more complex than is_convertible
122   // & I'm too lazy to write it right now.
123   template <class OtherT>
124   explicit ErrorOr(
125       ErrorOr<OtherT> &&Other,
126       std::enable_if_t<!std::is_convertible<OtherT, T>::value> * = nullptr) {
127     moveConstruct(std::move(Other));
128   }
129 
130   ErrorOr &operator=(const ErrorOr &Other) {
131     copyAssign(Other);
132     return *this;
133   }
134 
135   ErrorOr &operator=(ErrorOr &&Other) {
136     moveAssign(std::move(Other));
137     return *this;
138   }
139 
140   ~ErrorOr() {
141     if (!HasError)
142       getStorage()->~storage_type();
143   }
144 
145   /// Return false if there is an error.
146   explicit operator bool() const {
147     return !HasError;
148   }
149 
150   reference get() { return *getStorage(); }
151   const_reference get() const { return const_cast<ErrorOr<T> *>(this)->get(); }
152 
153   std::error_code getError() const {
154     return HasError ? *getErrorStorage() : std::error_code();
155   }
156 
157   pointer operator ->() {
158     return toPointer(getStorage());
159   }
160 
161   const_pointer operator->() const { return toPointer(getStorage()); }
162 
163   reference operator *() {
164     return *getStorage();
165   }
166 
167   const_reference operator*() const { return *getStorage(); }
168 
169 private:
170   template <class OtherT>
171   void copyConstruct(const ErrorOr<OtherT> &Other) {
172     if (!Other.HasError) {
173       // Get the other value.
174       HasError = false;
175       new (getStorage()) storage_type(*Other.getStorage());
176     } else {
177       // Get other's error.
178       HasError = true;
179       new (getErrorStorage()) std::error_code(Other.getError());
180     }
181   }
182 
183   template <class T1>
184   static bool compareThisIfSameType(const T1 &a, const T1 &b) {
185     return &a == &b;
186   }
187 
188   template <class T1, class T2>
189   static bool compareThisIfSameType(const T1 &a, const T2 &b) {
190     return false;
191   }
192 
193   template <class OtherT>
194   void copyAssign(const ErrorOr<OtherT> &Other) {
195     if (compareThisIfSameType(*this, Other))
196       return;
197 
198     this->~ErrorOr();
199     new (this) ErrorOr(Other);
200   }
201 
202   template <class OtherT>
203   void moveConstruct(ErrorOr<OtherT> &&Other) {
204     if (!Other.HasError) {
205       // Get the other value.
206       HasError = false;
207       new (getStorage()) storage_type(std::move(*Other.getStorage()));
208     } else {
209       // Get other's error.
210       HasError = true;
211       new (getErrorStorage()) std::error_code(Other.getError());
212     }
213   }
214 
215   template <class OtherT>
216   void moveAssign(ErrorOr<OtherT> &&Other) {
217     if (compareThisIfSameType(*this, Other))
218       return;
219 
220     this->~ErrorOr();
221     new (this) ErrorOr(std::move(Other));
222   }
223 
224   pointer toPointer(pointer Val) {
225     return Val;
226   }
227 
228   const_pointer toPointer(const_pointer Val) const { return Val; }
229 
230   pointer toPointer(wrap *Val) {
231     return &Val->get();
232   }
233 
234   const_pointer toPointer(const wrap *Val) const { return &Val->get(); }
235 
236   storage_type *getStorage() {
237     assert(!HasError && "Cannot get value when an error exists!");
238     return reinterpret_cast<storage_type *>(&TStorage);
239   }
240 
241   const storage_type *getStorage() const {
242     assert(!HasError && "Cannot get value when an error exists!");
243     return reinterpret_cast<const storage_type *>(&TStorage);
244   }
245 
246   std::error_code *getErrorStorage() {
247     assert(HasError && "Cannot get error when a value exists!");
248     return reinterpret_cast<std::error_code *>(&ErrorStorage);
249   }
250 
251   const std::error_code *getErrorStorage() const {
252     return const_cast<ErrorOr<T> *>(this)->getErrorStorage();
253   }
254 
255   union {
256     AlignedCharArrayUnion<storage_type> TStorage;
257     AlignedCharArrayUnion<std::error_code> ErrorStorage;
258   };
259   bool HasError : 1;
260 };
261 
262 template <class T, class E>
263 std::enable_if_t<std::is_error_code_enum<E>::value ||
264                      std::is_error_condition_enum<E>::value,
265                  bool>
266 operator==(const ErrorOr<T> &Err, E Code) {
267   return Err.getError() == Code;
268 }
269 
270 } // end namespace llvm
271 
272 #endif // LLVM_SUPPORT_ERROROR_H
273