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