From 044eb2f6afba375a914ac9d8024f8f5142bb912e Mon Sep 17 00:00:00 2001 From: Dimitry Andric Date: Mon, 18 Dec 2017 20:10:56 +0000 Subject: Vendor import of llvm trunk r321017: https://llvm.org/svn/llvm-project/llvm/trunk@321017 --- include/llvm/Support/Error.h | 721 +++++++++++++++++++++++-------------------- 1 file changed, 387 insertions(+), 334 deletions(-) (limited to 'include/llvm/Support/Error.h') diff --git a/include/llvm/Support/Error.h b/include/llvm/Support/Error.h index 9a7fa0ae6356..8567af392fb0 100644 --- a/include/llvm/Support/Error.h +++ b/include/llvm/Support/Error.h @@ -246,18 +246,20 @@ public: } private: +#if LLVM_ENABLE_ABI_BREAKING_CHECKS + // assertIsChecked() happens very frequently, but under normal circumstances + // is supposed to be a no-op. So we want it to be inlined, but having a bunch + // of debug prints can cause the function to be too large for inlining. So + // it's important that we define this function out of line so that it can't be + // inlined. + LLVM_ATTRIBUTE_NORETURN + void fatalUncheckedError() const; +#endif + void assertIsChecked() { #if LLVM_ENABLE_ABI_BREAKING_CHECKS - if (!getChecked() || getPtr()) { - dbgs() << "Program aborted due to an unhandled Error:\n"; - if (getPtr()) - getPtr()->log(dbgs()); - else - dbgs() - << "Error value was Success. (Note: Success values must still be " - "checked prior to being destroyed).\n"; - abort(); - } + if (LLVM_UNLIKELY(!getChecked() || getPtr())) + fatalUncheckedError(); #endif } @@ -407,235 +409,6 @@ inline Error joinErrors(Error E1, Error E2) { return ErrorList::join(std::move(E1), std::move(E2)); } -/// Helper for testing applicability of, and applying, handlers for -/// ErrorInfo types. -template -class ErrorHandlerTraits - : public ErrorHandlerTraits::type::operator())> {}; - -// Specialization functions of the form 'Error (const ErrT&)'. -template class ErrorHandlerTraits { -public: - static bool appliesTo(const ErrorInfoBase &E) { - return E.template isA(); - } - - template - static Error apply(HandlerT &&H, std::unique_ptr E) { - assert(appliesTo(*E) && "Applying incorrect handler"); - return H(static_cast(*E)); - } -}; - -// Specialization functions of the form 'void (const ErrT&)'. -template class ErrorHandlerTraits { -public: - static bool appliesTo(const ErrorInfoBase &E) { - return E.template isA(); - } - - template - static Error apply(HandlerT &&H, std::unique_ptr E) { - assert(appliesTo(*E) && "Applying incorrect handler"); - H(static_cast(*E)); - return Error::success(); - } -}; - -/// Specialization for functions of the form 'Error (std::unique_ptr)'. -template -class ErrorHandlerTraits)> { -public: - static bool appliesTo(const ErrorInfoBase &E) { - return E.template isA(); - } - - template - static Error apply(HandlerT &&H, std::unique_ptr E) { - assert(appliesTo(*E) && "Applying incorrect handler"); - std::unique_ptr SubE(static_cast(E.release())); - return H(std::move(SubE)); - } -}; - -/// Specialization for functions of the form 'Error (std::unique_ptr)'. -template -class ErrorHandlerTraits)> { -public: - static bool appliesTo(const ErrorInfoBase &E) { - return E.template isA(); - } - - template - static Error apply(HandlerT &&H, std::unique_ptr E) { - assert(appliesTo(*E) && "Applying incorrect handler"); - std::unique_ptr SubE(static_cast(E.release())); - H(std::move(SubE)); - return Error::success(); - } -}; - -// Specialization for member functions of the form 'RetT (const ErrT&)'. -template -class ErrorHandlerTraits - : public ErrorHandlerTraits {}; - -// Specialization for member functions of the form 'RetT (const ErrT&) const'. -template -class ErrorHandlerTraits - : public ErrorHandlerTraits {}; - -// Specialization for member functions of the form 'RetT (const ErrT&)'. -template -class ErrorHandlerTraits - : public ErrorHandlerTraits {}; - -// Specialization for member functions of the form 'RetT (const ErrT&) const'. -template -class ErrorHandlerTraits - : public ErrorHandlerTraits {}; - -/// Specialization for member functions of the form -/// 'RetT (std::unique_ptr) const'. -template -class ErrorHandlerTraits)> - : public ErrorHandlerTraits)> {}; - -/// Specialization for member functions of the form -/// 'RetT (std::unique_ptr) const'. -template -class ErrorHandlerTraits) const> - : public ErrorHandlerTraits)> {}; - -inline Error handleErrorImpl(std::unique_ptr Payload) { - return Error(std::move(Payload)); -} - -template -Error handleErrorImpl(std::unique_ptr Payload, - HandlerT &&Handler, HandlerTs &&... Handlers) { - if (ErrorHandlerTraits::appliesTo(*Payload)) - return ErrorHandlerTraits::apply(std::forward(Handler), - std::move(Payload)); - return handleErrorImpl(std::move(Payload), - std::forward(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 -Error handleErrors(Error E, HandlerTs &&... Hs) { - if (!E) - return Error::success(); - - std::unique_ptr Payload = E.takePayload(); - - if (Payload->isA()) { - ErrorList &List = static_cast(*Payload); - Error R; - for (auto &P : List.Payloads) - R = ErrorList::join( - std::move(R), - handleErrorImpl(std::move(P), std::forward(Hs)...)); - return R; - } - - return handleErrorImpl(std::move(Payload), std::forward(Hs)...); -} - -/// Behaves the same as handleErrors, except that it requires that all -/// errors be handled by the given handlers. If any unhandled error remains -/// after the handlers have run, abort() will be called. -template -void handleAllErrors(Error E, HandlerTs &&... Handlers) { - auto F = handleErrors(std::move(E), std::forward(Handlers)...); - // Cast 'F' to bool to set the 'Checked' flag if it's a success value: - (void)!F; -} - -/// Check that E is a non-error, then drop it. -inline void handleAllErrors(Error E) { - // Cast 'E' to a bool to set the 'Checked' flag if it's a success value: - (void)!E; -} - -/// 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 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. -inline std::string toString(Error E) { - SmallVector Errors; - handleAllErrors(std::move(E), [&Errors](const ErrorInfoBase &EI) { - Errors.push_back(EI.message()); - }); - return join(Errors.begin(), Errors.end(), "\n"); -} - -/// 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 Optional. -inline void consumeError(Error Err) { - handleAllErrors(std::move(Err), [](const ErrorInfoBase &) {}); -} - -/// 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 -/// // -/// // <- '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 { -public: - ErrorAsOutParameter(Error *Err) : Err(Err) { - // Raise the checked bit if Err is success. - if (Err) - (void)!!*Err; - } - - ~ErrorAsOutParameter() { - // Clear the checked bit. - if (Err && !*Err) - *Err = Error::success(); - } - -private: - Error *Err; -}; - /// Tagged union holding either a T or a Error. /// /// This class parallels ErrorOr, but replaces error_code with Error. Since @@ -861,19 +634,26 @@ private: #endif } +#if LLVM_ENABLE_ABI_BREAKING_CHECKS + LLVM_ATTRIBUTE_NORETURN + LLVM_ATTRIBUTE_NOINLINE + void fatalUncheckedExpected() const { + dbgs() << "Expected must be checked before access or destruction.\n"; + if (HasError) { + dbgs() << "Unchecked Expected contained error:\n"; + (*getErrorStorage())->log(dbgs()); + } else + dbgs() << "Expected value was in success state. (Note: Expected " + "values in success mode must still be checked prior to being " + "destroyed).\n"; + abort(); + } +#endif + void assertIsChecked() { #if LLVM_ENABLE_ABI_BREAKING_CHECKS - if (Unchecked) { - dbgs() << "Expected must be checked before access or destruction.\n"; - if (HasError) { - dbgs() << "Unchecked Expected contained error:\n"; - (*getErrorStorage())->log(dbgs()); - } else - dbgs() << "Expected value was in success state. (Note: Expected " - "values in success mode must still be checked prior to being " - "destroyed).\n"; - abort(); - } + if (LLVM_UNLIKELY(Unchecked)) + fatalUncheckedExpected(); #endif } @@ -887,26 +667,364 @@ private: #endif }; -/// Helper for Expecteds used as out-parameters. -/// -/// See ErrorAsOutParameter. -template -class ExpectedAsOutParameter { -public: - ExpectedAsOutParameter(Expected *ValOrErr) - : ValOrErr(ValOrErr) { - if (ValOrErr) - (void)!!*ValOrErr; - } - - ~ExpectedAsOutParameter() { - if (ValOrErr) - ValOrErr->setUnchecked(); - } +/// Report a serious error, calling any installed error handler. See +/// ErrorHandling.h. +LLVM_ATTRIBUTE_NORETURN void report_fatal_error(Error Err, + bool gen_crash_diag = true); -private: - Expected *ValOrErr; -}; +/// 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) { + if (Err) { + if (!Msg) + Msg = "Failure value returned from cantFail wrapped call"; + llvm_unreachable(Msg); + } +} + +/// 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 foo(bool DoFallibleOperation); +/// +/// int X = cantFail(foo(false)); +/// @endcode +template +T cantFail(Expected ValOrErr, const char *Msg = nullptr) { + if (ValOrErr) + return std::move(*ValOrErr); + else { + if (!Msg) + Msg = "Failure value returned from cantFail wrapped call"; + llvm_unreachable(Msg); + } +} + +/// 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 foo(bool DoFallibleOperation); +/// +/// Bar &X = cantFail(foo(false)); +/// @endcode +template +T& cantFail(Expected ValOrErr, const char *Msg = nullptr) { + if (ValOrErr) + return *ValOrErr; + else { + if (!Msg) + Msg = "Failure value returned from cantFail wrapped call"; + llvm_unreachable(Msg); + } +} + +/// Helper for testing applicability of, and applying, handlers for +/// ErrorInfo types. +template +class ErrorHandlerTraits + : public ErrorHandlerTraits::type::operator())> {}; + +// Specialization functions of the form 'Error (const ErrT&)'. +template class ErrorHandlerTraits { +public: + static bool appliesTo(const ErrorInfoBase &E) { + return E.template isA(); + } + + template + static Error apply(HandlerT &&H, std::unique_ptr E) { + assert(appliesTo(*E) && "Applying incorrect handler"); + return H(static_cast(*E)); + } +}; + +// Specialization functions of the form 'void (const ErrT&)'. +template class ErrorHandlerTraits { +public: + static bool appliesTo(const ErrorInfoBase &E) { + return E.template isA(); + } + + template + static Error apply(HandlerT &&H, std::unique_ptr E) { + assert(appliesTo(*E) && "Applying incorrect handler"); + H(static_cast(*E)); + return Error::success(); + } +}; + +/// Specialization for functions of the form 'Error (std::unique_ptr)'. +template +class ErrorHandlerTraits)> { +public: + static bool appliesTo(const ErrorInfoBase &E) { + return E.template isA(); + } + + template + static Error apply(HandlerT &&H, std::unique_ptr E) { + assert(appliesTo(*E) && "Applying incorrect handler"); + std::unique_ptr SubE(static_cast(E.release())); + return H(std::move(SubE)); + } +}; + +/// Specialization for functions of the form 'void (std::unique_ptr)'. +template +class ErrorHandlerTraits)> { +public: + static bool appliesTo(const ErrorInfoBase &E) { + return E.template isA(); + } + + template + static Error apply(HandlerT &&H, std::unique_ptr E) { + assert(appliesTo(*E) && "Applying incorrect handler"); + std::unique_ptr SubE(static_cast(E.release())); + H(std::move(SubE)); + return Error::success(); + } +}; + +// Specialization for member functions of the form 'RetT (const ErrT&)'. +template +class ErrorHandlerTraits + : public ErrorHandlerTraits {}; + +// Specialization for member functions of the form 'RetT (const ErrT&) const'. +template +class ErrorHandlerTraits + : public ErrorHandlerTraits {}; + +// Specialization for member functions of the form 'RetT (const ErrT&)'. +template +class ErrorHandlerTraits + : public ErrorHandlerTraits {}; + +// Specialization for member functions of the form 'RetT (const ErrT&) const'. +template +class ErrorHandlerTraits + : public ErrorHandlerTraits {}; + +/// Specialization for member functions of the form +/// 'RetT (std::unique_ptr)'. +template +class ErrorHandlerTraits)> + : public ErrorHandlerTraits)> {}; + +/// Specialization for member functions of the form +/// 'RetT (std::unique_ptr) const'. +template +class ErrorHandlerTraits) const> + : public ErrorHandlerTraits)> {}; + +inline Error handleErrorImpl(std::unique_ptr Payload) { + return Error(std::move(Payload)); +} + +template +Error handleErrorImpl(std::unique_ptr Payload, + HandlerT &&Handler, HandlerTs &&... Handlers) { + if (ErrorHandlerTraits::appliesTo(*Payload)) + return ErrorHandlerTraits::apply(std::forward(Handler), + std::move(Payload)); + return handleErrorImpl(std::move(Payload), + std::forward(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 +Error handleErrors(Error E, HandlerTs &&... Hs) { + if (!E) + return Error::success(); + + std::unique_ptr Payload = E.takePayload(); + + if (Payload->isA()) { + ErrorList &List = static_cast(*Payload); + Error R; + for (auto &P : List.Payloads) + R = ErrorList::join( + std::move(R), + handleErrorImpl(std::move(P), std::forward(Hs)...)); + return R; + } + + return handleErrorImpl(std::move(Payload), std::forward(Hs)...); +} + +/// Behaves the same as handleErrors, except that it requires that all +/// errors be handled by the given handlers. If any unhandled error remains +/// after the handlers have run, report_fatal_error() will be called. +template +void handleAllErrors(Error E, HandlerTs &&... Handlers) { + cantFail(handleErrors(std::move(E), std::forward(Handlers)...)); +} + +/// Check that E is a non-error, then drop it. +/// If E is an error report_fatal_error will be called. +inline void handleAllErrors(Error E) { + cantFail(std::move(E)); +} + +/// Handle any errors (if present) in an Expected, 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(FooStrategy S); +/// +/// auto ResultOrErr = +/// handleExpected( +/// foo(Aggressive), +/// []() { return foo(Conservative); }, +/// [](AggressiveStrategyError&) { +/// // Implicitly conusme this - we'll recover by using a conservative +/// // strategy. +/// }); +/// +/// @endcode +template +Expected handleExpected(Expected ValOrErr, RecoveryFtor &&RecoveryPath, + HandlerTs &&... Handlers) { + if (ValOrErr) + return ValOrErr; + + if (auto Err = handleErrors(ValOrErr.takeError(), + std::forward(Handlers)...)) + return std::move(Err); + + return RecoveryPath(); +} + +/// 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 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. +inline std::string toString(Error E) { + SmallVector Errors; + handleAllErrors(std::move(E), [&Errors](const ErrorInfoBase &EI) { + Errors.push_back(EI.message()); + }); + return join(Errors.begin(), Errors.end(), "\n"); +} + +/// 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 Optional. +inline void consumeError(Error Err) { + handleAllErrors(std::move(Err), [](const ErrorInfoBase &) {}); +} + +/// 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 +/// // +/// // <- '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 { +public: + ErrorAsOutParameter(Error *Err) : Err(Err) { + // Raise the checked bit if Err is success. + if (Err) + (void)!!*Err; + } + + ~ErrorAsOutParameter() { + // Clear the checked bit. + if (Err && !*Err) + *Err = Error::success(); + } + +private: + Error *Err; +}; + +/// Helper for Expecteds used as out-parameters. +/// +/// See ErrorAsOutParameter. +template +class ExpectedAsOutParameter { +public: + ExpectedAsOutParameter(Expected *ValOrErr) + : ValOrErr(ValOrErr) { + if (ValOrErr) + (void)!!*ValOrErr; + } + + ~ExpectedAsOutParameter() { + if (ValOrErr) + ValOrErr->setUnchecked(); + } + +private: + Expected *ValOrErr; +}; /// This class wraps a std::error_code in a Error. /// @@ -1032,71 +1150,6 @@ private: std::function GetExitCode; }; -/// Report a serious error, calling any installed error handler. See -/// ErrorHandling.h. -LLVM_ATTRIBUTE_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) { - if (Err) - llvm_unreachable("Failure value returned from cantFail wrapped call"); -} - -/// 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 foo(bool DoFallibleOperation); -/// -/// int X = cantFail(foo(false)); -/// @endcode -template -T cantFail(Expected ValOrErr) { - if (ValOrErr) - return std::move(*ValOrErr); - else - llvm_unreachable("Failure value returned from cantFail wrapped call"); -} - -/// 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 foo(bool DoFallibleOperation); -/// -/// Bar &X = cantFail(foo(false)); -/// @endcode -template -T& cantFail(Expected ValOrErr) { - if (ValOrErr) - return *ValOrErr; - else - llvm_unreachable("Failure value returned from cantFail wrapped call"); -} - } // end namespace llvm #endif // LLVM_SUPPORT_ERROR_H -- cgit v1.2.3