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