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 constexpr bool isRef = std::is_reference_v<T>; 60 61 using wrap = std::reference_wrapper<std::remove_reference_t<T>>; 62 63 public: 64 using storage_type = std::conditional_t<isRef, wrap, T>; 65 66 private: 67 using reference = std::remove_reference_t<T> &; 68 using const_reference = const std::remove_reference_t<T> &; 69 using pointer = std::remove_reference_t<T> *; 70 using const_pointer = const std::remove_reference_t<T> *; 71 72 public: 73 template <class E> 74 ErrorOr(E ErrorCode, 75 std::enable_if_t<std::is_error_code_enum<E>::value || 76 std::is_error_condition_enum<E>::value, 77 void *> = nullptr) HasError(true)78 : HasError(true) { 79 new (getErrorStorage()) std::error_code(make_error_code(ErrorCode)); 80 } 81 ErrorOr(std::error_code EC)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 std::enable_if_t<std::is_convertible_v<OtherT, T>> * = nullptr) HasError(false)89 : HasError(false) { 90 new (getStorage()) storage_type(std::forward<OtherT>(Val)); 91 } 92 ErrorOr(const ErrorOr & Other)93 ErrorOr(const ErrorOr &Other) { 94 copyConstruct(Other); 95 } 96 97 template <class OtherT> 98 ErrorOr(const ErrorOr<OtherT> &Other, 99 std::enable_if_t<std::is_convertible_v<OtherT, T>> * = nullptr) { 100 copyConstruct(Other); 101 } 102 103 template <class OtherT> 104 explicit ErrorOr( 105 const ErrorOr<OtherT> &Other, 106 std::enable_if_t<!std::is_convertible_v<OtherT, const T &>> * = nullptr) { 107 copyConstruct(Other); 108 } 109 ErrorOr(ErrorOr && Other)110 ErrorOr(ErrorOr &&Other) { 111 moveConstruct(std::move(Other)); 112 } 113 114 template <class OtherT> 115 ErrorOr(ErrorOr<OtherT> &&Other, 116 std::enable_if_t<std::is_convertible_v<OtherT, T>> * = nullptr) { 117 moveConstruct(std::move(Other)); 118 } 119 120 // This might eventually need SFINAE but it's more complex than is_convertible 121 // & I'm too lazy to write it right now. 122 template <class OtherT> 123 explicit ErrorOr( 124 ErrorOr<OtherT> &&Other, 125 std::enable_if_t<!std::is_convertible_v<OtherT, T>> * = nullptr) { 126 moveConstruct(std::move(Other)); 127 } 128 129 ErrorOr &operator=(const ErrorOr &Other) { 130 copyAssign(Other); 131 return *this; 132 } 133 134 ErrorOr &operator=(ErrorOr &&Other) { 135 moveAssign(std::move(Other)); 136 return *this; 137 } 138 ~ErrorOr()139 ~ErrorOr() { 140 if (!HasError) 141 getStorage()->~storage_type(); 142 } 143 144 /// Return false if there is an error. 145 explicit operator bool() const { 146 return !HasError; 147 } 148 get()149 reference get() { return *getStorage(); } get()150 const_reference get() const { return const_cast<ErrorOr<T> *>(this)->get(); } 151 getError()152 std::error_code getError() const { 153 return HasError ? *getErrorStorage() : std::error_code(); 154 } 155 156 pointer operator ->() { 157 return toPointer(getStorage()); 158 } 159 160 const_pointer operator->() const { return toPointer(getStorage()); } 161 162 reference operator *() { 163 return *getStorage(); 164 } 165 166 const_reference operator*() const { return *getStorage(); } 167 168 private: 169 template <class OtherT> copyConstruct(const ErrorOr<OtherT> & Other)170 void copyConstruct(const ErrorOr<OtherT> &Other) { 171 if (!Other.HasError) { 172 // Get the other value. 173 HasError = false; 174 new (getStorage()) storage_type(*Other.getStorage()); 175 } else { 176 // Get other's error. 177 HasError = true; 178 new (getErrorStorage()) std::error_code(Other.getError()); 179 } 180 } 181 182 template <class T1> compareThisIfSameType(const T1 & a,const T1 & b)183 static bool compareThisIfSameType(const T1 &a, const T1 &b) { 184 return &a == &b; 185 } 186 187 template <class T1, class T2> compareThisIfSameType(const T1 & a,const T2 & b)188 static bool compareThisIfSameType(const T1 &a, const T2 &b) { 189 return false; 190 } 191 192 template <class OtherT> copyAssign(const ErrorOr<OtherT> & Other)193 void copyAssign(const ErrorOr<OtherT> &Other) { 194 if (compareThisIfSameType(*this, Other)) 195 return; 196 197 this->~ErrorOr(); 198 new (this) ErrorOr(Other); 199 } 200 201 template <class OtherT> moveConstruct(ErrorOr<OtherT> && Other)202 void moveConstruct(ErrorOr<OtherT> &&Other) { 203 if (!Other.HasError) { 204 // Get the other value. 205 HasError = false; 206 new (getStorage()) storage_type(std::move(*Other.getStorage())); 207 } else { 208 // Get other's error. 209 HasError = true; 210 new (getErrorStorage()) std::error_code(Other.getError()); 211 } 212 } 213 214 template <class OtherT> moveAssign(ErrorOr<OtherT> && Other)215 void moveAssign(ErrorOr<OtherT> &&Other) { 216 if (compareThisIfSameType(*this, Other)) 217 return; 218 219 this->~ErrorOr(); 220 new (this) ErrorOr(std::move(Other)); 221 } 222 toPointer(pointer Val)223 pointer toPointer(pointer Val) { 224 return Val; 225 } 226 toPointer(const_pointer Val)227 const_pointer toPointer(const_pointer Val) const { return Val; } 228 toPointer(wrap * Val)229 pointer toPointer(wrap *Val) { 230 return &Val->get(); 231 } 232 toPointer(const wrap * Val)233 const_pointer toPointer(const wrap *Val) const { return &Val->get(); } 234 getStorage()235 storage_type *getStorage() { 236 assert(!HasError && "Cannot get value when an error exists!"); 237 return reinterpret_cast<storage_type *>(&TStorage); 238 } 239 getStorage()240 const storage_type *getStorage() const { 241 assert(!HasError && "Cannot get value when an error exists!"); 242 return reinterpret_cast<const storage_type *>(&TStorage); 243 } 244 getErrorStorage()245 std::error_code *getErrorStorage() { 246 assert(HasError && "Cannot get error when a value exists!"); 247 return reinterpret_cast<std::error_code *>(&ErrorStorage); 248 } 249 getErrorStorage()250 const std::error_code *getErrorStorage() const { 251 return const_cast<ErrorOr<T> *>(this)->getErrorStorage(); 252 } 253 254 union { 255 AlignedCharArrayUnion<storage_type> TStorage; 256 AlignedCharArrayUnion<std::error_code> ErrorStorage; 257 }; 258 bool HasError : 1; 259 }; 260 261 template <class T, class E> 262 std::enable_if_t<std::is_error_code_enum<E>::value || 263 std::is_error_condition_enum<E>::value, 264 bool> 265 operator==(const ErrorOr<T> &Err, E Code) { 266 return Err.getError() == Code; 267 } 268 269 } // end namespace llvm 270 271 #endif // LLVM_SUPPORT_ERROROR_H 272