[llvm-commits] [lld] r170483 - in /lld/trunk: CMakeLists.txt include/lld/Core/ErrorOr.h unittests/ unittests/CMakeLists.txt unittests/ErrorOrTest.cpp
Michael J. Spencer
bigcheesegs at gmail.com
Tue Dec 18 16:51:07 PST 2012
Author: mspencer
Date: Tue Dec 18 18:51:07 2012
New Revision: 170483
URL: http://llvm.org/viewvc/llvm-project?rev=170483&view=rev
Log:
[Core][ErrorOr] Add support for user error data.
Added:
lld/trunk/unittests/
lld/trunk/unittests/CMakeLists.txt
lld/trunk/unittests/ErrorOrTest.cpp
Modified:
lld/trunk/CMakeLists.txt
lld/trunk/include/lld/Core/ErrorOr.h
Modified: lld/trunk/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/CMakeLists.txt?rev=170483&r1=170482&r2=170483&view=diff
==============================================================================
--- lld/trunk/CMakeLists.txt (original)
+++ lld/trunk/CMakeLists.txt Tue Dec 18 18:51:07 2012
@@ -144,3 +144,7 @@
add_subdirectory(tools)
add_subdirectory(test)
+
+if (LLVM_INCLUDE_TESTS AND NOT LLD_BUILT_STANDALONE)
+ add_subdirectory(unittests)
+endif()
Modified: lld/trunk/include/lld/Core/ErrorOr.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/include/lld/Core/ErrorOr.h?rev=170483&r1=170482&r2=170483&view=diff
==============================================================================
--- lld/trunk/include/lld/Core/ErrorOr.h (original)
+++ lld/trunk/include/lld/Core/ErrorOr.h Tue Dec 18 18:51:07 2012
@@ -21,88 +21,52 @@
#include "llvm/Support/AlignOf.h"
#include "llvm/Support/system_error.h"
+#include <atomic>
#include <cassert>
#include <type_traits>
namespace lld {
-template<class T>
-class ErrorOrBase {
- static const bool isRef = std::is_reference<T>::value;
- typedef std::reference_wrapper<typename std::remove_reference<T>::type> wrap;
-
-public:
- typedef typename
- std::conditional< isRef
- , wrap
- , T
- >::type storage_type;
-
-private:
- typedef T &reference;
- typedef typename std::remove_reference<T>::type *pointer;
-
- ErrorOrBase(const ErrorOrBase&) LLVM_DELETED_FUNCTION;
- ErrorOrBase &operator =(const ErrorOrBase&) LLVM_DELETED_FUNCTION;
- ErrorOrBase(ErrorOrBase &&other) LLVM_DELETED_FUNCTION;
- ErrorOrBase &operator =(ErrorOrBase &&other) LLVM_DELETED_FUNCTION;
+struct ErrorHolderBase {
+ llvm::error_code Error;
+ std::atomic<uint16_t> RefCount;
+ bool HasUserData;
-public:
- ErrorOrBase() : _error(llvm::make_error_code(llvm::errc::invalid_argument)) {}
-
- ErrorOrBase(llvm::error_code ec) {
- if (!_error)
- get()->~storage_type();
- _error = ec;
- }
+ ErrorHolderBase() : RefCount(1) {}
- ErrorOrBase(T t) : _error(llvm::error_code::success()) {
- new (get()) storage_type(t);
- }
-
- ~ErrorOrBase() {
- if (!_error)
- get()->~storage_type();
- }
-
- /// \brief Return false if there is an error.
- operator bool() {
- return !_error;
- }
-
- operator llvm::error_code() {
- return _error;
- }
-
- operator reference() {
- return *get();
+ void aquire() {
+ ++RefCount;
}
- pointer operator ->() {
- return toPointer(get());
+ void release() {
+ if (RefCount.fetch_sub(1) == 1)
+ delete this;
}
- reference operator *() {
- return *get();
- }
+protected:
+ virtual ~ErrorHolderBase() {}
+};
-private:
- pointer toPointer(pointer t) {
- return t;
- }
+template<class T>
+struct ErrorHolder : ErrorHolderBase {
+ ErrorHolder(T &&UD) : UserData(UD) {}
+ T UserData;
+};
- pointer toPointer(wrap *t) {
- return &t->get();
- }
+template<class Tp> struct ErrorOrUserDataTraits : std::false_type {};
-protected:
- storage_type *get() {
- assert(!_error && "T not valid!");
- return reinterpret_cast<storage_type*>(_t.buffer);
- }
+template<class T, class V>
+typename std::enable_if< std::is_constructible<T, V>::value
+ , typename std::remove_reference<V>::type>::type &&
+ moveIfMoveConstructible(V &t) {
+ return std::move(t);
+}
- llvm::error_code _error;
- llvm::AlignedCharArrayUnion<storage_type> _t;
-};
+template<class T, class V>
+typename std::enable_if< !std::is_constructible<T, V>::value
+ , typename std::remove_reference<V>::type>::type &
+moveIfMoveConstructible(V &t) {
+ return t;
+}
/// \brief Represents either an error or a value T.
///
@@ -110,7 +74,7 @@
/// 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 that describes why it failed.
+/// have an error_code and optional user data that describes why it failed.
///
/// It is used like the following.
/// \code
@@ -123,6 +87,45 @@
/// 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 lld {
+/// template<>
+/// struct ErrorOrUserDataTraits<InvalidArgError> : std::true_type {
+/// static error_code error() {
+/// return make_error_code(errc::invalid_argument);
+/// }
+/// };
+/// } // end namespace lld
+///
+/// using namespace lld;
+///
+/// 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.
@@ -133,81 +136,181 @@
/// reference.
///
/// T cannot be a rvalue reference.
-template<class T,
- bool isMoveable =
- std::is_move_constructible<typename ErrorOrBase<T>::storage_type>::value>
-class ErrorOr;
-
template<class T>
-class ErrorOr<T, true> : public ErrorOrBase<T> {
- ErrorOr(const ErrorOr &other) LLVM_DELETED_FUNCTION;
- ErrorOr &operator =(const ErrorOr &other) LLVM_DELETED_FUNCTION;
+class ErrorOr {
+ static const bool isRef = std::is_reference<T>::value;
+ typedef std::reference_wrapper<typename std::remove_reference<T>::type> wrap;
+
+public:
+ typedef typename
+ std::conditional< isRef
+ , wrap
+ , T
+ >::type storage_type;
+
+private:
+ typedef T &reference;
+ typedef typename std::remove_reference<T>::type *pointer;
+
public:
- ErrorOr(llvm::error_code ec) : ErrorOrBase<T>(ec) {}
- ErrorOr(T t) : ErrorOrBase<T>(t) {}
- ErrorOr(ErrorOr &&other) : ErrorOrBase<T>() {
- // Get the other value.
- if (!other._error)
- new (this->get())
- typename ErrorOrBase<T>::storage_type(std::move(*other.get()));
+ 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
+ std::enable_if<ErrorOrUserDataTraits<UserDataT>::value>::type* = 0)
+ : HasError(true), IsValid(true) {
+ Error = new ErrorHolder<UserDataT>(std::move(UD));
+ Error->Error = ErrorOrUserDataTraits<UserDataT>::error();
+ Error->HasUserData = true;
+ }
+
+ ErrorOr(T t) : HasError(false), IsValid(true) {
+ new (get()) storage_type(moveIfMoveConstructible<storage_type>(t));
+ }
+
+ 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;
- // Get the other error.
- this->_error = other._error;
+ this->~ErrorOr();
+ new (this) ErrorOr(other);
- // Make sure other doesn't try to delete its storage.
- other._error = llvm::make_error_code(llvm::errc::invalid_argument);
+ return *this;
+ }
+
+ ErrorOr(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(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) {
- // Delete any existing value.
- if (!this->_error)
- this->get()->~storage_type();
-
- // Get the other value.
- if (!other._error)
- new (this->get())
- typename ErrorOrBase<T>::storage_type(std::move(*other.get()));
+ if (this == &other)
+ return *this;
- // Get the other error.
- this->_error = other._error;
+ this->~ErrorOr();
+ new (this) ErrorOr(other);
- // Make sure other doesn't try to delete its storage.
- other._error = llvm::make_error_code(llvm::errc::invalid_argument);
+ return *this;
}
-};
-template<class T>
-class ErrorOr<T, false> : public ErrorOrBase<T> {
- static_assert(std::is_copy_constructible<T>::value,
- "T must be copy or move constructible!");
+ ~ErrorOr() {
+ if (!IsValid)
+ return;
+ if (HasError)
+ Error->release();
+ else
+ get()->~storage_type();
+ }
- ErrorOr(ErrorOr &&other) LLVM_DELETED_FUNCTION;
- ErrorOr &operator =(ErrorOr &&other) LLVM_DELETED_FUNCTION;
-public:
- ErrorOr(llvm::error_code ec) : ErrorOrBase<T>(ec) {}
- ErrorOr(T t) : ErrorOrBase<T>(t) {}
- ErrorOr(const ErrorOr &other) : ErrorOrBase<T>() {
- // Get the other value.
- if (!other._error)
- new (this->get()) typename ErrorOrBase<T>::storage_type(*other.get());
+ 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() {}
- // Get the other error.
- this->_error = other._error;
+ /// \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;
}
- ErrorOr &operator =(const ErrorOr &other) {
- // Delete any existing value.
- if (!this->_error)
- this->get()->~storage_type();
-
- // Get the other value.
- if (!other._error)
- new (this->get()) typename ErrorOrBase<T>::storage_type(*other.get());
+ operator llvm::error_code() const {
+ assert(IsValid && "Can't do anything on a default constructed ErrorOr!");
+ return HasError ? Error->Error : llvm::error_code::success();
+ }
- // Get the other error.
- this->_error = other._error;
+ pointer operator ->() {
+ return toPointer(get());
}
+
+ reference operator *() {
+ return *get();
+ }
+
+private:
+ pointer toPointer(pointer t) {
+ return t;
+ }
+
+ pointer toPointer(wrap *t) {
+ return &t->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*>(_t.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*>(_t.buffer);
+ }
+
+ union {
+ llvm::AlignedCharArrayUnion<storage_type> _t;
+ ErrorHolderBase *Error;
+ };
+ bool HasError : 1;
+ bool IsValid : 1;
};
+
+template<class T, class E>
+typename std::enable_if<llvm::is_error_code_enum<E>::value ||
+ llvm::is_error_condition_enum<E>::value, bool>::type
+operator ==(ErrorOr<T> &Err, E Code) {
+ return error_code(Err) == Code;
}
+} // end namespace lld
#endif
Added: lld/trunk/unittests/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/unittests/CMakeLists.txt?rev=170483&view=auto
==============================================================================
--- lld/trunk/unittests/CMakeLists.txt (added)
+++ lld/trunk/unittests/CMakeLists.txt Tue Dec 18 18:51:07 2012
@@ -0,0 +1,16 @@
+add_custom_target(LLDUnitTests)
+set_target_properties(LLDUnitTests PROPERTIES FOLDER "lld tests")
+
+# add_lld_unittest(test_dirname file1.cpp file2.cpp)
+#
+# Will compile the list of files together and link against lld
+# Produces a binary named 'basename(test_dirname)'.
+function(add_lld_unittest test_dirname)
+ add_unittest(LLDUnitTests ${test_dirname} ${ARGN})
+endfunction()
+
+set(LLVM_LINK_COMPONENTS
+ support
+ )
+
+add_lld_unittest(CoreTests ErrorOrTest.cpp)
Added: lld/trunk/unittests/ErrorOrTest.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/unittests/ErrorOrTest.cpp?rev=170483&view=auto
==============================================================================
--- lld/trunk/unittests/ErrorOrTest.cpp (added)
+++ lld/trunk/unittests/ErrorOrTest.cpp Tue Dec 18 18:51:07 2012
@@ -0,0 +1,75 @@
+//===- 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 "lld/Core/ErrorOr.h"
+
+#include "gtest/gtest.h"
+
+#include <memory>
+
+using namespace lld;
+using namespace llvm;
+
+namespace {
+
+ErrorOr<int> t1() {return 1;}
+ErrorOr<int> t2() {return make_error_code(errc::invalid_argument);}
+
+TEST(ErrorOr, SimpleValue) {
+ auto 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");
+}
+
+ErrorOr<std::unique_ptr<int>> t3() {
+ return std::unique_ptr<int>(new int(3));
+}
+
+TEST(ErrorOr, Types) {
+ int x;
+ ErrorOr<int&> a(x);
+ *a = 42;
+ EXPECT_EQ(42, *a);
+
+ // Move only types.
+ EXPECT_EQ(3, **t3());
+}
+} // end anon namespace
+
+struct InvalidArgError {
+ InvalidArgError() {}
+ InvalidArgError(std::string S) : ArgName(S) {}
+ std::string ArgName;
+};
+
+namespace lld {
+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) {
+ auto a = t4();
+ EXPECT_EQ(errc::invalid_argument, a);
+ EXPECT_EQ("adena", t4().getError<InvalidArgError>().ArgName);
+}
+} // end anon namespace
More information about the llvm-commits
mailing list