diff options
| author | Richard Qin <richard.qin.x@gmail.com> | 2026-11-01 02:12:34 +0800 |
|---|---|---|
| committer | Richard Qin <richard.qin.x@gmail.com> | 2026-11-01 02:12:34 +0800 |
| commit | 02b615c8704aeda9a94cf5457c9c78de847c911a (patch) | |
| tree | 3257dd23e643ae732cb0abf14d62ab70b64893be /libsupport/include/CoreLib/Error | |
| parent | 2850abdb23a2ba4fde6268ea4191f4d062b9a01f (diff) | |
| download | rcc-project-master.tar.gz rcc-project-master.tar.xz | |
Diffstat (limited to 'libsupport/include/CoreLib/Error')
| -rw-r--r-- | libsupport/include/CoreLib/Error/ErrorHandling.h | 217 | ||||
| -rw-r--r-- | libsupport/include/CoreLib/Error/ErrorOr.h | 378 |
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 |
