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 const bool isRef = std::is_reference<T>::value;
60 
61   using wrap = std::reference_wrapper<typename std::remove_reference<T>::type>;
62 
63 public:
64   using storage_type = typename std::conditional<isRef, wrap, T>::type;
65 
66 private:
67   using reference = typename std::remove_reference<T>::type &;
68   using const_reference = const typename std::remove_reference<T>::type &;
69   using pointer = typename std::remove_reference<T>::type *;
70   using const_pointer = const typename std::remove_reference<T>::type *;
71 
72 public:
73   template <class E>
74   ErrorOr(E ErrorCode,
75           typename std::enable_if<std::is_error_code_enum<E>::value ||
76                                       std::is_error_condition_enum<E>::value,
77                                   void *>::type = 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           typename std::enable_if<std::is_convertible<OtherT, T>::value>::type
89               * = nullptr)
90       : HasError(false) {
91     new (getStorage()) storage_type(std::forward<OtherT>(Val));
92   }
93 
94   ErrorOr(const ErrorOr &Other) {
95     copyConstruct(Other);
96   }
97 
98   template <class OtherT>
99   ErrorOr(
100       const ErrorOr<OtherT> &Other,
101       typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * =
102           nullptr) {
103     copyConstruct(Other);
104   }
105 
106   template <class OtherT>
107   explicit ErrorOr(
108       const ErrorOr<OtherT> &Other,
109       typename std::enable_if<
110           !std::is_convertible<OtherT, const T &>::value>::type * = nullptr) {
111     copyConstruct(Other);
112   }
113 
114   ErrorOr(ErrorOr &&Other) {
115     moveConstruct(std::move(Other));
116   }
117 
118   template <class OtherT>
119   ErrorOr(
120       ErrorOr<OtherT> &&Other,
121       typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * =
122           nullptr) {
123     moveConstruct(std::move(Other));
124   }
125 
126   // This might eventually need SFINAE but it's more complex than is_convertible
127   // & I'm too lazy to write it right now.
128   template <class OtherT>
129   explicit 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   ErrorOr &operator=(const ErrorOr &Other) {
137     copyAssign(Other);
138     return *this;
139   }
140 
141   ErrorOr &operator=(ErrorOr &&Other) {
142     moveAssign(std::move(Other));
143     return *this;
144   }
145 
146   ~ErrorOr() {
147     if (!HasError)
148       getStorage()->~storage_type();
149   }
150 
151   /// Return false if there is an error.
152   explicit operator bool() const {
153     return !HasError;
154   }
155 
156   reference get() { return *getStorage(); }
157   const_reference get() const { return const_cast<ErrorOr<T> *>(this)->get(); }
158 
159   std::error_code getError() const {
160     return HasError ? *getErrorStorage() : std::error_code();
161   }
162 
163   pointer operator ->() {
164     return toPointer(getStorage());
165   }
166 
167   const_pointer operator->() const { return toPointer(getStorage()); }
168 
169   reference operator *() {
170     return *getStorage();
171   }
172 
173   const_reference operator*() const { return *getStorage(); }
174 
175 private:
176   template <class OtherT>
177   void copyConstruct(const ErrorOr<OtherT> &Other) {
178     if (!Other.HasError) {
179       // Get the other value.
180       HasError = false;
181       new (getStorage()) storage_type(*Other.getStorage());
182     } else {
183       // Get other's error.
184       HasError = true;
185       new (getErrorStorage()) std::error_code(Other.getError());
186     }
187   }
188 
189   template <class T1>
190   static bool compareThisIfSameType(const T1 &a, const T1 &b) {
191     return &a == &b;
192   }
193 
194   template <class T1, class T2>
195   static bool compareThisIfSameType(const T1 &a, const T2 &b) {
196     return false;
197   }
198 
199   template <class OtherT>
200   void copyAssign(const ErrorOr<OtherT> &Other) {
201     if (compareThisIfSameType(*this, Other))
202       return;
203 
204     this->~ErrorOr();
205     new (this) ErrorOr(Other);
206   }
207 
208   template <class OtherT>
209   void moveConstruct(ErrorOr<OtherT> &&Other) {
210     if (!Other.HasError) {
211       // Get the other value.
212       HasError = false;
213       new (getStorage()) storage_type(std::move(*Other.getStorage()));
214     } else {
215       // Get other's error.
216       HasError = true;
217       new (getErrorStorage()) std::error_code(Other.getError());
218     }
219   }
220 
221   template <class OtherT>
222   void moveAssign(ErrorOr<OtherT> &&Other) {
223     if (compareThisIfSameType(*this, Other))
224       return;
225 
226     this->~ErrorOr();
227     new (this) ErrorOr(std::move(Other));
228   }
229 
230   pointer toPointer(pointer Val) {
231     return Val;
232   }
233 
234   const_pointer toPointer(const_pointer Val) const { return Val; }
235 
236   pointer toPointer(wrap *Val) {
237     return &Val->get();
238   }
239 
240   const_pointer toPointer(const wrap *Val) const { return &Val->get(); }
241 
242   storage_type *getStorage() {
243     assert(!HasError && "Cannot get value when an error exists!");
244     return reinterpret_cast<storage_type*>(TStorage.buffer);
245   }
246 
247   const storage_type *getStorage() const {
248     assert(!HasError && "Cannot get value when an error exists!");
249     return reinterpret_cast<const storage_type*>(TStorage.buffer);
250   }
251 
252   std::error_code *getErrorStorage() {
253     assert(HasError && "Cannot get error when a value exists!");
254     return reinterpret_cast<std::error_code *>(ErrorStorage.buffer);
255   }
256 
257   const std::error_code *getErrorStorage() const {
258     return const_cast<ErrorOr<T> *>(this)->getErrorStorage();
259   }
260 
261   union {
262     AlignedCharArrayUnion<storage_type> TStorage;
263     AlignedCharArrayUnion<std::error_code> ErrorStorage;
264   };
265   bool HasError : 1;
266 };
267 
268 template <class T, class E>
269 typename std::enable_if<std::is_error_code_enum<E>::value ||
270                             std::is_error_condition_enum<E>::value,
271                         bool>::type
272 operator==(const ErrorOr<T> &Err, E Code) {
273   return Err.getError() == Code;
274 }
275 
276 } // end namespace llvm
277 
278 #endif // LLVM_SUPPORT_ERROROR_H
279