[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