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