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