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 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) HasError(true)79 : HasError(true) { 80 new (getErrorStorage()) std::error_code(make_error_code(ErrorCode)); 81 } 82 ErrorOr(std::error_code EC)83 ErrorOr(std::error_code EC) : HasError(true) { 84 new (getErrorStorage()) std::error_code(EC); 85 } 86 87 template <class OtherT> 88 ErrorOr(OtherT &&Val, 89 typename std::enable_if<std::is_convertible<OtherT, T>::value>::type 90 * = nullptr) HasError(false)91 : HasError(false) { 92 new (getStorage()) storage_type(std::forward<OtherT>(Val)); 93 } 94 ErrorOr(const ErrorOr & Other)95 ErrorOr(const ErrorOr &Other) { 96 copyConstruct(Other); 97 } 98 99 template <class OtherT> 100 ErrorOr( 101 const ErrorOr<OtherT> &Other, 102 typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * = 103 nullptr) { 104 copyConstruct(Other); 105 } 106 107 template <class OtherT> 108 explicit ErrorOr( 109 const ErrorOr<OtherT> &Other, 110 typename std::enable_if< 111 !std::is_convertible<OtherT, const T &>::value>::type * = nullptr) { 112 copyConstruct(Other); 113 } 114 ErrorOr(ErrorOr && Other)115 ErrorOr(ErrorOr &&Other) { 116 moveConstruct(std::move(Other)); 117 } 118 119 template <class OtherT> 120 ErrorOr( 121 ErrorOr<OtherT> &&Other, 122 typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * = 123 nullptr) { 124 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 typename std::enable_if<!std::is_convertible<OtherT, T>::value>::type * = 133 nullptr) { 134 moveConstruct(std::move(Other)); 135 } 136 137 ErrorOr &operator=(const ErrorOr &Other) { 138 copyAssign(Other); 139 return *this; 140 } 141 142 ErrorOr &operator=(ErrorOr &&Other) { 143 moveAssign(std::move(Other)); 144 return *this; 145 } 146 ~ErrorOr()147 ~ErrorOr() { 148 if (!HasError) 149 getStorage()->~storage_type(); 150 } 151 152 /// Return false if there is an error. 153 explicit operator bool() const { 154 return !HasError; 155 } 156 get()157 reference get() { return *getStorage(); } get()158 const_reference get() const { return const_cast<ErrorOr<T> *>(this)->get(); } 159 getError()160 std::error_code getError() const { 161 return HasError ? *getErrorStorage() : std::error_code(); 162 } 163 164 pointer operator ->() { 165 return toPointer(getStorage()); 166 } 167 168 const_pointer operator->() const { return toPointer(getStorage()); } 169 170 reference operator *() { 171 return *getStorage(); 172 } 173 174 const_reference operator*() const { return *getStorage(); } 175 176 private: 177 template <class OtherT> copyConstruct(const ErrorOr<OtherT> & Other)178 void copyConstruct(const ErrorOr<OtherT> &Other) { 179 if (!Other.HasError) { 180 // Get the other value. 181 HasError = false; 182 new (getStorage()) storage_type(*Other.getStorage()); 183 } else { 184 // Get other's error. 185 HasError = true; 186 new (getErrorStorage()) std::error_code(Other.getError()); 187 } 188 } 189 190 template <class T1> compareThisIfSameType(const T1 & a,const T1 & b)191 static bool compareThisIfSameType(const T1 &a, const T1 &b) { 192 return &a == &b; 193 } 194 195 template <class T1, class T2> compareThisIfSameType(const T1 & a,const T2 & b)196 static bool compareThisIfSameType(const T1 &a, const T2 &b) { 197 return false; 198 } 199 200 template <class OtherT> copyAssign(const ErrorOr<OtherT> & Other)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> moveConstruct(ErrorOr<OtherT> && Other)210 void moveConstruct(ErrorOr<OtherT> &&Other) { 211 if (!Other.HasError) { 212 // Get the other value. 213 HasError = false; 214 new (getStorage()) storage_type(std::move(*Other.getStorage())); 215 } else { 216 // Get other's error. 217 HasError = true; 218 new (getErrorStorage()) std::error_code(Other.getError()); 219 } 220 } 221 222 template <class OtherT> moveAssign(ErrorOr<OtherT> && Other)223 void moveAssign(ErrorOr<OtherT> &&Other) { 224 if (compareThisIfSameType(*this, Other)) 225 return; 226 227 this->~ErrorOr(); 228 new (this) ErrorOr(std::move(Other)); 229 } 230 toPointer(pointer Val)231 pointer toPointer(pointer Val) { 232 return Val; 233 } 234 toPointer(const_pointer Val)235 const_pointer toPointer(const_pointer Val) const { return Val; } 236 toPointer(wrap * Val)237 pointer toPointer(wrap *Val) { 238 return &Val->get(); 239 } 240 toPointer(const wrap * Val)241 const_pointer toPointer(const wrap *Val) const { return &Val->get(); } 242 getStorage()243 storage_type *getStorage() { 244 assert(!HasError && "Cannot get value when an error exists!"); 245 return reinterpret_cast<storage_type*>(TStorage.buffer); 246 } 247 getStorage()248 const storage_type *getStorage() const { 249 assert(!HasError && "Cannot get value when an error exists!"); 250 return reinterpret_cast<const storage_type*>(TStorage.buffer); 251 } 252 getErrorStorage()253 std::error_code *getErrorStorage() { 254 assert(HasError && "Cannot get error when a value exists!"); 255 return reinterpret_cast<std::error_code *>(ErrorStorage.buffer); 256 } 257 getErrorStorage()258 const std::error_code *getErrorStorage() const { 259 return const_cast<ErrorOr<T> *>(this)->getErrorStorage(); 260 } 261 262 union { 263 AlignedCharArrayUnion<storage_type> TStorage; 264 AlignedCharArrayUnion<std::error_code> ErrorStorage; 265 }; 266 bool HasError : 1; 267 }; 268 269 template <class T, class E> 270 typename std::enable_if<std::is_error_code_enum<E>::value || 271 std::is_error_condition_enum<E>::value, 272 bool>::type 273 operator==(const ErrorOr<T> &Err, E Code) { 274 return Err.getError() == Code; 275 } 276 277 } // end namespace llvm 278 279 #endif // LLVM_SUPPORT_ERROROR_H 280