mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-12-13 04:30:23 +00:00
780e9004c8
Thanks to Andrew, David, and Aaron for helping fix this. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@174552 91177308-0d34-0410-b5e6-96231b3b80d8
489 lines
12 KiB
C++
489 lines
12 KiB
C++
//===- llvm/Support/ErrorOr.h - Error Smart Pointer -----------------------===//
|
|
//
|
|
// The LLVM Linker
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// \file
|
|
///
|
|
/// Provides ErrorOr<T> smart pointer.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLVM_SUPPORT_ERROR_OR_H
|
|
#define LLVM_SUPPORT_ERROR_OR_H
|
|
|
|
#include "llvm/ADT/PointerIntPair.h"
|
|
#include "llvm/Support/AlignOf.h"
|
|
#include "llvm/Support/system_error.h"
|
|
#include "llvm/Support/type_traits.h"
|
|
|
|
#include <cassert>
|
|
#if LLVM_HAS_CXX11_TYPETRAITS
|
|
#include <type_traits>
|
|
#endif
|
|
|
|
namespace llvm {
|
|
struct ErrorHolderBase {
|
|
error_code Error;
|
|
uint16_t RefCount;
|
|
bool HasUserData;
|
|
|
|
ErrorHolderBase() : RefCount(1) {}
|
|
|
|
void aquire() {
|
|
++RefCount;
|
|
}
|
|
|
|
void release() {
|
|
if (--RefCount == 0)
|
|
delete this;
|
|
}
|
|
|
|
protected:
|
|
virtual ~ErrorHolderBase() {}
|
|
};
|
|
|
|
template<class T>
|
|
struct ErrorHolder : ErrorHolderBase {
|
|
#if LLVM_HAS_RVALUE_REFERENCES
|
|
ErrorHolder(T &&UD) : UserData(llvm_move(UD)) {}
|
|
#else
|
|
ErrorHolder(T &UD) : UserData(UD) {}
|
|
#endif
|
|
T UserData;
|
|
};
|
|
|
|
template<class Tp> struct ErrorOrUserDataTraits : llvm::false_type {};
|
|
|
|
#if LLVM_HAS_CXX11_TYPETRAITS && LLVM_HAS_RVALUE_REFERENCES
|
|
template<class T, class V>
|
|
typename std::enable_if< std::is_constructible<T, V>::value
|
|
, typename std::remove_reference<V>::type>::type &&
|
|
moveIfMoveConstructible(V &Val) {
|
|
return std::move(Val);
|
|
}
|
|
|
|
template<class T, class V>
|
|
typename std::enable_if< !std::is_constructible<T, V>::value
|
|
, typename std::remove_reference<V>::type>::type &
|
|
moveIfMoveConstructible(V &Val) {
|
|
return Val;
|
|
}
|
|
#else
|
|
template<class T, class V>
|
|
V &moveIfMoveConstructible(V &Val) {
|
|
return Val;
|
|
}
|
|
#endif
|
|
|
|
/// \brief Stores a reference that can be changed.
|
|
template <typename T>
|
|
class ReferenceStorage {
|
|
T *Storage;
|
|
|
|
public:
|
|
ReferenceStorage(T &Ref) : Storage(&Ref) {}
|
|
|
|
operator T &() const { return *Storage; }
|
|
T &get() const { return *Storage; }
|
|
};
|
|
|
|
/// \brief Represents either an error or a value T.
|
|
///
|
|
/// ErrorOr<T> is a pointer-like class that represents the result of an
|
|
/// operation. The result is either an error, or a value of type T. This is
|
|
/// designed to emulate the usage of returning a pointer where nullptr indicates
|
|
/// failure. However instead of just knowing that the operation failed, we also
|
|
/// have an error_code and optional user data that describes why it failed.
|
|
///
|
|
/// It is used like the following.
|
|
/// \code
|
|
/// ErrorOr<Buffer> getBuffer();
|
|
/// void handleError(error_code ec);
|
|
///
|
|
/// auto buffer = getBuffer();
|
|
/// if (!buffer)
|
|
/// handleError(buffer);
|
|
/// buffer->write("adena");
|
|
/// \endcode
|
|
///
|
|
/// ErrorOr<T> also supports user defined data for specific error_codes. To use
|
|
/// this feature you must first add a template specialization of
|
|
/// ErrorOrUserDataTraits derived from std::true_type for your type in the lld
|
|
/// namespace. This specialization must have a static error_code error()
|
|
/// function that returns the error_code this data is used with.
|
|
///
|
|
/// getError<UserData>() may be called to get either the stored user data, or
|
|
/// a default constructed UserData if none was stored.
|
|
///
|
|
/// Example:
|
|
/// \code
|
|
/// struct InvalidArgError {
|
|
/// InvalidArgError() {}
|
|
/// InvalidArgError(std::string S) : ArgName(S) {}
|
|
/// std::string ArgName;
|
|
/// };
|
|
///
|
|
/// namespace llvm {
|
|
/// template<>
|
|
/// struct ErrorOrUserDataTraits<InvalidArgError> : std::true_type {
|
|
/// static error_code error() {
|
|
/// return make_error_code(errc::invalid_argument);
|
|
/// }
|
|
/// };
|
|
/// } // end namespace llvm
|
|
///
|
|
/// using namespace llvm;
|
|
///
|
|
/// ErrorOr<int> foo() {
|
|
/// return InvalidArgError("adena");
|
|
/// }
|
|
///
|
|
/// int main() {
|
|
/// auto a = foo();
|
|
/// if (!a && error_code(a) == errc::invalid_argument)
|
|
/// llvm::errs() << a.getError<InvalidArgError>().ArgName << "\n";
|
|
/// }
|
|
/// \endcode
|
|
///
|
|
/// An implicit conversion to bool provides a way to check if there was an
|
|
/// error. The unary * and -> operators provide pointer like access to the
|
|
/// value. Accessing the value when there is an error has undefined behavior.
|
|
///
|
|
/// When T is a reference type the behaivor is slightly different. The reference
|
|
/// is held in a std::reference_wrapper<std::remove_reference<T>::type>, and
|
|
/// there is special handling to make operator -> work as if T was not a
|
|
/// reference.
|
|
///
|
|
/// T cannot be a rvalue reference.
|
|
template<class T>
|
|
class ErrorOr {
|
|
template <class OtherT> friend class ErrorOr;
|
|
static const bool isRef = is_reference<T>::value;
|
|
typedef ReferenceStorage<typename remove_reference<T>::type> wrap;
|
|
|
|
public:
|
|
typedef typename
|
|
conditional< isRef
|
|
, wrap
|
|
, T
|
|
>::type storage_type;
|
|
|
|
private:
|
|
typedef typename remove_reference<T>::type &reference;
|
|
typedef typename remove_reference<T>::type *pointer;
|
|
|
|
public:
|
|
ErrorOr() : IsValid(false) {}
|
|
|
|
ErrorOr(llvm::error_code EC) : HasError(true), IsValid(true) {
|
|
Error = new ErrorHolderBase;
|
|
Error->Error = EC;
|
|
Error->HasUserData = false;
|
|
}
|
|
|
|
template<class UserDataT>
|
|
ErrorOr(UserDataT UD, typename
|
|
enable_if_c<ErrorOrUserDataTraits<UserDataT>::value>::type* = 0)
|
|
: HasError(true), IsValid(true) {
|
|
Error = new ErrorHolder<UserDataT>(llvm_move(UD));
|
|
Error->Error = ErrorOrUserDataTraits<UserDataT>::error();
|
|
Error->HasUserData = true;
|
|
}
|
|
|
|
ErrorOr(T Val) : HasError(false), IsValid(true) {
|
|
new (get()) storage_type(moveIfMoveConstructible<storage_type>(Val));
|
|
}
|
|
|
|
ErrorOr(const ErrorOr &Other) : IsValid(false) {
|
|
copyConstruct(Other);
|
|
}
|
|
|
|
template <class OtherT>
|
|
ErrorOr(const ErrorOr<OtherT> &Other) : IsValid(false) {
|
|
copyConstruct(Other);
|
|
}
|
|
|
|
ErrorOr &operator =(const ErrorOr &Other) {
|
|
copyAssign(Other);
|
|
return *this;
|
|
}
|
|
|
|
template <class OtherT>
|
|
ErrorOr &operator =(const ErrorOr<OtherT> &Other) {
|
|
copyAssign(Other);
|
|
return *this;
|
|
}
|
|
|
|
#if LLVM_HAS_RVALUE_REFERENCES
|
|
ErrorOr(ErrorOr &&Other) : IsValid(false) {
|
|
moveConstruct(std::move(Other));
|
|
}
|
|
|
|
template <class OtherT>
|
|
ErrorOr(ErrorOr<OtherT> &&Other) : IsValid(false) {
|
|
moveConstruct(std::move(Other));
|
|
}
|
|
|
|
ErrorOr &operator =(ErrorOr &&Other) {
|
|
moveAssign(std::move(Other));
|
|
return *this;
|
|
}
|
|
|
|
template <class OtherT>
|
|
ErrorOr &operator =(ErrorOr<OtherT> &&Other) {
|
|
moveAssign(std::move(Other));
|
|
return *this;
|
|
}
|
|
#endif
|
|
|
|
~ErrorOr() {
|
|
if (!IsValid)
|
|
return;
|
|
if (HasError)
|
|
Error->release();
|
|
else
|
|
get()->~storage_type();
|
|
}
|
|
|
|
template<class ET>
|
|
ET getError() const {
|
|
assert(IsValid && "Cannot get the error of a default constructed ErrorOr!");
|
|
assert(HasError && "Cannot get an error if none exists!");
|
|
assert(ErrorOrUserDataTraits<ET>::error() == Error->Error &&
|
|
"Incorrect user error data type for error!");
|
|
if (!Error->HasUserData)
|
|
return ET();
|
|
return reinterpret_cast<const ErrorHolder<ET>*>(Error)->UserData;
|
|
}
|
|
|
|
typedef void (*unspecified_bool_type)();
|
|
static void unspecified_bool_true() {}
|
|
|
|
/// \brief Return false if there is an error.
|
|
operator unspecified_bool_type() const {
|
|
assert(IsValid && "Can't do anything on a default constructed ErrorOr!");
|
|
return HasError ? 0 : unspecified_bool_true;
|
|
}
|
|
|
|
operator llvm::error_code() const {
|
|
assert(IsValid && "Can't do anything on a default constructed ErrorOr!");
|
|
return HasError ? Error->Error : llvm::error_code::success();
|
|
}
|
|
|
|
pointer operator ->() {
|
|
return toPointer(get());
|
|
}
|
|
|
|
reference operator *() {
|
|
return *get();
|
|
}
|
|
|
|
private:
|
|
template <class OtherT>
|
|
void copyConstruct(const ErrorOr<OtherT> &Other) {
|
|
// Construct an invalid ErrorOr if other is invalid.
|
|
if (!Other.IsValid)
|
|
return;
|
|
IsValid = true;
|
|
if (!Other.HasError) {
|
|
// Get the other value.
|
|
HasError = false;
|
|
new (get()) storage_type(*Other.get());
|
|
} else {
|
|
// Get other's error.
|
|
Error = Other.Error;
|
|
HasError = true;
|
|
Error->aquire();
|
|
}
|
|
}
|
|
|
|
template <class T1>
|
|
static bool compareThisIfSameType(const T1 &a, const T1 &b) {
|
|
return &a == &b;
|
|
}
|
|
|
|
template <class T1, class T2>
|
|
static bool compareThisIfSameType(const T1 &a, const T2 &b) {
|
|
return false;
|
|
}
|
|
|
|
template <class OtherT>
|
|
void copyAssign(const ErrorOr<OtherT> &Other) {
|
|
if (compareThisIfSameType(*this, Other))
|
|
return;
|
|
|
|
this->~ErrorOr();
|
|
new (this) ErrorOr(Other);
|
|
}
|
|
|
|
#if LLVM_HAS_RVALUE_REFERENCES
|
|
template <class OtherT>
|
|
void moveConstruct(ErrorOr<OtherT> &&Other) {
|
|
// Construct an invalid ErrorOr if other is invalid.
|
|
if (!Other.IsValid)
|
|
return;
|
|
IsValid = true;
|
|
if (!Other.HasError) {
|
|
// Get the other value.
|
|
HasError = false;
|
|
new (get()) storage_type(std::move(*Other.get()));
|
|
// Tell other not to do any destruction.
|
|
Other.IsValid = false;
|
|
} else {
|
|
// Get other's error.
|
|
Error = Other.Error;
|
|
HasError = true;
|
|
// Tell other not to do any destruction.
|
|
Other.IsValid = false;
|
|
}
|
|
}
|
|
|
|
template <class OtherT>
|
|
void moveAssign(ErrorOr<OtherT> &&Other) {
|
|
if (compareThisIfSameType(*this, Other))
|
|
return;
|
|
|
|
this->~ErrorOr();
|
|
new (this) ErrorOr(std::move(Other));
|
|
}
|
|
#endif
|
|
|
|
pointer toPointer(pointer Val) {
|
|
return Val;
|
|
}
|
|
|
|
pointer toPointer(wrap *Val) {
|
|
return &Val->get();
|
|
}
|
|
|
|
storage_type *get() {
|
|
assert(IsValid && "Can't do anything on a default constructed ErrorOr!");
|
|
assert(!HasError && "Cannot get value when an error exists!");
|
|
return reinterpret_cast<storage_type*>(TStorage.buffer);
|
|
}
|
|
|
|
const storage_type *get() const {
|
|
assert(IsValid && "Can't do anything on a default constructed ErrorOr!");
|
|
assert(!HasError && "Cannot get value when an error exists!");
|
|
return reinterpret_cast<const storage_type*>(TStorage.buffer);
|
|
}
|
|
|
|
union {
|
|
AlignedCharArrayUnion<storage_type> TStorage;
|
|
ErrorHolderBase *Error;
|
|
};
|
|
bool HasError : 1;
|
|
bool IsValid : 1;
|
|
};
|
|
|
|
// ErrorOr specialization for void.
|
|
template <>
|
|
class ErrorOr<void> {
|
|
public:
|
|
ErrorOr() : Error(0, 0) {}
|
|
|
|
ErrorOr(llvm::error_code EC) : Error(0, 0) {
|
|
if (EC == errc::success) {
|
|
Error.setInt(1);
|
|
return;
|
|
}
|
|
ErrorHolderBase *E = new ErrorHolderBase;
|
|
E->Error = EC;
|
|
E->HasUserData = false;
|
|
Error.setPointer(E);
|
|
}
|
|
|
|
template<class UserDataT>
|
|
ErrorOr(UserDataT UD, typename
|
|
enable_if_c<ErrorOrUserDataTraits<UserDataT>::value>::type* = 0)
|
|
: Error(0, 0) {
|
|
ErrorHolderBase *E = new ErrorHolder<UserDataT>(llvm_move(UD));
|
|
E->Error = ErrorOrUserDataTraits<UserDataT>::error();
|
|
E->HasUserData = true;
|
|
Error.setPointer(E);
|
|
}
|
|
|
|
ErrorOr(const ErrorOr &Other) : Error(0, 0) {
|
|
Error = Other.Error;
|
|
if (Other.Error.getPointer()->Error) {
|
|
Error.getPointer()->aquire();
|
|
}
|
|
}
|
|
|
|
ErrorOr &operator =(const ErrorOr &Other) {
|
|
if (this == &Other)
|
|
return *this;
|
|
|
|
this->~ErrorOr();
|
|
new (this) ErrorOr(Other);
|
|
|
|
return *this;
|
|
}
|
|
|
|
#if LLVM_HAS_RVALUE_REFERENCES
|
|
ErrorOr(ErrorOr &&Other) : Error(0) {
|
|
// Get other's error.
|
|
Error = Other.Error;
|
|
// Tell other not to do any destruction.
|
|
Other.Error.setPointer(0);
|
|
}
|
|
|
|
ErrorOr &operator =(ErrorOr &&Other) {
|
|
if (this == &Other)
|
|
return *this;
|
|
|
|
this->~ErrorOr();
|
|
new (this) ErrorOr(std::move(Other));
|
|
|
|
return *this;
|
|
}
|
|
#endif
|
|
|
|
~ErrorOr() {
|
|
if (Error.getPointer())
|
|
Error.getPointer()->release();
|
|
}
|
|
|
|
template<class ET>
|
|
ET getError() const {
|
|
assert(ErrorOrUserDataTraits<ET>::error() == *this &&
|
|
"Incorrect user error data type for error!");
|
|
if (!Error.getPointer()->HasUserData)
|
|
return ET();
|
|
return reinterpret_cast<const ErrorHolder<ET> *>(
|
|
Error.getPointer())->UserData;
|
|
}
|
|
|
|
typedef void (*unspecified_bool_type)();
|
|
static void unspecified_bool_true() {}
|
|
|
|
/// \brief Return false if there is an error.
|
|
operator unspecified_bool_type() const {
|
|
return Error.getInt() ? unspecified_bool_true : 0;
|
|
}
|
|
|
|
operator llvm::error_code() const {
|
|
return Error.getInt() ? make_error_code(errc::success)
|
|
: Error.getPointer()->Error;
|
|
}
|
|
|
|
private:
|
|
// If the bit is 1, the error is success.
|
|
llvm::PointerIntPair<ErrorHolderBase *, 1> Error;
|
|
};
|
|
|
|
template<class T, class E>
|
|
typename enable_if_c<is_error_code_enum<E>::value ||
|
|
is_error_condition_enum<E>::value, bool>::type
|
|
operator ==(ErrorOr<T> &Err, E Code) {
|
|
return error_code(Err) == Code;
|
|
}
|
|
} // end namespace llvm
|
|
|
|
#endif
|