diff options
Diffstat (limited to 'clang-r353983/include/llvm/Support/ErrorOr.h')
| -rw-r--r-- | clang-r353983/include/llvm/Support/ErrorOr.h | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/clang-r353983/include/llvm/Support/ErrorOr.h b/clang-r353983/include/llvm/Support/ErrorOr.h new file mode 100644 index 00000000..8211f4d8 --- /dev/null +++ b/clang-r353983/include/llvm/Support/ErrorOr.h @@ -0,0 +1,278 @@ +//===- llvm/Support/ErrorOr.h - Error Smart Pointer -------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// +/// Provides ErrorOr<T> smart pointer. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_ERROROR_H +#define LLVM_SUPPORT_ERROROR_H + +#include "llvm/Support/AlignOf.h" +#include <cassert> +#include <system_error> +#include <type_traits> +#include <utility> + +namespace llvm { + +/// 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(); +/// +/// auto buffer = getBuffer(); +/// if (error_code ec = buffer.getError()) +/// return ec; +/// buffer->write("adena"); +/// \endcode +/// +/// +/// Implicit conversion to bool returns true if there is a usable value. 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 behavior 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 = std::is_reference<T>::value; + + using wrap = std::reference_wrapper<typename std::remove_reference<T>::type>; + +public: + using storage_type = typename std::conditional<isRef, wrap, T>::type; + +private: + using reference = typename std::remove_reference<T>::type &; + using const_reference = const typename std::remove_reference<T>::type &; + using pointer = typename std::remove_reference<T>::type *; + using const_pointer = const typename std::remove_reference<T>::type *; + +public: + template <class E> + ErrorOr(E ErrorCode, + typename std::enable_if<std::is_error_code_enum<E>::value || + std::is_error_condition_enum<E>::value, + void *>::type = nullptr) + : HasError(true) { + new (getErrorStorage()) std::error_code(make_error_code(ErrorCode)); + } + + ErrorOr(std::error_code EC) : HasError(true) { + new (getErrorStorage()) std::error_code(EC); + } + + template <class OtherT> + ErrorOr(OtherT &&Val, + typename std::enable_if<std::is_convertible<OtherT, T>::value>::type + * = nullptr) + : HasError(false) { + new (getStorage()) storage_type(std::forward<OtherT>(Val)); + } + + ErrorOr(const ErrorOr &Other) { + copyConstruct(Other); + } + + template <class OtherT> + ErrorOr( + const ErrorOr<OtherT> &Other, + typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * = + nullptr) { + copyConstruct(Other); + } + + template <class OtherT> + explicit ErrorOr( + const ErrorOr<OtherT> &Other, + typename std::enable_if< + !std::is_convertible<OtherT, const T &>::value>::type * = nullptr) { + copyConstruct(Other); + } + + ErrorOr(ErrorOr &&Other) { + moveConstruct(std::move(Other)); + } + + template <class OtherT> + ErrorOr( + ErrorOr<OtherT> &&Other, + typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * = + nullptr) { + moveConstruct(std::move(Other)); + } + + // This might eventually need SFINAE but it's more complex than is_convertible + // & I'm too lazy to write it right now. + template <class OtherT> + explicit ErrorOr( + ErrorOr<OtherT> &&Other, + typename std::enable_if<!std::is_convertible<OtherT, T>::value>::type * = + nullptr) { + moveConstruct(std::move(Other)); + } + + ErrorOr &operator=(const ErrorOr &Other) { + copyAssign(Other); + return *this; + } + + ErrorOr &operator=(ErrorOr &&Other) { + moveAssign(std::move(Other)); + return *this; + } + + ~ErrorOr() { + if (!HasError) + getStorage()->~storage_type(); + } + + /// Return false if there is an error. + explicit operator bool() const { + return !HasError; + } + + reference get() { return *getStorage(); } + const_reference get() const { return const_cast<ErrorOr<T> *>(this)->get(); } + + std::error_code getError() const { + return HasError ? *getErrorStorage() : std::error_code(); + } + + pointer operator ->() { + return toPointer(getStorage()); + } + + const_pointer operator->() const { return toPointer(getStorage()); } + + reference operator *() { + return *getStorage(); + } + + const_reference operator*() const { return *getStorage(); } + +private: + template <class OtherT> + void copyConstruct(const ErrorOr<OtherT> &Other) { + if (!Other.HasError) { + // Get the other value. + HasError = false; + new (getStorage()) storage_type(*Other.getStorage()); + } else { + // Get other's error. + HasError = true; + new (getErrorStorage()) std::error_code(Other.getError()); + } + } + + 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); + } + + template <class OtherT> + void moveConstruct(ErrorOr<OtherT> &&Other) { + if (!Other.HasError) { + // Get the other value. + HasError = false; + new (getStorage()) storage_type(std::move(*Other.getStorage())); + } else { + // Get other's error. + HasError = true; + new (getErrorStorage()) std::error_code(Other.getError()); + } + } + + template <class OtherT> + void moveAssign(ErrorOr<OtherT> &&Other) { + if (compareThisIfSameType(*this, Other)) + return; + + this->~ErrorOr(); + new (this) ErrorOr(std::move(Other)); + } + + pointer toPointer(pointer Val) { + return Val; + } + + const_pointer toPointer(const_pointer Val) const { return Val; } + + pointer toPointer(wrap *Val) { + return &Val->get(); + } + + const_pointer toPointer(const wrap *Val) const { return &Val->get(); } + + storage_type *getStorage() { + assert(!HasError && "Cannot get value when an error exists!"); + return reinterpret_cast<storage_type*>(TStorage.buffer); + } + + const storage_type *getStorage() const { + assert(!HasError && "Cannot get value when an error exists!"); + return reinterpret_cast<const storage_type*>(TStorage.buffer); + } + + std::error_code *getErrorStorage() { + assert(HasError && "Cannot get error when a value exists!"); + return reinterpret_cast<std::error_code *>(ErrorStorage.buffer); + } + + const std::error_code *getErrorStorage() const { + return const_cast<ErrorOr<T> *>(this)->getErrorStorage(); + } + + union { + AlignedCharArrayUnion<storage_type> TStorage; + AlignedCharArrayUnion<std::error_code> ErrorStorage; + }; + bool HasError : 1; +}; + +template <class T, class E> +typename std::enable_if<std::is_error_code_enum<E>::value || + std::is_error_condition_enum<E>::value, + bool>::type +operator==(const ErrorOr<T> &Err, E Code) { + return Err.getError() == Code; +} + +} // end namespace llvm + +#endif // LLVM_SUPPORT_ERROROR_H |
