aboutsummaryrefslogtreecommitdiff
path: root/libsupport/include/CoreLib/Error
diff options
context:
space:
mode:
Diffstat (limited to 'libsupport/include/CoreLib/Error')
-rw-r--r--libsupport/include/CoreLib/Error/ErrorHandling.h217
-rw-r--r--libsupport/include/CoreLib/Error/ErrorOr.h378
2 files changed, 595 insertions, 0 deletions
diff --git a/libsupport/include/CoreLib/Error/ErrorHandling.h b/libsupport/include/CoreLib/Error/ErrorHandling.h
new file mode 100644
index 0000000..5ab5a9e
--- /dev/null
+++ b/libsupport/include/CoreLib/Error/ErrorHandling.h
@@ -0,0 +1,217 @@
+/*
+ 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/ErrorHandling.h - Fatal error handling ------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines an API used to indicate fatal error conditions. Non-fatal
+// errors (most of them) should be handled through LLVMContext.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef RCC_SUPPORT_ERRORHANDLING_H
+#define RCC_SUPPORT_ERRORHANDLING_H
+
+#include "CoreLib/Compiler/Compiler.h"
+
+namespace rcc
+{
+class StringRef;
+class Twine;
+
+/// An error handler callback.
+using fatal_error_handler_t = void (*) (void *user_data, const char *reason,
+ bool gen_crash_diag);
+
+/// install_fatal_error_handler - Installs a new error handler to be used
+/// whenever a serious (non-recoverable) error is encountered by LLVM.
+///
+/// If no error handler is installed the default is to print the error message
+/// to stderr, and call exit(1). If an error handler is installed then it is
+/// the handler's responsibility to log the message, it will no longer be
+/// printed to stderr. If the error handler returns, then exit(1) will be
+/// called.
+///
+/// It is dangerous to naively use an error handler which throws an exception.
+/// Even though some applications desire to gracefully recover from arbitrary
+/// faults, blindly throwing exceptions through unfamiliar code isn't a way to
+/// achieve this.
+///
+/// \param user_data - An argument which will be passed to the install error
+/// handler.
+RCC_ABI void
+install_fatal_error_handler (fatal_error_handler_t handler,
+ void *user_data = nullptr);
+
+/// Restores default error handling behaviour.
+RCC_ABI void
+remove_fatal_error_handler ();
+
+/// ScopedFatalErrorHandler - This is a simple helper class which just
+/// calls install_fatal_error_handler in its constructor and
+/// remove_fatal_error_handler in its destructor.
+struct ScopedFatalErrorHandler
+{
+ explicit ScopedFatalErrorHandler (fatal_error_handler_t handler,
+ void *user_data = nullptr)
+ {
+ install_fatal_error_handler (handler, user_data);
+ }
+
+ ~ScopedFatalErrorHandler ()
+ {
+ remove_fatal_error_handler ();
+ }
+};
+
+/// @deprecated Use reportFatalInternalError() or reportFatalUsageError()
+/// instead.
+[[noreturn]] RCC_ABI void
+report_fatal_error (const char *reason, bool gen_crash_diag = true);
+[[noreturn]] RCC_ABI void
+report_fatal_error (StringRef reason, bool gen_crash_diag = true);
+[[noreturn]] RCC_ABI void
+report_fatal_error (const Twine &reason, bool gen_crash_diag = true);
+
+/// Report a fatal error that likely indicates a bug in LLVM. It serves a
+/// similar purpose as an assertion, but is always enabled, regardless of the
+/// value of NDEBUG.
+///
+/// This will call installed error handlers (or print the message by default)
+/// and then abort. This will produce a crash trace and *will* ask users to
+/// report an LLVM bug.
+[[noreturn]] RCC_ABI void
+reportFatalInternalError (const char *reason);
+[[noreturn]] RCC_ABI void
+reportFatalInternalError (StringRef reason);
+[[noreturn]] RCC_ABI void
+reportFatalInternalError (const Twine &reason);
+
+/// Report a fatal error that does not indicate a bug in LLVM.
+///
+/// This can be used in contexts where a proper error reporting mechanism
+/// (such as Error/Expected or DiagnosticInfo) is currently not supported, and
+/// would be too involved to introduce at the moment.
+///
+/// Examples where this function should be used instead of
+/// reportFatalInternalError() include invalid inputs or options, but also
+/// environment error conditions outside LLVM's control. It should also be used
+/// for known unsupported/unimplemented functionality.
+///
+/// This will call installed error handlers (or print the message by default)
+/// and then exit with code 1. It will not produce a crash trace and will
+/// *not* ask users to report an LLVM bug.
+[[noreturn]] RCC_ABI void
+reportFatalUsageError (const char *reason);
+[[noreturn]] RCC_ABI void
+reportFatalUsageError (StringRef reason);
+[[noreturn]] RCC_ABI void
+reportFatalUsageError (const Twine &reason);
+
+/// Installs a new bad alloc error handler that should be used whenever a
+/// bad alloc error, e.g. failing malloc/calloc, is encountered by LLVM.
+///
+/// The user can install a bad alloc handler, in order to define the behavior
+/// in case of failing allocations, e.g. throwing an exception. Note that this
+/// handler must not trigger any additional allocations itself.
+///
+/// If no error handler is installed the default is to print the error message
+/// to stderr, and call exit(1). If an error handler is installed then it is
+/// the handler's responsibility to log the message, it will no longer be
+/// printed to stderr. If the error handler returns, then exit(1) will be
+/// called.
+///
+///
+/// \param user_data - An argument which will be passed to the installed error
+/// handler.
+RCC_ABI void
+install_bad_alloc_error_handler (fatal_error_handler_t handler,
+ void *user_data = nullptr);
+
+/// Restores default bad alloc error handling behavior.
+RCC_ABI void
+remove_bad_alloc_error_handler ();
+
+RCC_ABI void
+install_out_of_memory_new_handler ();
+
+/// Reports a bad alloc error, calling any user defined bad alloc
+/// error handler. In contrast to the generic 'report_fatal_error'
+/// functions, this function might not terminate, e.g. the user
+/// defined error handler throws an exception, but it won't return.
+///
+/// Note: When throwing an exception in the bad alloc handler, make sure that
+/// the following unwind succeeds, e.g. do not trigger additional allocations
+/// in the unwind chain.
+///
+/// If no error handler is installed (default), throws a bad_alloc exception
+/// if LLVM is compiled with exception support. Otherwise prints the error
+/// to standard error and calls abort().
+[[noreturn]] RCC_ABI void
+report_bad_alloc_error (const char *Reason, bool GenCrashDiag = true);
+
+/// This function calls abort(), and prints the optional message to stderr.
+/// Use the llvm_unreachable macro (that adds location info), instead of
+/// calling this function directly.
+[[noreturn]] RCC_ABI void
+rcc_unreachable_internal (const char *msg = nullptr, const char *file = nullptr,
+ unsigned line = 0);
+} // namespace rcc
+
+/// Marks that the current location is not supposed to be reachable.
+/// In !NDEBUG builds, prints the message and location info to stderr.
+/// In NDEBUG builds, if the platform does not support a builtin unreachable
+/// then we call an internal LLVM runtime function. Otherwise the behavior is
+/// controlled by the CMake flag
+/// -DLLVM_UNREACHABLE_OPTIMIZE
+/// * When "ON" (default) llvm_unreachable() becomes an optimizer hint
+/// that the current location is not supposed to be reachable: the hint
+/// turns such code path into undefined behavior. On compilers that don't
+/// support such hints, prints a reduced message instead and aborts the
+/// program.
+/// * When "OFF", a builtin_trap is emitted instead of an
+// optimizer hint or printing a reduced message.
+///
+/// Use this instead of assert(0). It conveys intent more clearly, suppresses
+/// diagnostics for unreachable code paths, and allows compilers to omit
+/// unnecessary code.
+#ifndef NDEBUG
+#define rcc_unreachable(msg) \
+ ::rcc::rcc_unreachable_internal (msg, __FILE__, __LINE__)
+#elif !defined(RCC_BUILTIN_UNREACHABLE)
+#define rcc_unreachable(msg) ::rcc::rcc_unreachable_internal ()
+#elif RCC_UNREACHABLE_OPTIMIZE
+#define rcc_unreachable(msg) RCC_BUILTIN_UNREACHABLE
+#else
+#define rcc_unreachable(msg) \
+ do \
+ { \
+ RCC_BUILTIN_TRAP; \
+ RCC_BUILTIN_UNREACHABLE; \
+ } while (false)
+#endif
+
+#endif
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