mirror of
				https://github.com/c64scene-ar/llvm-6502.git
				synced 2025-10-30 16:17:05 +00:00 
			
		
		
		
	git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@176228 91177308-0d34-0410-b5e6-96231b3b80d8
		
			
				
	
	
		
			515 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			515 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| //===- 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/ADT/PointerIntPair.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 {
 | |
|   template <class OtherT> friend 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 typename remove_reference<T>::type &reference;
 | |
|   typedef typename remove_reference<T>::type *pointer;
 | |
| 
 | |
| public:
 | |
|   ErrorOr() : IsValid(false) {}
 | |
| 
 | |
|   template <class E>
 | |
|   ErrorOr(E ErrorCode, typename enable_if_c<is_error_code_enum<E>::value ||
 | |
|                                             is_error_condition_enum<E>::value,
 | |
|                                             void *>::type = 0)
 | |
|       : HasError(true), IsValid(true) {
 | |
|     Error = new ErrorHolderBase;
 | |
|     Error->Error = make_error_code(ErrorCode);
 | |
|     Error->HasUserData = 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) {
 | |
|     copyConstruct(Other);
 | |
|   }
 | |
| 
 | |
|   template <class OtherT>
 | |
|   ErrorOr(const ErrorOr<OtherT> &Other) : IsValid(false) {
 | |
|     copyConstruct(Other);
 | |
|   }
 | |
| 
 | |
|   ErrorOr &operator =(const ErrorOr &Other) {
 | |
|     copyAssign(Other);
 | |
|     return *this;
 | |
|   }
 | |
| 
 | |
|   template <class OtherT>
 | |
|   ErrorOr &operator =(const ErrorOr<OtherT> &Other) {
 | |
|     copyAssign(Other);
 | |
|     return *this;
 | |
|   }
 | |
| 
 | |
| #if LLVM_HAS_RVALUE_REFERENCES
 | |
|   ErrorOr(ErrorOr &&Other) : IsValid(false) {
 | |
|     moveConstruct(std::move(Other));
 | |
|   }
 | |
| 
 | |
|   template <class OtherT>
 | |
|   ErrorOr(ErrorOr<OtherT> &&Other) : IsValid(false) {
 | |
|     moveConstruct(std::move(Other));
 | |
|   }
 | |
| 
 | |
|   ErrorOr &operator =(ErrorOr &&Other) {
 | |
|     moveAssign(std::move(Other));
 | |
|     return *this;
 | |
|   }
 | |
| 
 | |
|   template <class OtherT>
 | |
|   ErrorOr &operator =(ErrorOr<OtherT> &&Other) {
 | |
|     moveAssign(std::move(Other));
 | |
|     return *this;
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   ~ErrorOr() {
 | |
|     if (!IsValid)
 | |
|       return;
 | |
|     if (HasError)
 | |
|       Error->release();
 | |
|     else
 | |
|       get()->~storage_type();
 | |
|   }
 | |
| 
 | |
|   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:
 | |
|   template <class OtherT>
 | |
|   void copyConstruct(const ErrorOr<OtherT> &Other) {
 | |
|     // Construct an invalid ErrorOr if other is invalid.
 | |
|     if (!Other.IsValid)
 | |
|       return;
 | |
|     IsValid = true;
 | |
|     if (!Other.HasError) {
 | |
|       // Get the other value.
 | |
|       HasError = false;
 | |
|       new (get()) storage_type(*Other.get());
 | |
|     } else {
 | |
|       // Get other's error.
 | |
|       Error = Other.Error;
 | |
|       HasError = true;
 | |
|       Error->aquire();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   template <class T1>
 | |
|   static bool compareThisIfSameType(const T1 &a, const T1 &b) {
 | |
|     return &a == &b;
 | |
|   }
 | |
| 
 | |
|   template <class T1, class T2>
 | |
|   static bool compareThisIfSameType(const T1 &a, const T2 &b) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   template <class OtherT>
 | |
|   void copyAssign(const ErrorOr<OtherT> &Other) {
 | |
|     if (compareThisIfSameType(*this, Other))
 | |
|       return;
 | |
| 
 | |
|     this->~ErrorOr();
 | |
|     new (this) ErrorOr(Other);
 | |
|   }
 | |
| 
 | |
| #if LLVM_HAS_RVALUE_REFERENCES
 | |
|   template <class OtherT>
 | |
|   void moveConstruct(ErrorOr<OtherT> &&Other) {
 | |
|     // Construct an invalid ErrorOr if other is invalid.
 | |
|     if (!Other.IsValid)
 | |
|       return;
 | |
|     IsValid = true;
 | |
|     if (!Other.HasError) {
 | |
|       // Get the other value.
 | |
|       HasError = false;
 | |
|       new (get()) storage_type(std::move(*Other.get()));
 | |
|       // 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;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   template <class OtherT>
 | |
|   void moveAssign(ErrorOr<OtherT> &&Other) {
 | |
|     if (compareThisIfSameType(*this, Other))
 | |
|       return;
 | |
| 
 | |
|     this->~ErrorOr();
 | |
|     new (this) ErrorOr(std::move(Other));
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   pointer toPointer(pointer Val) {
 | |
|     return Val;
 | |
|   }
 | |
| 
 | |
|   pointer toPointer(wrap *Val) {
 | |
|     return &Val->get();
 | |
|   }
 | |
| 
 | |
|   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;
 | |
| };
 | |
| 
 | |
| // ErrorOr specialization for void.
 | |
| template <>
 | |
| class ErrorOr<void> {
 | |
| public:
 | |
|   ErrorOr() : Error(0, 0) {}
 | |
| 
 | |
|   template <class E>
 | |
|   ErrorOr(E ErrorCode, typename enable_if_c<is_error_code_enum<E>::value ||
 | |
|                                             is_error_condition_enum<E>::value,
 | |
|                                             void *> ::type = 0)
 | |
|       : Error(0, 0) {
 | |
|     error_code EC = make_error_code(ErrorCode);
 | |
|     if (EC == errc::success) {
 | |
|       Error.setInt(1);
 | |
|       return;
 | |
|     }
 | |
|     ErrorHolderBase *EHB = new ErrorHolderBase;
 | |
|     EHB->Error = EC;
 | |
|     EHB->HasUserData = false;
 | |
|     Error.setPointer(EHB);
 | |
|   }
 | |
| 
 | |
|   ErrorOr(llvm::error_code EC) : Error(0, 0) {
 | |
|     if (EC == errc::success) {
 | |
|       Error.setInt(1);
 | |
|       return;
 | |
|     }
 | |
|     ErrorHolderBase *E = new ErrorHolderBase;
 | |
|     E->Error = EC;
 | |
|     E->HasUserData = false;
 | |
|     Error.setPointer(E);
 | |
|   }
 | |
| 
 | |
|   template<class UserDataT>
 | |
|   ErrorOr(UserDataT UD, typename
 | |
|           enable_if_c<ErrorOrUserDataTraits<UserDataT>::value>::type* = 0)
 | |
|       : Error(0, 0) {
 | |
|     ErrorHolderBase *E = new ErrorHolder<UserDataT>(llvm_move(UD));
 | |
|     E->Error = ErrorOrUserDataTraits<UserDataT>::error();
 | |
|     E->HasUserData = true;
 | |
|     Error.setPointer(E);
 | |
|   }
 | |
| 
 | |
|   ErrorOr(const ErrorOr &Other) : Error(0, 0) {
 | |
|     Error = Other.Error;
 | |
|     if (Other.Error.getPointer()->Error) {
 | |
|       Error.getPointer()->aquire();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   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) : Error(0) {
 | |
|     // Get other's error.
 | |
|     Error = Other.Error;
 | |
|     // Tell other not to do any destruction.
 | |
|     Other.Error.setPointer(0);
 | |
|   }
 | |
| 
 | |
|   ErrorOr &operator =(ErrorOr &&Other) {
 | |
|     if (this == &Other)
 | |
|       return *this;
 | |
| 
 | |
|     this->~ErrorOr();
 | |
|     new (this) ErrorOr(std::move(Other));
 | |
| 
 | |
|     return *this;
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   ~ErrorOr() {
 | |
|     if (Error.getPointer())
 | |
|       Error.getPointer()->release();
 | |
|   }
 | |
| 
 | |
|   template<class ET>
 | |
|   ET getError() const {
 | |
|     assert(ErrorOrUserDataTraits<ET>::error() == *this &&
 | |
|            "Incorrect user error data type for error!");
 | |
|     if (!Error.getPointer()->HasUserData)
 | |
|       return ET();
 | |
|     return reinterpret_cast<const ErrorHolder<ET> *>(
 | |
|         Error.getPointer())->UserData;
 | |
|   }
 | |
| 
 | |
|   typedef void (*unspecified_bool_type)();
 | |
|   static void unspecified_bool_true() {}
 | |
| 
 | |
|   /// \brief Return false if there is an error.
 | |
|   operator unspecified_bool_type() const {
 | |
|     return Error.getInt() ? unspecified_bool_true : 0;
 | |
|   }
 | |
| 
 | |
|   operator llvm::error_code() const {
 | |
|     return Error.getInt() ? make_error_code(errc::success)
 | |
|                           : Error.getPointer()->Error;
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   // If the bit is 1, the error is success.
 | |
|   llvm::PointerIntPair<ErrorHolderBase *, 1> Error;
 | |
| };
 | |
| 
 | |
| 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
 |