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 /// \brief Stores a reference that can be changed. 27 template <typename T> 28 class ReferenceStorage { 29 T *Storage; 30 31 public: ReferenceStorage(T & Ref)32 ReferenceStorage(T &Ref) : Storage(&Ref) {} 33 34 operator T &() const { return *Storage; } get()35 T &get() const { return *Storage; } 36 }; 37 38 /// \brief Represents either an error or a value T. 39 /// 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. 45 /// 46 /// It is used like the following. 47 /// \code 48 /// ErrorOr<Buffer> getBuffer(); 49 /// 50 /// auto buffer = getBuffer(); 51 /// if (error_code ec = buffer.getError()) 52 /// return ec; 53 /// buffer->write("adena"); 54 /// \endcode 55 /// 56 /// 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. 60 /// 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 64 /// reference. 65 /// 66 /// T cannot be a rvalue reference. 67 template<class T> 68 class ErrorOr { 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; 72 73 public: 74 typedef typename std::conditional<isRef, wrap, T>::type storage_type; 75 76 private: 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; 81 82 public: 83 template <class E> 84 ErrorOr(E ErrorCode, 85 typename std::enable_if<std::is_error_code_enum<E>::value || 86 std::is_error_condition_enum<E>::value, 87 void *>::type = nullptr) HasError(true)88 : HasError(true) { 89 new (getErrorStorage()) std::error_code(make_error_code(ErrorCode)); 90 } 91 ErrorOr(std::error_code EC)92 ErrorOr(std::error_code EC) : HasError(true) { 93 new (getErrorStorage()) std::error_code(EC); 94 } 95 96 template <class OtherT> 97 ErrorOr(OtherT &&Val, 98 typename std::enable_if<std::is_convertible<OtherT, T>::value>::type 99 * = nullptr) HasError(false)100 : HasError(false) { 101 new (getStorage()) storage_type(std::forward<OtherT>(Val)); 102 } 103 ErrorOr(const ErrorOr & Other)104 ErrorOr(const ErrorOr &Other) { 105 copyConstruct(Other); 106 } 107 108 template <class OtherT> 109 ErrorOr( 110 const ErrorOr<OtherT> &Other, 111 typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * = 112 nullptr) { 113 copyConstruct(Other); 114 } 115 116 template <class OtherT> 117 explicit ErrorOr( 118 const ErrorOr<OtherT> &Other, 119 typename std::enable_if< 120 !std::is_convertible<OtherT, const T &>::value>::type * = nullptr) { 121 copyConstruct(Other); 122 } 123 ErrorOr(ErrorOr && Other)124 ErrorOr(ErrorOr &&Other) { 125 moveConstruct(std::move(Other)); 126 } 127 128 template <class OtherT> 129 ErrorOr( 130 ErrorOr<OtherT> &&Other, 131 typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * = 132 nullptr) { 133 moveConstruct(std::move(Other)); 134 } 135 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> 139 explicit 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 ErrorOr &operator=(const ErrorOr &Other) { 147 copyAssign(Other); 148 return *this; 149 } 150 151 ErrorOr &operator=(ErrorOr &&Other) { 152 moveAssign(std::move(Other)); 153 return *this; 154 } 155 ~ErrorOr()156 ~ErrorOr() { 157 if (!HasError) 158 getStorage()->~storage_type(); 159 } 160 161 /// \brief Return false if there is an error. 162 explicit operator bool() const { 163 return !HasError; 164 } 165 get()166 reference get() { return *getStorage(); } get()167 const_reference get() const { return const_cast<ErrorOr<T> *>(this)->get(); } 168 getError()169 std::error_code getError() const { 170 return HasError ? *getErrorStorage() : std::error_code(); 171 } 172 173 pointer operator ->() { 174 return toPointer(getStorage()); 175 } 176 177 const_pointer operator->() const { return toPointer(getStorage()); } 178 179 reference operator *() { 180 return *getStorage(); 181 } 182 183 const_reference operator*() const { return *getStorage(); } 184 185 private: 186 template <class OtherT> copyConstruct(const ErrorOr<OtherT> & Other)187 void copyConstruct(const ErrorOr<OtherT> &Other) { 188 if (!Other.HasError) { 189 // Get the other value. 190 HasError = false; 191 new (getStorage()) storage_type(*Other.getStorage()); 192 } else { 193 // Get other's error. 194 HasError = true; 195 new (getErrorStorage()) std::error_code(Other.getError()); 196 } 197 } 198 199 template <class T1> compareThisIfSameType(const T1 & a,const T1 & b)200 static bool compareThisIfSameType(const T1 &a, const T1 &b) { 201 return &a == &b; 202 } 203 204 template <class T1, class T2> compareThisIfSameType(const T1 & a,const T2 & b)205 static bool compareThisIfSameType(const T1 &a, const T2 &b) { 206 return false; 207 } 208 209 template <class OtherT> copyAssign(const ErrorOr<OtherT> & Other)210 void copyAssign(const ErrorOr<OtherT> &Other) { 211 if (compareThisIfSameType(*this, Other)) 212 return; 213 214 this->~ErrorOr(); 215 new (this) ErrorOr(Other); 216 } 217 218 template <class OtherT> moveConstruct(ErrorOr<OtherT> && Other)219 void moveConstruct(ErrorOr<OtherT> &&Other) { 220 if (!Other.HasError) { 221 // Get the other value. 222 HasError = false; 223 new (getStorage()) storage_type(std::move(*Other.getStorage())); 224 } else { 225 // Get other's error. 226 HasError = true; 227 new (getErrorStorage()) std::error_code(Other.getError()); 228 } 229 } 230 231 template <class OtherT> moveAssign(ErrorOr<OtherT> && Other)232 void moveAssign(ErrorOr<OtherT> &&Other) { 233 if (compareThisIfSameType(*this, Other)) 234 return; 235 236 this->~ErrorOr(); 237 new (this) ErrorOr(std::move(Other)); 238 } 239 toPointer(pointer Val)240 pointer toPointer(pointer Val) { 241 return Val; 242 } 243 toPointer(const_pointer Val)244 const_pointer toPointer(const_pointer Val) const { return Val; } 245 toPointer(wrap * Val)246 pointer toPointer(wrap *Val) { 247 return &Val->get(); 248 } 249 toPointer(const wrap * Val)250 const_pointer toPointer(const wrap *Val) const { return &Val->get(); } 251 getStorage()252 storage_type *getStorage() { 253 assert(!HasError && "Cannot get value when an error exists!"); 254 return reinterpret_cast<storage_type*>(TStorage.buffer); 255 } 256 getStorage()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); 260 } 261 getErrorStorage()262 std::error_code *getErrorStorage() { 263 assert(HasError && "Cannot get error when a value exists!"); 264 return reinterpret_cast<std::error_code *>(ErrorStorage.buffer); 265 } 266 getErrorStorage()267 const std::error_code *getErrorStorage() const { 268 return const_cast<ErrorOr<T> *>(this)->getErrorStorage(); 269 } 270 271 union { 272 AlignedCharArrayUnion<storage_type> TStorage; 273 AlignedCharArrayUnion<std::error_code> ErrorStorage; 274 }; 275 bool HasError : 1; 276 }; 277 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, 281 bool>::type 282 operator==(const ErrorOr<T> &Err, E Code) { 283 return Err.getError() == Code; 284 } 285 } // end namespace llvm 286 287 #endif // LLVM_SUPPORT_ERROROR_H 288