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