//===- llvm/Support/Error.h - Recoverable 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 report recoverable errors. // //===----------------------------------------------------------------------===// #ifndef LLVM_SUPPORT_ERROR_H #define LLVM_SUPPORT_ERROR_H #include "llvm-c/Error.h" #include "llvm/ADT/Twine.h" #include "llvm/Config/abi-breaking.h" #include "llvm/Support/AlignOf.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" #include <cassert> #include <cstdint> #include <cstdlib> #include <functional> #include <memory> #include <new> #include <optional> #include <string> #include <system_error> #include <type_traits> #include <utility> #include <vector> namespace llvm { class ErrorSuccess; /// Base class for error info classes. Do not extend this directly: Extend /// the ErrorInfo template subclass instead. class ErrorInfoBase { … }; /// Lightweight error class with error context and mandatory checking. /// /// Instances of this class wrap a ErrorInfoBase pointer. Failure states /// are represented by setting the pointer to a ErrorInfoBase subclass /// instance containing information describing the failure. Success is /// represented by a null pointer value. /// /// Instances of Error also contains a 'Checked' flag, which must be set /// before the destructor is called, otherwise the destructor will trigger a /// runtime error. This enforces at runtime the requirement that all Error /// instances be checked or returned to the caller. /// /// There are two ways to set the checked flag, depending on what state the /// Error instance is in. For Error instances indicating success, it /// is sufficient to invoke the boolean conversion operator. E.g.: /// /// @code{.cpp} /// Error foo(<...>); /// /// if (auto E = foo(<...>)) /// return E; // <- Return E if it is in the error state. /// // We have verified that E was in the success state. It can now be safely /// // destroyed. /// @endcode /// /// A success value *can not* be dropped. For example, just calling 'foo(<...>)' /// without testing the return value will raise a runtime error, even if foo /// returns success. /// /// For Error instances representing failure, you must use either the /// handleErrors or handleAllErrors function with a typed handler. E.g.: /// /// @code{.cpp} /// class MyErrorInfo : public ErrorInfo<MyErrorInfo> { /// // Custom error info. /// }; /// /// Error foo(<...>) { return make_error<MyErrorInfo>(...); } /// /// auto E = foo(<...>); // <- foo returns failure with MyErrorInfo. /// auto NewE = /// handleErrors(std::move(E), /// [](const MyErrorInfo &M) { /// // Deal with the error. /// }, /// [](std::unique_ptr<OtherError> M) -> Error { /// if (canHandle(*M)) { /// // handle error. /// return Error::success(); /// } /// // Couldn't handle this error instance. Pass it up the stack. /// return Error(std::move(M)); /// }); /// // Note - The error passed to handleErrors will be marked as checked. If /// // there is no matched handler, a new error with the same payload is /// // created and returned. /// // The handlers take the error checked by handleErrors as an argument, /// // which can be used to retrieve more information. If a new error is /// // created by a handler, it will be passed back to the caller of /// // handleErrors and needs to be checked or return up to the stack. /// // Otherwise, the passed-in error is considered consumed. /// @endcode /// /// The handleAllErrors function is identical to handleErrors, except /// that it has a void return type, and requires all errors to be handled and /// no new errors be returned. It prevents errors (assuming they can all be /// handled) from having to be bubbled all the way to the top-level. /// /// *All* Error instances must be checked before destruction, even if /// they're moved-assigned or constructed from Success values that have already /// been checked. This enforces checking through all levels of the call stack. class [[nodiscard]] Error { … }; /// Subclass of Error for the sole purpose of identifying the success path in /// the type system. This allows to catch invalid conversion to Expected<T> at /// compile time. class ErrorSuccess final : public Error { … }; inline ErrorSuccess Error::success() { … } /// Make a Error instance representing failure using the given error info /// type. template <typename ErrT, typename... ArgTs> Error make_error(ArgTs &&... Args) { … } /// Base class for user error types. Users should declare their error types /// like: /// /// class MyError : public ErrorInfo<MyError> { /// .... /// }; /// /// This class provides an implementation of the ErrorInfoBase::kind /// method, which is used by the Error RTTI system. template <typename ThisErrT, typename ParentErrT = ErrorInfoBase> class ErrorInfo : public ParentErrT { … }; /// Special ErrorInfo subclass representing a list of ErrorInfos. /// Instances of this class are constructed by joinError. class ErrorList final : public ErrorInfo<ErrorList> { … }; /// Concatenate errors. The resulting Error is unchecked, and contains the /// ErrorInfo(s), if any, contained in E1, followed by the /// ErrorInfo(s), if any, contained in E2. inline Error joinErrors(Error E1, Error E2) { … } /// Tagged union holding either a T or a Error. /// /// This class parallels ErrorOr, but replaces error_code with Error. Since /// Error cannot be copied, this class replaces getError() with /// takeError(). It also adds an bool errorIsA<ErrT>() method for testing the /// error class type. /// /// Example usage of 'Expected<T>' as a function return type: /// /// @code{.cpp} /// Expected<int> myDivide(int A, int B) { /// if (B == 0) { /// // return an Error /// return createStringError(inconvertibleErrorCode(), /// "B must not be zero!"); /// } /// // return an integer /// return A / B; /// } /// @endcode /// /// Checking the results of to a function returning 'Expected<T>': /// @code{.cpp} /// if (auto E = Result.takeError()) { /// // We must consume the error. Typically one of: /// // - return the error to our caller /// // - toString(), when logging /// // - consumeError(), to silently swallow the error /// // - handleErrors(), to distinguish error types /// errs() << "Problem with division " << toString(std::move(E)) << "\n"; /// return; /// } /// // use the result /// outs() << "The answer is " << *Result << "\n"; /// @endcode /// /// For unit-testing a function returning an 'Expected<T>', see the /// 'EXPECT_THAT_EXPECTED' macros in llvm/Testing/Support/Error.h template <class T> class [[nodiscard]] Expected { … }; /// Report a serious error, calling any installed error handler. See /// ErrorHandling.h. [[noreturn]] void report_fatal_error(Error Err, bool gen_crash_diag = true); /// Report a fatal error if Err is a failure value. /// /// This function can be used to wrap calls to fallible functions ONLY when it /// is known that the Error will always be a success value. E.g. /// /// @code{.cpp} /// // foo only attempts the fallible operation if DoFallibleOperation is /// // true. If DoFallibleOperation is false then foo always returns /// // Error::success(). /// Error foo(bool DoFallibleOperation); /// /// cantFail(foo(false)); /// @endcode inline void cantFail(Error Err, const char *Msg = nullptr) { … } /// Report a fatal error if ValOrErr is a failure value, otherwise unwraps and /// returns the contained value. /// /// This function can be used to wrap calls to fallible functions ONLY when it /// is known that the Error will always be a success value. E.g. /// /// @code{.cpp} /// // foo only attempts the fallible operation if DoFallibleOperation is /// // true. If DoFallibleOperation is false then foo always returns an int. /// Expected<int> foo(bool DoFallibleOperation); /// /// int X = cantFail(foo(false)); /// @endcode template <typename T> T cantFail(Expected<T> ValOrErr, const char *Msg = nullptr) { … } /// Report a fatal error if ValOrErr is a failure value, otherwise unwraps and /// returns the contained reference. /// /// This function can be used to wrap calls to fallible functions ONLY when it /// is known that the Error will always be a success value. E.g. /// /// @code{.cpp} /// // foo only attempts the fallible operation if DoFallibleOperation is /// // true. If DoFallibleOperation is false then foo always returns a Bar&. /// Expected<Bar&> foo(bool DoFallibleOperation); /// /// Bar &X = cantFail(foo(false)); /// @endcode template <typename T> T& cantFail(Expected<T&> ValOrErr, const char *Msg = nullptr) { … } /// Helper for testing applicability of, and applying, handlers for /// ErrorInfo types. template <typename HandlerT> class ErrorHandlerTraits : public ErrorHandlerTraits< decltype(&std::remove_reference_t<HandlerT>::operator())> { … }; // Specialization functions of the form 'Error (const ErrT&)'. ErrorHandlerTraits<Error (&)(ErrT &)>; // Specialization functions of the form 'void (const ErrT&)'. ErrorHandlerTraits<void (&)(ErrT &)>; /// Specialization for functions of the form 'Error (std::unique_ptr<ErrT>)'. ErrorHandlerTraits<Error (&)(std::unique_ptr<ErrT>)>; /// Specialization for functions of the form 'void (std::unique_ptr<ErrT>)'. ErrorHandlerTraits<void (&)(std::unique_ptr<ErrT>)>; // Specialization for member functions of the form 'RetT (const ErrT&)'. ErrorHandlerTraits<RetT (C::*)(ErrT &)>; // Specialization for member functions of the form 'RetT (const ErrT&) const'. ErrorHandlerTraits<RetT (C::*)(ErrT &) const>; // Specialization for member functions of the form 'RetT (const ErrT&)'. ErrorHandlerTraits<RetT (C::*)(const ErrT &)>; // Specialization for member functions of the form 'RetT (const ErrT&) const'. ErrorHandlerTraits<RetT (C::*)(const ErrT &) const>; /// Specialization for member functions of the form /// 'RetT (std::unique_ptr<ErrT>)'. ErrorHandlerTraits<RetT (C::*)(std::unique_ptr<ErrT>)>; /// Specialization for member functions of the form /// 'RetT (std::unique_ptr<ErrT>) const'. ErrorHandlerTraits<RetT (C::*)(std::unique_ptr<ErrT>) const>; inline Error handleErrorImpl(std::unique_ptr<ErrorInfoBase> Payload) { … } template <typename HandlerT, typename... HandlerTs> Error handleErrorImpl(std::unique_ptr<ErrorInfoBase> Payload, HandlerT &&Handler, HandlerTs &&... Handlers) { … } /// Pass the ErrorInfo(s) contained in E to their respective handlers. Any /// unhandled errors (or Errors returned by handlers) are re-concatenated and /// returned. /// Because this function returns an error, its result must also be checked /// or returned. If you intend to handle all errors use handleAllErrors /// (which returns void, and will abort() on unhandled errors) instead. template <typename... HandlerTs> Error handleErrors(Error E, HandlerTs &&... Hs) { … } /// Behaves the same as handleErrors, except that by contract all errors /// *must* be handled by the given handlers (i.e. there must be no remaining /// errors after running the handlers, or llvm_unreachable is called). template <typename... HandlerTs> void handleAllErrors(Error E, HandlerTs &&... Handlers) { … } /// Check that E is a non-error, then drop it. /// If E is an error, llvm_unreachable will be called. inline void handleAllErrors(Error E) { … } /// Visit all the ErrorInfo(s) contained in E by passing them to the respective /// handler, without consuming the error. template <typename HandlerT> void visitErrors(const Error &E, HandlerT H) { … } /// Handle any errors (if present) in an Expected<T>, then try a recovery path. /// /// If the incoming value is a success value it is returned unmodified. If it /// is a failure value then it the contained error is passed to handleErrors. /// If handleErrors is able to handle the error then the RecoveryPath functor /// is called to supply the final result. If handleErrors is not able to /// handle all errors then the unhandled errors are returned. /// /// This utility enables the follow pattern: /// /// @code{.cpp} /// enum FooStrategy { Aggressive, Conservative }; /// Expected<Foo> foo(FooStrategy S); /// /// auto ResultOrErr = /// handleExpected( /// foo(Aggressive), /// []() { return foo(Conservative); }, /// [](AggressiveStrategyError&) { /// // Implicitly conusme this - we'll recover by using a conservative /// // strategy. /// }); /// /// @endcode template <typename T, typename RecoveryFtor, typename... HandlerTs> Expected<T> handleExpected(Expected<T> ValOrErr, RecoveryFtor &&RecoveryPath, HandlerTs &&... Handlers) { … } /// Log all errors (if any) in E to OS. If there are any errors, ErrorBanner /// will be printed before the first one is logged. A newline will be printed /// after each error. /// /// This function is compatible with the helpers from Support/WithColor.h. You /// can pass any of them as the OS. Please consider using them instead of /// including 'error: ' in the ErrorBanner. /// /// This is useful in the base level of your program to allow clean termination /// (allowing clean deallocation of resources, etc.), while reporting error /// information to the user. void logAllUnhandledErrors(Error E, raw_ostream &OS, Twine ErrorBanner = { … }; /// Write all error messages (if any) in E to a string. The newline character /// is used to separate error messages. std::string toString(Error E); /// Like toString(), but does not consume the error. This can be used to print /// a warning while retaining the original error object. std::string toStringWithoutConsuming(const Error &E); /// Consume a Error without doing anything. This method should be used /// only where an error can be considered a reasonable and expected return /// value. /// /// Uses of this method are potentially indicative of design problems: If it's /// legitimate to do nothing while processing an "error", the error-producer /// might be more clearly refactored to return an std::optional<T>. inline void consumeError(Error Err) { … } /// Convert an Expected to an Optional without doing anything. This method /// should be used only where an error can be considered a reasonable and /// expected return value. /// /// Uses of this method are potentially indicative of problems: perhaps the /// error should be propagated further, or the error-producer should just /// return an Optional in the first place. template <typename T> std::optional<T> expectedToOptional(Expected<T> &&E) { … } template <typename T> std::optional<T> expectedToStdOptional(Expected<T> &&E) { … } /// Helper for converting an Error to a bool. /// /// This method returns true if Err is in an error state, or false if it is /// in a success state. Puts Err in a checked state in both cases (unlike /// Error::operator bool(), which only does this for success states). inline bool errorToBool(Error Err) { … } /// Helper for Errors used as out-parameters. /// /// This helper is for use with the Error-as-out-parameter idiom, where an error /// is passed to a function or method by reference, rather than being returned. /// In such cases it is helpful to set the checked bit on entry to the function /// so that the error can be written to (unchecked Errors abort on assignment) /// and clear the checked bit on exit so that clients cannot accidentally forget /// to check the result. This helper performs these actions automatically using /// RAII: /// /// @code{.cpp} /// Result foo(Error &Err) { /// ErrorAsOutParameter ErrAsOutParam(&Err); // 'Checked' flag set /// // <body of foo> /// // <- 'Checked' flag auto-cleared when ErrAsOutParam is destructed. /// } /// @endcode /// /// ErrorAsOutParameter takes an Error* rather than Error& so that it can be /// used with optional Errors (Error pointers that are allowed to be null). If /// ErrorAsOutParameter took an Error reference, an instance would have to be /// created inside every condition that verified that Error was non-null. By /// taking an Error pointer we can just create one instance at the top of the /// function. class ErrorAsOutParameter { … }; /// Helper for Expected<T>s used as out-parameters. /// /// See ErrorAsOutParameter. template <typename T> class ExpectedAsOutParameter { … }; /// This class wraps a std::error_code in a Error. /// /// This is useful if you're writing an interface that returns a Error /// (or Expected) and you want to call code that still returns /// std::error_codes. class ECError : public ErrorInfo<ECError> { … }; /// The value returned by this function can be returned from convertToErrorCode /// for Error values where no sensible translation to std::error_code exists. /// It should only be used in this situation, and should never be used where a /// sensible conversion to std::error_code is available, as attempts to convert /// to/from this error will result in a fatal error. (i.e. it is a programmatic /// error to try to convert such a value). std::error_code inconvertibleErrorCode(); /// Helper for converting an std::error_code to a Error. Error errorCodeToError(std::error_code EC); /// Helper for converting an ECError to a std::error_code. /// /// This method requires that Err be Error() or an ECError, otherwise it /// will trigger a call to abort(). std::error_code errorToErrorCode(Error Err); /// Helper to get errno as an std::error_code. /// /// errno should always be represented using the generic category as that's what /// both libc++ and libstdc++ do. On POSIX systems you can also represent them /// using the system category, however this makes them compare differently for /// values outside of those used by `std::errc` if one is generic and the other /// is system. /// /// See the libc++ and libstdc++ implementations of `default_error_condition` on /// the system category for more details on what the difference is. inline std::error_code errnoAsErrorCode() { … } /// Convert an ErrorOr<T> to an Expected<T>. template <typename T> Expected<T> errorOrToExpected(ErrorOr<T> &&EO) { … } /// Convert an Expected<T> to an ErrorOr<T>. template <typename T> ErrorOr<T> expectedToErrorOr(Expected<T> &&E) { … } /// This class wraps a string in an Error. /// /// StringError is useful in cases where the client is not expected to be able /// to consume the specific error message programmatically (for example, if the /// error message is to be presented to the user). /// /// StringError can also be used when additional information is to be printed /// along with a error_code message. Depending on the constructor called, this /// class can either display: /// 1. the error_code message (ECError behavior) /// 2. a string /// 3. the error_code message and a string /// /// These behaviors are useful when subtyping is required; for example, when a /// specific library needs an explicit error type. In the example below, /// PDBError is derived from StringError: /// /// @code{.cpp} /// Expected<int> foo() { /// return llvm::make_error<PDBError>(pdb_error_code::dia_failed_loading, /// "Additional information"); /// } /// @endcode /// class StringError : public ErrorInfo<StringError> { … }; /// Create formatted StringError object. template <typename... Ts> inline Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals) { … } Error createStringError(std::string &&Msg, std::error_code EC); inline Error createStringError(std::error_code EC, const char *S) { … } inline Error createStringError(std::error_code EC, const Twine &S) { … } /// Create a StringError with an inconvertible error code. inline Error createStringError(const Twine &S) { … } template <typename... Ts> inline Error createStringError(char const *Fmt, const Ts &...Vals) { … } template <typename... Ts> inline Error createStringError(std::errc EC, char const *Fmt, const Ts &... Vals) { … } /// This class wraps a filename and another Error. /// /// In some cases, an error needs to live along a 'source' name, in order to /// show more detailed information to the user. class FileError final : public ErrorInfo<FileError> { … }; /// Concatenate a source file path and/or name with an Error. The resulting /// Error is unchecked. inline Error createFileError(const Twine &F, Error E) { … } /// Concatenate a source file path and/or name with line number and an Error. /// The resulting Error is unchecked. inline Error createFileError(const Twine &F, size_t Line, Error E) { … } /// Concatenate a source file path and/or name with a std::error_code /// to form an Error object. inline Error createFileError(const Twine &F, std::error_code EC) { … } /// Concatenate a source file path and/or name with line number and /// std::error_code to form an Error object. inline Error createFileError(const Twine &F, size_t Line, std::error_code EC) { … } Error createFileError(const Twine &F, ErrorSuccess) = delete; /// Helper for check-and-exit error handling. /// /// For tool use only. NOT FOR USE IN LIBRARY CODE. /// class ExitOnError { … }; /// Conversion from Error to LLVMErrorRef for C error bindings. inline LLVMErrorRef wrap(Error Err) { … } /// Conversion from LLVMErrorRef to Error for C error bindings. inline Error unwrap(LLVMErrorRef ErrRef) { … } } // end namespace llvm #endif // LLVM_SUPPORT_ERROR_H