aboutsummaryrefslogtreecommitdiff
path: root/libsupport/include/CoreLib/Error/ErrorOr.h
diff options
context:
space:
mode:
authorRichard Qin <richard.qin.x@gmail.com>2026-11-01 02:12:34 +0800
committerRichard Qin <richard.qin.x@gmail.com>2026-11-01 02:12:34 +0800
commit02b615c8704aeda9a94cf5457c9c78de847c911a (patch)
tree3257dd23e643ae732cb0abf14d62ab70b64893be /libsupport/include/CoreLib/Error/ErrorOr.h
parent2850abdb23a2ba4fde6268ea4191f4d062b9a01f (diff)
downloadrcc-project-master.tar.gz
rcc-project-master.tar.xz
Move support code into CoreLibHEADmaster
Diffstat (limited to 'libsupport/include/CoreLib/Error/ErrorOr.h')
-rw-r--r--libsupport/include/CoreLib/Error/ErrorOr.h378
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