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