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