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 24namespace 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. 55template<class T> 56class 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 63public: 64 using storage_type = typename std::conditional<isRef, wrap, T>::type; 65 66private: 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 72public: 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 175private: 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 268template <class T, class E> 269typename std::enable_if<std::is_error_code_enum<E>::value || 270 std::is_error_condition_enum<E>::value, 271 bool>::type 272operator==(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