summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael J. Spencer <bigcheesegs@gmail.com>2013-01-20 20:32:30 +0000
committerMichael J. Spencer <bigcheesegs@gmail.com>2013-01-20 20:32:30 +0000
commit01812bebcc345b09ce261317b6fdefde8f097642 (patch)
treef49ada585f33eb34cea9a3ea266d95099f8ea30f
parent5ff7a3f947c245df9ae95a381ef38184527e83e1 (diff)
downloadllvm-01812bebcc345b09ce261317b6fdefde8f097642.tar.gz
llvm-01812bebcc345b09ce261317b6fdefde8f097642.tar.bz2
llvm-01812bebcc345b09ce261317b6fdefde8f097642.tar.xz
[Support] Port ErrorOr<T> from lld to C++03.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@172991 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/llvm/Support/Compiler.h27
-rw-r--r--include/llvm/Support/ErrorOr.h342
-rw-r--r--include/llvm/Support/type_traits.h4
-rw-r--r--unittests/Support/CMakeLists.txt1
-rw-r--r--unittests/Support/ErrorOrTest.cpp78
5 files changed, 452 insertions, 0 deletions
diff --git a/include/llvm/Support/Compiler.h b/include/llvm/Support/Compiler.h
index ebc1fadc14..2d49d4caf4 100644
--- a/include/llvm/Support/Compiler.h
+++ b/include/llvm/Support/Compiler.h
@@ -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
diff --git a/include/llvm/Support/ErrorOr.h b/include/llvm/Support/ErrorOr.h
new file mode 100644
index 0000000000..f74ff2166c
--- /dev/null
+++ b/include/llvm/Support/ErrorOr.h
@@ -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
diff --git a/include/llvm/Support/type_traits.h b/include/llvm/Support/type_traits.h
index f9306395fc..db43ccfece 100644
--- a/include/llvm/Support/type_traits.h
+++ b/include/llvm/Support/type_traits.h
@@ -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.
///
diff --git a/unittests/Support/CMakeLists.txt b/unittests/Support/CMakeLists.txt
index dd42585b06..b4b982f2ef 100644
--- a/unittests/Support/CMakeLists.txt
+++ b/unittests/Support/CMakeLists.txt
@@ -13,6 +13,7 @@ add_llvm_unittest(SupportTests
ConstantRangeTest.cpp
DataExtractorTest.cpp
EndianTest.cpp
+ ErrorOrTest.cpp
FileOutputBufferTest.cpp
IntegersSubsetTest.cpp
LeakDetectorTest.cpp
diff --git a/unittests/Support/ErrorOrTest.cpp b/unittests/Support/ErrorOrTest.cpp
new file mode 100644
index 0000000000..1f80aa0cdf
--- /dev/null
+++ b/unittests/Support/ErrorOrTest.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