forked from OSchip/llvm-project
[Support] Port ErrorOr<T> from lld to C++03.
llvm-svn: 172991
This commit is contained in:
parent
a6e2e2a0a7
commit
779c4247f7
|
@ -42,6 +42,33 @@
|
|||
#define LLVM_HAS_RVALUE_REFERENCE_THIS 0
|
||||
#endif
|
||||
|
||||
/// \macro LLVM_HAS_CXX11_TYPETRAITS
|
||||
/// \brief Does the compiler have the C++11 type traits.
|
||||
///
|
||||
/// #include <type_traits>
|
||||
///
|
||||
/// * enable_if
|
||||
/// * {true,false}_type
|
||||
/// * is_constructible
|
||||
/// * etc...
|
||||
#if defined(__GXX_EXPERIMENTAL_CXX0X__) \
|
||||
|| (defined(_MSC_VER) && _MSC_VER >= 1600)
|
||||
#define LLVM_HAS_CXX11_TYPETRAITS 1
|
||||
#else
|
||||
#define LLVM_HAS_CXX11_TYPETRAITS 0
|
||||
#endif
|
||||
|
||||
/// \macro LLVM_HAS_CXX11_STDLIB
|
||||
/// \brief Does the compiler have the C++11 standard library.
|
||||
///
|
||||
/// Implies LLVM_HAS_RVALUE_REFERENCES, LLVM_HAS_CXX11_TYPETRAITS
|
||||
#if defined(__GXX_EXPERIMENTAL_CXX0X__) \
|
||||
|| (defined(_MSC_VER) && _MSC_VER >= 1600)
|
||||
#define LLVM_HAS_CXX11_STDLIB 1
|
||||
#else
|
||||
#define LLVM_HAS_CXX11_STDLIB 0
|
||||
#endif
|
||||
|
||||
/// llvm_move - Expands to ::std::move if the compiler supports
|
||||
/// r-value references; otherwise, expands to the argument.
|
||||
#if LLVM_HAS_RVALUE_REFERENCES
|
||||
|
|
|
@ -0,0 +1,342 @@
|
|||
//===- 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/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 {
|
||||
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 T &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) {
|
||||
// Construct an invalid ErrorOr if other is invalid.
|
||||
if (!Other.IsValid)
|
||||
return;
|
||||
if (!Other.HasError) {
|
||||
// Get the other value.
|
||||
new (get()) storage_type(*Other.get());
|
||||
HasError = false;
|
||||
} else {
|
||||
// Get other's error.
|
||||
Error = Other.Error;
|
||||
HasError = true;
|
||||
Error->aquire();
|
||||
}
|
||||
|
||||
IsValid = true;
|
||||
}
|
||||
|
||||
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) : IsValid(false) {
|
||||
// Construct an invalid ErrorOr if other is invalid.
|
||||
if (!Other.IsValid)
|
||||
return;
|
||||
if (!Other.HasError) {
|
||||
// Get the other value.
|
||||
IsValid = true;
|
||||
new (get()) storage_type(std::move(*Other.get()));
|
||||
HasError = false;
|
||||
// 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;
|
||||
}
|
||||
|
||||
IsValid = true;
|
||||
}
|
||||
|
||||
ErrorOr &operator =(ErrorOr &&Other) {
|
||||
if (this == &Other)
|
||||
return *this;
|
||||
|
||||
this->~ErrorOr();
|
||||
new (this) ErrorOr(std::move(Other));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
~ErrorOr() {
|
||||
if (!IsValid)
|
||||
return;
|
||||
if (HasError)
|
||||
Error->release();
|
||||
else
|
||||
get()->~storage_type();
|
||||
}
|
||||
#endif
|
||||
|
||||
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:
|
||||
pointer toPointer(pointer Val) {
|
||||
return Val;
|
||||
}
|
||||
|
||||
pointer toPointer(wrap *Val) {
|
||||
return &Val->get();
|
||||
}
|
||||
|
||||
protected:
|
||||
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;
|
||||
};
|
||||
|
||||
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
|
|
@ -145,6 +145,10 @@ template <typename T> struct is_pointer<T* const> : true_type {};
|
|||
template <typename T> struct is_pointer<T* volatile> : true_type {};
|
||||
template <typename T> struct is_pointer<T* const volatile> : true_type {};
|
||||
|
||||
/// \brief Metafunction that determines wheather the given type is a reference.
|
||||
template <typename T> struct is_reference : false_type {};
|
||||
template <typename T> struct is_reference<T&> : true_type {};
|
||||
|
||||
/// \brief Metafunction that determines whether the given type is either an
|
||||
/// integral type or an enumeration type.
|
||||
///
|
||||
|
|
|
@ -13,6 +13,7 @@ add_llvm_unittest(SupportTests
|
|||
ConstantRangeTest.cpp
|
||||
DataExtractorTest.cpp
|
||||
EndianTest.cpp
|
||||
ErrorOrTest.cpp
|
||||
FileOutputBufferTest.cpp
|
||||
IntegersSubsetTest.cpp
|
||||
LeakDetectorTest.cpp
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
//===- unittests/ErrorOrTest.cpp - ErrorOr.h tests ------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/Support/ErrorOr.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
|
||||
ErrorOr<int> t1() {return 1;}
|
||||
ErrorOr<int> t2() {return make_error_code(errc::invalid_argument);}
|
||||
|
||||
TEST(ErrorOr, SimpleValue) {
|
||||
ErrorOr<int> a = t1();
|
||||
EXPECT_TRUE(a);
|
||||
EXPECT_EQ(1, *a);
|
||||
|
||||
a = t2();
|
||||
EXPECT_FALSE(a);
|
||||
EXPECT_EQ(errc::invalid_argument, a);
|
||||
EXPECT_DEBUG_DEATH(*a, "Cannot get value when an error exists");
|
||||
}
|
||||
|
||||
#if LLVM_HAS_CXX11_STDLIB
|
||||
ErrorOr<std::unique_ptr<int> > t3() {
|
||||
return std::unique_ptr<int>(new int(3));
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(ErrorOr, Types) {
|
||||
int x;
|
||||
ErrorOr<int&> a(x);
|
||||
*a = 42;
|
||||
EXPECT_EQ(42, x);
|
||||
|
||||
#if LLVM_HAS_CXX11_STDLIB
|
||||
// Move only types.
|
||||
EXPECT_EQ(3, **t3());
|
||||
#endif
|
||||
}
|
||||
} // end anon namespace
|
||||
|
||||
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 lld
|
||||
|
||||
ErrorOr<int> t4() {
|
||||
return InvalidArgError("adena");
|
||||
}
|
||||
|
||||
namespace {
|
||||
TEST(ErrorOr, UserErrorData) {
|
||||
ErrorOr<int> a = t4();
|
||||
EXPECT_EQ(errc::invalid_argument, a);
|
||||
EXPECT_EQ("adena", t4().getError<InvalidArgError>().ArgName);
|
||||
}
|
||||
} // end anon namespace
|
Loading…
Reference in New Issue