//===- 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 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 #if LLVM_HAS_CXX11_TYPETRAITS #include #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 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 struct ErrorOrUserDataTraits : llvm::false_type {}; #if LLVM_HAS_CXX11_TYPETRAITS && LLVM_HAS_RVALUE_REFERENCES template typename std::enable_if< std::is_constructible::value , typename std::remove_reference::type>::type && moveIfMoveConstructible(V &Val) { return std::move(Val); } template typename std::enable_if< !std::is_constructible::value , typename std::remove_reference::type>::type & moveIfMoveConstructible(V &Val) { return Val; } #else template V &moveIfMoveConstructible(V &Val) { return Val; } #endif /// \brief Stores a reference that can be changed. template 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 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 getBuffer(); /// void handleError(error_code ec); /// /// auto buffer = getBuffer(); /// if (!buffer) /// handleError(buffer); /// buffer->write("adena"); /// \endcode /// /// ErrorOr 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() 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 : std::true_type { /// static error_code error() { /// return make_error_code(errc::invalid_argument); /// } /// }; /// } // end namespace llvm /// /// using namespace llvm; /// /// ErrorOr foo() { /// return InvalidArgError("adena"); /// } /// /// int main() { /// auto a = foo(); /// if (!a && error_code(a) == errc::invalid_argument) /// llvm::errs() << a.getError().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::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 ErrorOr { template friend class ErrorOr; static const bool isRef = is_reference::value; typedef ReferenceStorage::type> wrap; public: typedef typename conditional< isRef , wrap , T >::type storage_type; private: typedef typename remove_reference::type &reference; typedef typename remove_reference::type *pointer; public: ErrorOr() : IsValid(false) {} template ErrorOr(E ErrorCode, typename enable_if_c::value || is_error_condition_enum::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 ErrorOr(UserDataT UD, typename enable_if_c::value>::type* = 0) : HasError(true), IsValid(true) { Error = new ErrorHolder(llvm_move(UD)); Error->Error = ErrorOrUserDataTraits::error(); Error->HasUserData = true; } ErrorOr(T Val) : HasError(false), IsValid(true) { new (get()) storage_type(moveIfMoveConstructible(Val)); } ErrorOr(const ErrorOr &Other) : IsValid(false) { copyConstruct(Other); } template ErrorOr(const ErrorOr &Other) : IsValid(false) { copyConstruct(Other); } ErrorOr &operator =(const ErrorOr &Other) { copyAssign(Other); return *this; } template ErrorOr &operator =(const ErrorOr &Other) { copyAssign(Other); return *this; } #if LLVM_HAS_RVALUE_REFERENCES ErrorOr(ErrorOr &&Other) : IsValid(false) { moveConstruct(std::move(Other)); } template ErrorOr(ErrorOr &&Other) : IsValid(false) { moveConstruct(std::move(Other)); } ErrorOr &operator =(ErrorOr &&Other) { moveAssign(std::move(Other)); return *this; } template ErrorOr &operator =(ErrorOr &&Other) { moveAssign(std::move(Other)); return *this; } #endif ~ErrorOr() { if (!IsValid) return; if (HasError) Error->release(); else get()->~storage_type(); } template 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::error() == Error->Error && "Incorrect user error data type for error!"); if (!Error->HasUserData) return ET(); return reinterpret_cast*>(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 void copyConstruct(const ErrorOr &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 static bool compareThisIfSameType(const T1 &a, const T1 &b) { return &a == &b; } template static bool compareThisIfSameType(const T1 &a, const T2 &b) { return false; } template void copyAssign(const ErrorOr &Other) { if (compareThisIfSameType(*this, Other)) return; this->~ErrorOr(); new (this) ErrorOr(Other); } #if LLVM_HAS_RVALUE_REFERENCES template void moveConstruct(ErrorOr &&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 void moveAssign(ErrorOr &&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(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(TStorage.buffer); } union { AlignedCharArrayUnion TStorage; ErrorHolderBase *Error; }; bool HasError : 1; bool IsValid : 1; }; // ErrorOr specialization for void. template <> class ErrorOr { public: ErrorOr() : Error(0, 0) {} template ErrorOr(E ErrorCode, typename enable_if_c::value || is_error_condition_enum::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 ErrorOr(UserDataT UD, typename enable_if_c::value>::type* = 0) : Error(0, 0) { ErrorHolderBase *E = new ErrorHolder(llvm_move(UD)); E->Error = ErrorOrUserDataTraits::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 ET getError() const { assert(ErrorOrUserDataTraits::error() == *this && "Incorrect user error data type for error!"); if (!Error.getPointer()->HasUserData) return ET(); return reinterpret_cast *>( 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 Error; }; template typename enable_if_c::value || is_error_condition_enum::value, bool>::type operator ==(ErrorOr &Err, E Code) { return error_code(Err) == Code; } } // end namespace llvm #endif