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 25namespace llvm { 26/// \brief Stores a reference that can be changed. 27template <typename T> 28class ReferenceStorage { 29 T *Storage; 30 31public: 32 ReferenceStorage(T &Ref) : Storage(&Ref) {} 33 34 operator T &() const { return *Storage; } 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. 67template<class T> 68class 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 73public: 74 typedef typename std::conditional<isRef, wrap, T>::type storage_type; 75 76private: 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 82public: 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) 88 : HasError(true) { 89 new (getErrorStorage()) std::error_code(make_error_code(ErrorCode)); 90 } 91 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) 100 : HasError(false) { 101 new (getStorage()) storage_type(std::forward<OtherT>(Val)); 102 } 103 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 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 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 166 reference get() { return *getStorage(); } 167 const_reference get() const { return const_cast<ErrorOr<T> *>(this)->get(); } 168 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 185private: 186 template <class OtherT> 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> 200 static bool compareThisIfSameType(const T1 &a, const T1 &b) { 201 return &a == &b; 202 } 203 204 template <class T1, class T2> 205 static bool compareThisIfSameType(const T1 &a, const T2 &b) { 206 return false; 207 } 208 209 template <class OtherT> 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> 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> 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 240 pointer toPointer(pointer Val) { 241 return Val; 242 } 243 244 const_pointer toPointer(const_pointer Val) const { return Val; } 245 246 pointer toPointer(wrap *Val) { 247 return &Val->get(); 248 } 249 250 const_pointer toPointer(const wrap *Val) const { return &Val->get(); } 251 252 storage_type *getStorage() { 253 assert(!HasError && "Cannot get value when an error exists!"); 254 return reinterpret_cast<storage_type*>(TStorage.buffer); 255 } 256 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 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 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 278template <class T, class E> 279typename std::enable_if<std::is_error_code_enum<E>::value || 280 std::is_error_condition_enum<E>::value, 281 bool>::type 282operator==(const ErrorOr<T> &Err, E Code) { 283 return Err.getError() == Code; 284} 285} // end namespace llvm 286 287#endif // LLVM_SUPPORT_ERROROR_H 288