diff options
author | Michael J. Spencer <bigcheesegs@gmail.com> | 2013-01-20 20:32:30 +0000 |
---|---|---|
committer | Michael J. Spencer <bigcheesegs@gmail.com> | 2013-01-20 20:32:30 +0000 |
commit | 01812bebcc345b09ce261317b6fdefde8f097642 (patch) | |
tree | f49ada585f33eb34cea9a3ea266d95099f8ea30f /include/llvm/Support | |
parent | 5ff7a3f947c245df9ae95a381ef38184527e83e1 (diff) |
[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
Diffstat (limited to 'include/llvm/Support')
-rw-r--r-- | include/llvm/Support/Compiler.h | 27 | ||||
-rw-r--r-- | include/llvm/Support/ErrorOr.h | 342 | ||||
-rw-r--r-- | include/llvm/Support/type_traits.h | 4 |
3 files changed, 373 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. /// |