1 //===- llvm/Support/ErrorOr.h - Error Smart Pointer -------------*- C++ -*-===//
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
12 /// Provides ErrorOr<T> smart pointer.
14 //===----------------------------------------------------------------------===//
16 #ifndef LLVM_SUPPORT_ERROROR_H
17 #define LLVM_SUPPORT_ERROROR_H
19 #include "llvm/ADT/PointerIntPair.h"
20 #include "llvm/Support/AlignOf.h"
22 #include <system_error>
23 #include <type_traits>
26 /// \brief Stores a reference that can be changed.
28 class ReferenceStorage {
32 ReferenceStorage(T &Ref) : Storage(&Ref) {}
34 operator T &() const { return *Storage; }
35 T &get() const { return *Storage; }
38 /// \brief Represents either an error or a value T.
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.
46 /// It is used like the following.
48 /// ErrorOr<Buffer> getBuffer();
50 /// auto buffer = getBuffer();
51 /// if (error_code ec = buffer.getError())
53 /// buffer->write("adena");
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.
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
66 /// T cannot be a rvalue reference.
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;
74 typedef typename std::conditional<isRef, wrap, T>::type storage_type;
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;
85 typename std::enable_if<std::is_error_code_enum<E>::value ||
86 std::is_error_condition_enum<E>::value,
87 void *>::type = nullptr)
89 new (getErrorStorage()) std::error_code(make_error_code(ErrorCode));
92 ErrorOr(std::error_code EC) : HasError(true) {
93 new (getErrorStorage()) std::error_code(EC);
96 template <class OtherT>
98 typename std::enable_if<std::is_convertible<OtherT, T>::value>::type
101 new (getStorage()) storage_type(std::forward<OtherT>(Val));
104 ErrorOr(const ErrorOr &Other) {
105 copyConstruct(Other);
108 template <class OtherT>
110 const ErrorOr<OtherT> &Other,
111 typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * =
113 copyConstruct(Other);
116 template <class OtherT>
118 const ErrorOr<OtherT> &Other,
119 typename std::enable_if<
120 !std::is_convertible<OtherT, const T &>::value>::type * = nullptr) {
121 copyConstruct(Other);
124 ErrorOr(ErrorOr &&Other) {
125 moveConstruct(std::move(Other));
128 template <class OtherT>
130 ErrorOr<OtherT> &&Other,
131 typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * =
133 moveConstruct(std::move(Other));
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>
140 ErrorOr<OtherT> &&Other,
141 typename std::enable_if<!std::is_convertible<OtherT, T>::value>::type * =
143 moveConstruct(std::move(Other));
146 ErrorOr &operator=(const ErrorOr &Other) {
151 ErrorOr &operator=(ErrorOr &&Other) {
152 moveAssign(std::move(Other));
158 getStorage()->~storage_type();
161 /// \brief Return false if there is an error.
162 explicit operator bool() const {
166 reference get() { return *getStorage(); }
167 const_reference get() const { return const_cast<ErrorOr<T> *>(this)->get(); }
169 std::error_code getError() const {
170 return HasError ? *getErrorStorage() : std::error_code();
173 pointer operator ->() {
174 return toPointer(getStorage());
177 const_pointer operator->() const { return toPointer(getStorage()); }
179 reference operator *() {
180 return *getStorage();
183 const_reference operator*() const { return *getStorage(); }
186 template <class OtherT>
187 void copyConstruct(const ErrorOr<OtherT> &Other) {
188 if (!Other.HasError) {
189 // Get the other value.
191 new (getStorage()) storage_type(*Other.getStorage());
193 // Get other's error.
195 new (getErrorStorage()) std::error_code(Other.getError());
200 static bool compareThisIfSameType(const T1 &a, const T1 &b) {
204 template <class T1, class T2>
205 static bool compareThisIfSameType(const T1 &a, const T2 &b) {
209 template <class OtherT>
210 void copyAssign(const ErrorOr<OtherT> &Other) {
211 if (compareThisIfSameType(*this, Other))
215 new (this) ErrorOr(Other);
218 template <class OtherT>
219 void moveConstruct(ErrorOr<OtherT> &&Other) {
220 if (!Other.HasError) {
221 // Get the other value.
223 new (getStorage()) storage_type(std::move(*Other.getStorage()));
225 // Get other's error.
227 new (getErrorStorage()) std::error_code(Other.getError());
231 template <class OtherT>
232 void moveAssign(ErrorOr<OtherT> &&Other) {
233 if (compareThisIfSameType(*this, Other))
237 new (this) ErrorOr(std::move(Other));
240 pointer toPointer(pointer Val) {
244 const_pointer toPointer(const_pointer Val) const { return Val; }
246 pointer toPointer(wrap *Val) {
250 const_pointer toPointer(const wrap *Val) const { return &Val->get(); }
252 storage_type *getStorage() {
253 assert(!HasError && "Cannot get value when an error exists!");
254 return reinterpret_cast<storage_type*>(TStorage.buffer);
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);
262 std::error_code *getErrorStorage() {
263 assert(HasError && "Cannot get error when a value exists!");
264 return reinterpret_cast<std::error_code *>(ErrorStorage.buffer);
267 const std::error_code *getErrorStorage() const {
268 return const_cast<ErrorOr<T> *>(this)->getErrorStorage();
272 AlignedCharArrayUnion<storage_type> TStorage;
273 AlignedCharArrayUnion<std::error_code> ErrorStorage;
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,
282 operator==(const ErrorOr<T> &Err, E Code) {
283 return Err.getError() == Code;
285 } // end namespace llvm
287 #endif // LLVM_SUPPORT_ERROROR_H