diff options
Diffstat (limited to 'libsupport/include/CoreLib/Error/ErrorOr.h')
| -rw-r--r-- | libsupport/include/CoreLib/Error/ErrorOr.h | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/libsupport/include/CoreLib/Error/ErrorOr.h b/libsupport/include/CoreLib/Error/ErrorOr.h new file mode 100644 index 0000000..0e17822 --- /dev/null +++ b/libsupport/include/CoreLib/Error/ErrorOr.h @@ -0,0 +1,378 @@ +/* + RCC-Project + + Copyright (C) 2026 Richard Qin + + This file is part of RCC-Project. + + RCC-Project is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your option) any + later version. + + RCC-Project is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + RCC-Project; see the file COPYING. If not see + <http://www.gnu.org/licenses/>. +*/ + +//===- 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 RCC_SUPPORT_ERROROR_H +#define RCC_SUPPORT_ERROROR_H + +#include <cassert> +#include <functional> // for std::reference_wrapper +#include <system_error> +#include <type_traits> +#include <utility> + +namespace rcc +{ + +/// 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 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 constexpr bool isRef = std::is_reference_v<T>; + + using wrap = std::reference_wrapper<std::remove_reference_t<T>>; + +public: + using storage_type = std::conditional_t<isRef, wrap, T>; + +private: + using reference = std::remove_reference_t<T> &; + using const_reference = const std::remove_reference_t<T> &; + using pointer = std::remove_reference_t<T> *; + using const_pointer = const std::remove_reference_t<T> *; + +public: + template <class E> + ErrorOr (E ErrorCode, + std::enable_if_t<std::is_error_code_enum<E>::value || + std::is_error_condition_enum<E>::value, + void *> = 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, + std::enable_if_t<std::is_convertible_v<OtherT, T>> * = 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, + std::enable_if_t<std::is_convertible_v<OtherT, T>> * = nullptr) + { + copyConstruct (Other); + } + + template <class OtherT> + explicit ErrorOr ( + const ErrorOr<OtherT> &Other, + std::enable_if_t<!std::is_convertible_v<OtherT, const T &>> * = nullptr) + { + copyConstruct (Other); + } + + ErrorOr (ErrorOr &&Other) + { + moveConstruct (std::move (Other)); + } + + template <class OtherT> + ErrorOr (ErrorOr<OtherT> &&Other, + std::enable_if_t<std::is_convertible_v<OtherT, T>> * = 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, + std::enable_if_t<!std::is_convertible_v<OtherT, T>> * = 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 &, const T2 &) + { + 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 &TStorage; + } + + const storage_type * + getStorage () const + { + assert (!HasError && "Cannot get value when an error exists!"); + return &TStorage; + } + + std::error_code * + getErrorStorage () + { + assert (HasError && "Cannot get error when a value exists!"); + return &ErrorStorage; + } + + const std::error_code * + getErrorStorage () const + { + assert (HasError && "Cannot get error when a value exists!"); + return &ErrorStorage; + } + + union + { + storage_type TStorage; + std::error_code ErrorStorage; + }; + bool HasError : 1; +}; + +template <class T, class E> +std::enable_if_t<std::is_error_code_enum<E>::value || + std::is_error_condition_enum<E>::value, + bool> +operator== (const ErrorOr<T> &Err, E Code) +{ + return Err.getError () == Code; +} + +} // end namespace rcc + +#endif // RCC_SUPPORT_ERROROR_H |
