[Support] Add the 'Error' class for structured error handling.

This patch introduces the Error classs for lightweight, structured,
recoverable error handling. It includes utilities for creating, manipulating
and handling errors. The scheme is similar to exceptions, in that errors are
described with user-defined types. Unlike exceptions however, errors are
represented as ordinary return types in the API (similar to the way
std::error_code is used).

For usage notes see the LLVM programmer's manual, and the Error.h header.
Usage examples can be found in unittests/Support/ErrorTest.cpp.

Many thanks to David Blaikie, Mehdi Amini, Kevin Enderby and others on the
llvm-dev and llvm-commits lists for lots of discussion and review.



git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@263609 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Lang Hames 2016-03-16 01:02:46 +00:00
parent a235a96895
commit b92213233c
5 changed files with 1378 additions and 0 deletions

View File

@ -263,8 +263,176 @@ almost never be stored or mentioned directly. They are intended solely for use
when defining a function which should be able to efficiently accept concatenated
strings.
.. _error_apis:
Error handling
--------------
Proper error handling helps us identify bugs in our code, and helps end-users
understand errors in their tool usage. Errors fall into two broad categories:
*programmatic* and *recoverable*, with different strategies for handling and
reporting.
Programmatic Errors
^^^^^^^^^^^^^^^^^^^
Programmatic errors are violations of program invariants or API contracts, and
represent bugs within the program itself. Our aim is to document invariants, and
to abort quickly at the point of failure (providing some basic diagnostic) when
invariants are broken at runtime.
The fundamental tools for handling programmatic errors are assertions and the
llvm_unreachable function. Assertions are used to express invariant conditions,
and should include a message describing the invariant:
.. code-block:: c++
assert(isPhysReg(R) && "All virt regs should have been allocated already.");
The llvm_unreachable function can be used to document areas of control flow
that should never be entered if the program invariants hold:
.. code-block:: c++
enum { Foo, Bar, Baz } X = foo();
switch (X) {
case Foo: /* Handle Foo */; break;
case Bar: /* Handle Bar */; break;
default:
llvm_unreachable("X should be Foo or Bar here");
}
Recoverable Errors
^^^^^^^^^^^^^^^^^^
Recoverable errors represent an error in the program's environment, for example
a resource failure (a missing file, a dropped network connection, etc.), or
malformed input. These errors should be detected and communicated to a level of
the program where they can be handled appropriately. Handling the error may be
as simple as reporting the issue to the user, or it may involve attempts at
recovery.
Recoverable errors are modeled using LLVM's ``Error`` scheme. This scheme
represents errors using function return values, similar to classic C integer
error codes, or C++'s ``std::error_code``. However, the ``Error`` class is
actually a lightweight wrapper for user-defined error types, allowing arbitrary
information to be attached to describe the error. This is similar to the way C++
exceptions allow throwing of user-defined types.
Success values are created by calling ``Error::success()``:
.. code-block:: c++
Error foo() {
// Do something.
// Return success.
return Error::success();
}
Success values are very cheap to construct and return - they have minimal
impact on program performance.
Failure values are constructed using ``make_error<T>``, where ``T`` is any class
that inherits from the ErrorInfo utility:
.. code-block:: c++
class MyError : public ErrorInfo<MyError> {
public:
MyError(std::string Msg) : Msg(Msg) {}
void log(OStream &OS) const override { OS << "MyError - " << Msg; }
private:
std::string Msg;
};
Error bar() {
if (checkErrorCondition)
return make_error<MyError>("Error condition detected");
// No error - proceed with bar.
// Return success value.
return Error::success();
}
For functions that can fail but need to return a value the ``Expected<T>``
utility can be used. Values of this type can be constructed with either a
``T``, or a ``Error``. Values are implicitly convertible to boolean: true
for success, false for error. If success, the ``T`` value can be accessed via
the dereference operator. If failure, the ``Error`` value can be extracted
using the ``takeError()`` method:
.. code-block:: c++
Expected<float> parseAndSquareRoot(IStream &IS) {
float f;
OS >> f;
if (f < 0)
return make_error<FloatingPointError>(...);
return sqrt(f);
}
Error foo(IStream &IS) {
if (auto SqrtOrErr = parseAndSquartRoot(IS)) {
float Sqrt = *SqrtOrErr;
// ...
} else
return SqrtOrErr.takeError();
}
All Error instances, whether success or failure, must be either checked or
moved from (via std::move or a return) before they are destructed. Accidentally
discarding an unchecked error will cause a program abort at the point where the
unchecked value's destructor is run, making it easy to identify and fix
violations of this rule.
Success values are considered checked once they have been tested (by invoking
the boolean conversion operator):
.. code-block:: c++
if (auto Err = canFail(...))
return Err; // Failure value - move error to caller.
// Safe to continue: Err was checked.
In contrast, the following code will always cause an abort, regardless of the
return value of ``foo``:
.. code-block:: c++
canFail();
// Program will always abort here, even if canFail() returns Success, since
// the value is not checked.
Failure values are considered checked once a handler for the error type has
been activated:
.. code-block:: c++
auto Err = canFail(...);
if (auto Err2 =
handleErrors(std::move(Err),
[](std::unique_ptr<MyError> M) {
// Try to handle 'M'. If successful, return a success value from
// the handler.
if (tryToHandle(M))
return Error::success();
// We failed to handle 'M' - return it from the handler.
// This value will be passed back from catchErrors and
// wind up in Err2, where it will be returned from this function.
return Error(std::move(M));
})))
return Err2;
.. _function_apis:
More information on Error and its related utilities can be found in the
Error.h header file.
Passing functions and other callable objects
--------------------------------------------

View File

@ -0,0 +1,747 @@
//===----- llvm/Support/Error.h - Recoverable error handling ----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines an API used to report recoverable errors.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_SUPPORT_ERROR_H
#define LLVM_SUPPORT_ERROR_H
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/raw_ostream.h"
#include <vector>
namespace llvm {
class Error;
class ErrorList;
/// Base class for error info classes. Do not extend this directly: Extend
/// the ErrorInfo template subclass instead.
class ErrorInfoBase {
public:
virtual ~ErrorInfoBase() {}
/// Print an error message to an output stream.
virtual void log(raw_ostream &OS) const = 0;
// Check whether this instance is a subclass of the class identified by
// ClassID.
virtual bool isA(const void *const ClassID) const {
return ClassID == classID();
}
// Check whether this instance is a subclass of ErrorInfoT.
template <typename ErrorInfoT> bool isA() const {
return isA(ErrorInfoT::classID());
}
// Returns the class ID for this type.
static const void *classID() { return &ID; }
private:
virtual void anchor();
static char ID;
};
/// 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.:
///
/// 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.
///
/// 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 the either the
/// handleErrors or handleAllErrors function with a typed handler. E.g.:
///
/// class MyErrorInfo : public ErrorInfo<MyErrorInfo> {
/// // Custom error info.
/// };
///
/// Error foo(<...>) { return make_error<MyErrorInfo>(...); }
///
/// auto E = foo(<...>); // <- foo returns failure with MyErrorInfo.
/// auto NewE =
/// checkErrors(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 - we must check or return NewE in case any of the handlers
/// // returned a new error.
///
/// 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 Error {
// ErrorList needs to be able to yank ErrorInfoBase pointers out of this
// class to add to the error list.
friend class ErrorList;
// handleErrors needs to be able to set the Checked flag.
template <typename... HandlerTs>
friend Error handleErrors(Error E, HandlerTs &&... Handlers);
public:
/// Create a success value. Prefer using 'Error::success()' for readability
/// where possible.
Error() {
setPtr(nullptr);
setChecked(false);
}
/// Create a success value. This is equivalent to calling the default
/// constructor, but should be preferred for readability where possible.
static Error success() { return Error(); }
// Errors are not copy-constructable.
Error(const Error &Other) = delete;
/// Move-construct an error value. The newly constructed error is considered
/// unchecked, even if the source error had been checked. The original error
/// becomes a checked Success value, regardless of its original state.
Error(Error &&Other) {
setChecked(true);
*this = std::move(Other);
}
/// Create an error value. Prefer using the 'make_error' function, but
/// this constructor can be useful when "re-throwing" errors from handlers.
Error(std::unique_ptr<ErrorInfoBase> Payload) {
setPtr(Payload.release());
setChecked(false);
}
// Errors are not copy-assignable.
Error &operator=(const Error &Other) = delete;
/// Move-assign an error value. The current error must represent success, you
/// you cannot overwrite an unhandled error. The current error is then
/// considered unchecked. The source error becomes a checked success value,
/// regardless of its original state.
Error &operator=(Error &&Other) {
// Move assignment shouldn't drop an existing error, but we won't complain
// about overwriting success.
assertIsChecked();
setPtr(Other.getPtr());
// This Error is unchecked, even if the source error was checked.
setChecked(false);
// Null out Other's payload and set its checked bit.
Other.setPtr(nullptr);
Other.setChecked(true);
return *this;
}
/// Destroy a Error. Fails with a call to abort() if the error is
/// unchecked.
~Error() {
assertIsChecked();
delete getPtr();
}
/// Bool conversion. Returns true if this Error is in a failure state,
/// and false if it is in an accept state. If the error is in a Success state
/// it will be considered checked.
explicit operator bool() {
setChecked(getPtr() == nullptr);
return getPtr() != nullptr;
}
/// Check whether one error is a subclass of another.
template <typename ErrT> bool isA() const {
return getPtr()->isA(ErrT::classID());
}
private:
void assertIsChecked() {
#ifndef NDEBUG
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();
}
#endif
}
ErrorInfoBase *getPtr() const {
#ifndef NDEBUG
return PayloadAndCheckedBit.getPointer();
#else
return Payload;
#endif
}
void setPtr(ErrorInfoBase *EI) {
#ifndef NDEBUG
PayloadAndCheckedBit.setPointer(EI);
#else
Payload = EI;
#endif
}
bool getChecked() const {
#ifndef NDEBUG
return PayloadAndCheckedBit.getInt();
#else
return true;
#endif
}
void setChecked(bool V) {
#ifndef NDEBUG
PayloadAndCheckedBit.setInt(V);
#endif
}
std::unique_ptr<ErrorInfoBase> takePayload() {
std::unique_ptr<ErrorInfoBase> Tmp(getPtr());
setPtr(nullptr);
setChecked(true);
return Tmp;
}
#ifndef NDEBUG
PointerIntPair<ErrorInfoBase *, 1> PayloadAndCheckedBit;
#else
ErrorInfoBase *Payload;
#endif
};
/// Make a Error instance representing failure using the given error info
/// type.
template <typename ErrT, typename... ArgTs> Error make_error(ArgTs &&... Args) {
return Error(llvm::make_unique<ErrT>(std::forward<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 {
public:
bool isA(const void *const ClassID) const override {
return ClassID == classID() || ParentErrT::isA(ClassID);
}
static const void *classID() { return &ID; }
private:
static char ID;
};
template <typename MyErrT, typename ParentErrT>
char ErrorInfo<MyErrT, ParentErrT>::ID = 0;
/// Special ErrorInfo subclass representing a list of ErrorInfos.
/// Instances of this class are constructed by joinError.
class ErrorList final : public ErrorInfo<ErrorList> {
// handleErrors needs to be able to iterate the payload list of an
// ErrorList.
template <typename... HandlerTs>
friend Error handleErrors(Error E, HandlerTs &&... Handlers);
// joinErrors is implemented in terms of join.
friend Error joinErrors(Error, Error);
public:
void log(raw_ostream &OS) const override {
OS << "Multiple errors:\n";
for (auto &ErrPayload : Payloads) {
ErrPayload->log(OS);
OS << "\n";
}
}
private:
ErrorList(std::unique_ptr<ErrorInfoBase> Payload1,
std::unique_ptr<ErrorInfoBase> Payload2) {
assert(!Payload1->isA<ErrorList>() && !Payload2->isA<ErrorList>() &&
"ErrorList constructor payloads should be singleton errors");
Payloads.push_back(std::move(Payload1));
Payloads.push_back(std::move(Payload2));
}
static Error join(Error E1, Error E2) {
if (!E1)
return E2;
if (!E2)
return E1;
if (E1.isA<ErrorList>()) {
auto &E1List = static_cast<ErrorList &>(*E1.getPtr());
if (E2.isA<ErrorList>()) {
auto &E2List = static_cast<ErrorList &>(*E2.getPtr());
for (auto &Payload : E2List.Payloads)
E1List.Payloads.push_back(std::move(Payload));
} else
E1List.Payloads.push_back(E2.takePayload());
return E1;
}
if (E2.isA<ErrorList>()) {
auto &E2List = static_cast<ErrorList &>(*E2.getPtr());
E2List.Payloads.insert(E2List.Payloads.begin(), E1.takePayload());
return E2;
}
return Error(std::unique_ptr<ErrorList>(
new ErrorList(E1.takePayload(), E2.takePayload())));
}
std::vector<std::unique_ptr<ErrorInfoBase>> Payloads;
};
/// 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) {
return ErrorList::join(std::move(E1), std::move(E2));
}
/// Helper for testing applicability of, and applying, handlers for
/// ErrorInfo types.
template <typename HandlerT>
class ErrorHandlerTraits
: public ErrorHandlerTraits<decltype(
&std::remove_reference<HandlerT>::type::operator())> {};
// Specialization functions of the form 'Error (const ErrT&)'.
template <typename ErrT> class ErrorHandlerTraits<Error (&)(ErrT &)> {
public:
static bool appliesTo(const ErrorInfoBase &E) {
return E.template isA<ErrT>();
}
template <typename HandlerT>
static Error apply(HandlerT &&H, std::unique_ptr<ErrorInfoBase> E) {
assert(appliesTo(*E) && "Applying incorrect handler");
return H(static_cast<ErrT &>(*E));
}
};
// Specialization functions of the form 'void (const ErrT&)'.
template <typename ErrT> class ErrorHandlerTraits<void (&)(ErrT &)> {
public:
static bool appliesTo(const ErrorInfoBase &E) {
return E.template isA<ErrT>();
}
template <typename HandlerT>
static Error apply(HandlerT &&H, std::unique_ptr<ErrorInfoBase> E) {
assert(appliesTo(*E) && "Applying incorrect handler");
H(static_cast<ErrT &>(*E));
return Error::success();
}
};
/// Specialization for functions of the form 'Error (std::unique_ptr<ErrT>)'.
template <typename ErrT>
class ErrorHandlerTraits<Error (&)(std::unique_ptr<ErrT>)> {
public:
static bool appliesTo(const ErrorInfoBase &E) {
return E.template isA<ErrT>();
}
template <typename HandlerT>
static Error apply(HandlerT &&H, std::unique_ptr<ErrorInfoBase> E) {
assert(appliesTo(*E) && "Applying incorrect handler");
std::unique_ptr<ErrT> SubE(static_cast<ErrT *>(E.release()));
return H(std::move(SubE));
}
};
/// Specialization for functions of the form 'Error (std::unique_ptr<ErrT>)'.
template <typename ErrT>
class ErrorHandlerTraits<void (&)(std::unique_ptr<ErrT>)> {
public:
static bool appliesTo(const ErrorInfoBase &E) {
return E.template isA<ErrT>();
}
template <typename HandlerT>
static Error apply(HandlerT &&H, std::unique_ptr<ErrorInfoBase> E) {
assert(appliesTo(*E) && "Applying incorrect handler");
std::unique_ptr<ErrT> SubE(static_cast<ErrT *>(E.release()));
H(std::move(SubE));
return Error::success();
}
};
// Specialization for member functions of the form 'RetT (const ErrT&)'.
template <typename C, typename RetT, typename ErrT>
class ErrorHandlerTraits<RetT (C::*)(ErrT &)>
: public ErrorHandlerTraits<RetT (&)(ErrT &)> {};
// Specialization for member functions of the form 'RetT (const ErrT&) const'.
template <typename C, typename RetT, typename ErrT>
class ErrorHandlerTraits<RetT (C::*)(ErrT &) const>
: public ErrorHandlerTraits<RetT (&)(ErrT &)> {};
// Specialization for member functions of the form 'RetT (const ErrT&)'.
template <typename C, typename RetT, typename ErrT>
class ErrorHandlerTraits<RetT (C::*)(const ErrT &)>
: public ErrorHandlerTraits<RetT (&)(ErrT &)> {};
// Specialization for member functions of the form 'RetT (const ErrT&) const'.
template <typename C, typename RetT, typename ErrT>
class ErrorHandlerTraits<RetT (C::*)(const ErrT &) const>
: public ErrorHandlerTraits<RetT (&)(ErrT &)> {};
/// Specialization for member functions of the form
/// 'RetT (std::unique_ptr<ErrT>) const'.
template <typename C, typename RetT, typename ErrT>
class ErrorHandlerTraits<RetT (C::*)(std::unique_ptr<ErrT>)>
: public ErrorHandlerTraits<RetT (&)(std::unique_ptr<ErrT>)> {};
/// Specialization for member functions of the form
/// 'RetT (std::unique_ptr<ErrT>) const'.
template <typename C, typename RetT, typename ErrT>
class ErrorHandlerTraits<RetT (C::*)(std::unique_ptr<ErrT>) const>
: public ErrorHandlerTraits<RetT (&)(std::unique_ptr<ErrT>)> {};
inline Error handleErrorImpl(std::unique_ptr<ErrorInfoBase> Payload) {
return Error(std::move(Payload));
}
template <typename HandlerT, typename... HandlerTs>
Error handleErrorImpl(std::unique_ptr<ErrorInfoBase> Payload,
HandlerT &&Handler, HandlerTs &&... Handlers) {
if (ErrorHandlerTraits<HandlerT>::appliesTo(*Payload))
return ErrorHandlerTraits<HandlerT>::apply(std::forward<HandlerT>(Handler),
std::move(Payload));
return handleErrorImpl(std::move(Payload),
std::forward<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) {
if (!E)
return Error::success();
std::unique_ptr<ErrorInfoBase> Payload = E.takePayload();
if (Payload->isA<ErrorList>()) {
ErrorList &List = static_cast<ErrorList &>(*Payload);
Error R;
for (auto &P : List.Payloads)
R = ErrorList::join(
std::move(R),
handleErrorImpl(std::move(P), std::forward<HandlerTs>(Hs)...));
return R;
}
return handleErrorImpl(std::move(Payload), std::forward<HandlerTs>(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 <typename... HandlerTs>
void handleAllErrors(Error E, HandlerTs &&... Handlers) {
auto F = handleErrors(std::move(E), std::forward<HandlerTs>(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.
template <typename... HandlerTs>
void logAllUnhandledErrors(Error E, raw_ostream &OS, std::string ErrorBanner) {
if (!E)
return;
OS << ErrorBanner;
handleAllErrors(std::move(E), [&](const ErrorInfoBase &EI) {
EI.log(OS);
OS << "\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<T>.
inline void consumeError(Error Err) {
handleAllErrors(std::move(Err), [](const ErrorInfoBase &) {});
}
/// 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.
template <class T> class Expected {
template <class OtherT> friend class Expected;
static const bool isRef = std::is_reference<T>::value;
typedef ReferenceStorage<typename std::remove_reference<T>::type> wrap;
public:
typedef typename std::conditional<isRef, wrap, T>::type storage_type;
private:
typedef typename std::remove_reference<T>::type &reference;
typedef const typename std::remove_reference<T>::type &const_reference;
typedef typename std::remove_reference<T>::type *pointer;
typedef const typename std::remove_reference<T>::type *const_pointer;
public:
/// Create an Expected<T> error value from the given Error.
Expected(Error Err) : HasError(true) {
assert(Err && "Cannot create Expected from Error success value.");
new (getErrorStorage()) Error(std::move(Err));
}
/// Create an Expected<T> success value from the given OtherT value, which
/// must be convertible to T.
template <typename OtherT>
Expected(OtherT &&Val,
typename std::enable_if<std::is_convertible<OtherT, T>::value>::type
* = nullptr)
: HasError(false) {
new (getStorage()) storage_type(std::move(Val));
}
/// Move construct an Expected<T> value.
Expected(Expected &&Other) { moveConstruct(std::move(Other)); }
/// Move construct an Expected<T> value from an Expected<OtherT>, where OtherT
/// must be convertible to T.
template <class OtherT>
Expected(Expected<OtherT> &&Other,
typename std::enable_if<std::is_convertible<OtherT, T>::value>::type
* = nullptr) {
moveConstruct(std::move(Other));
}
/// Move construct an Expected<T> value from an Expected<OtherT>, where OtherT
/// isn't convertible to T.
template <class OtherT>
explicit Expected(
Expected<OtherT> &&Other,
typename std::enable_if<!std::is_convertible<OtherT, T>::value>::type * =
nullptr) {
moveConstruct(std::move(Other));
}
/// Move-assign from another Expected<T>.
Expected &operator=(Expected &&Other) {
moveAssign(std::move(Other));
return *this;
}
/// Destroy an Expected<T>.
~Expected() {
if (!HasError)
getStorage()->~storage_type();
else
getErrorStorage()->~Error();
}
/// \brief Return false if there is an error.
explicit operator bool() const { return !HasError; }
/// \brief Returns a reference to the stored T value.
reference get() { return *getStorage(); }
/// \brief Returns a const reference to the stored T value.
const_reference get() const { return const_cast<Expected<T> *>(this)->get(); }
/// \brief Check that this Expected<T> is an error of type ErrT.
template <typename ErrT> bool errorIsA() const {
return HasError && getErrorStorage()->template isA<ErrT>();
}
/// \brief Take ownership of the stored error.
/// After calling this the Expected<T> is in an indeterminate state that can
/// only be safely destructed. No further calls (beside the destructor) should
/// be made on the Expected<T> vaule.
Error takeError() {
return HasError ? std::move(*getErrorStorage()) : Error();
}
/// \brief Returns a pointer to the stored T value.
pointer operator->() { return toPointer(getStorage()); }
/// \brief Returns a const pointer to the stored T value.
const_pointer operator->() const { return toPointer(getStorage()); }
/// \brief Returns a reference to the stored T value.
reference operator*() { return *getStorage(); }
/// \brief Returns a const reference to the stored T value.
const_reference operator*() const { return *getStorage(); }
private:
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 &a, const T2 &b) {
return false;
}
template <class OtherT> void moveConstruct(Expected<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()) Error(Other.takeError());
}
}
template <class OtherT> void moveAssign(Expected<OtherT> &&Other) {
if (compareThisIfSameType(*this, Other))
return;
this->~Expected();
new (this) Expected(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 reinterpret_cast<storage_type *>(TStorage.buffer);
}
const storage_type *getStorage() const {
assert(!HasError && "Cannot get value when an error exists!");
return reinterpret_cast<const storage_type *>(TStorage.buffer);
}
Error *getErrorStorage() {
assert(HasError && "Cannot get error when a value exists!");
return reinterpret_cast<Error *>(ErrorStorage.buffer);
}
union {
AlignedCharArrayUnion<storage_type> TStorage;
AlignedCharArrayUnion<Error> ErrorStorage;
};
bool HasError : 1;
};
/// 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> {
public:
ECError() = default;
ECError(std::error_code EC) : EC(EC) {}
std::error_code getErrorCode() const { return EC; }
void log(raw_ostream &OS) const override { OS << EC.message(); }
protected:
std::error_code EC;
};
/// Helper for converting an std::error_code to a Error.
inline Error errorCodeToError(std::error_code EC) {
if (!EC)
return Error::success();
return make_error<ECError>(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().
inline std::error_code errorToErrorCode(Error Err) {
std::error_code EC;
handleAllErrors(std::move(Err),
[&](const ECError &ECE) { EC = ECE.getErrorCode(); });
return EC;
}
} // namespace llvm
#endif // LLVM_SUPPORT_ERROR_H

View File

@ -19,6 +19,7 @@
#include "llvm/Config/config.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Mutex.h"
#include "llvm/Support/MutexGuard.h"
@ -138,6 +139,9 @@ void LLVMResetFatalErrorHandler() {
remove_fatal_error_handler();
}
void ErrorInfoBase::anchor() {}
char ErrorInfoBase::ID = 0;
#ifdef LLVM_ON_WIN32
#include <winerror.h>

View File

@ -16,6 +16,7 @@ add_llvm_unittest(SupportTests
DwarfTest.cpp
EndianStreamTest.cpp
EndianTest.cpp
ErrorTest.cpp
ErrorOrTest.cpp
FileOutputBufferTest.cpp
IteratorTest.cpp

View File

@ -0,0 +1,458 @@
//===----- unittests/ErrorTest.cpp - Error.h tests ------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "llvm/Support/Error.h"
#include "llvm/Support/Errc.h"
#include "gtest/gtest.h"
#include <memory>
using namespace llvm;
namespace {
// Test:
//
// Constructing success.
// - Silent acceptance of tested success.
// - Programmatic error on untested success.
//
// Custom error class.
// - Creation of a custom error class with a default base class.
// - Handling of a custom error class with a default base class.
// - Handler type deduction.
// - Failure to handle a custom error class.
// - Isa testing of a custom error class.
//
// - Creation of a custom error class with a custom base class.
// - Handling of a custom error class with a default base class.
// - Correct shadowing of handlers.
//
// Utility functions:
// - join_errors to defer errors.
// - consume_error to consume a "safe" error without any output.
// - handleAllUnhandledErrors to assert that all errors are handled.
// - logAllUnhandledErrors to log errors to a stream.
//
// Expected tests:
// - Expected<T> with T.
// - Expected<T> with Error.
// - Failure to handle an Expected<T> in failure mode.
// - Error extraction (Expected -> Error).
//
// std::error_code:
// - std::error_code to Error in success mode.
// - std::error_code to Error (ECError) in failure mode.
// - Error to std::error_code in success mode.
// - Error (ECError) to std::error_code in failure mode.
// Custom error class with a default base class and some random 'info' attached.
class CustomError : public ErrorInfo<CustomError> {
public:
// Create an error with some info attached.
CustomError(int Info) : Info(Info) {}
// Get the info attached to this error.
int getInfo() const { return Info; }
// Log this error to a stream.
void log(raw_ostream &OS) const override {
OS << "CustomError { " << getInfo() << "}";
}
protected:
// This error is subclassed below, but we can't use inheriting constructors
// yet, so we can't propagate the constructors through ErrorInfo. Instead
// we have to have a default constructor and have the subclass initialize all
// fields.
CustomError() : Info(0) {}
int Info;
};
// Custom error class with a custom base class and some additional random
// 'info'.
class CustomSubError : public ErrorInfo<CustomSubError, CustomError> {
public:
// Create a sub-error with some info attached.
CustomSubError(int Info, int ExtraInfo) : ExtraInfo(ExtraInfo) {
this->Info = Info;
}
// Get the extra info attached to this error.
int getExtraInfo() const { return ExtraInfo; }
// Log this error to a stream.
void log(raw_ostream &OS) const override {
OS << "CustomSubError { " << getInfo() << ", " << getExtraInfo() << "}";
}
protected:
int ExtraInfo;
};
// Verify that success values that are checked (i.e. cast to 'bool') are
// destructed without error, and that unchecked success values cause an
// abort.
TEST(Error, CheckSuccess) {
// Test checked success.
{
Error E;
EXPECT_FALSE(E) << "Unexpected error while testing Error 'Success'";
}
// Test unchecked success.
// Test runs in debug mode only.
#ifndef NDEBUG
{
auto DropUncheckedSuccess = []() { Error E; };
EXPECT_DEATH(DropUncheckedSuccess(),
"Program aborted due to an unhandled Error:")
<< "Unchecked Error Succes value did not cause abort()";
}
#endif
}
static Error handleCustomError(const CustomError &CE) { return Error(); }
static void handleCustomErrorVoid(const CustomError &CE) {}
static Error handleCustomErrorUP(std::unique_ptr<CustomError> CE) {
return Error();
}
static void handleCustomErrorUPVoid(std::unique_ptr<CustomError> CE) {}
// Verify creation and handling of custom error classes.
TEST(Error, CheckCustomErrors) {
// Check that we abort on unhandled failure cases. (Force conversion to bool
// to make sure that we don't accidentally treat checked errors as handled).
// Test runs in debug mode only.
#ifndef NDEBUG
{
auto DropUnhandledError = []() {
Error E = make_error<CustomError>(42);
(void)!E;
};
EXPECT_DEATH(DropUnhandledError(),
"Program aborted due to an unhandled Error:")
<< "Unhandled Error failure value did not cause abort()";
}
#endif
// Check 'isA' handling.
{
Error E = make_error<CustomError>(1);
Error F = make_error<CustomSubError>(1, 2);
EXPECT_TRUE(E.isA<CustomError>());
EXPECT_FALSE(E.isA<CustomSubError>());
EXPECT_TRUE(F.isA<CustomError>());
EXPECT_TRUE(F.isA<CustomSubError>());
consumeError(std::move(E));
consumeError(std::move(F));
}
// Check that we can handle a custom error.
{
int CaughtErrorInfo = 0;
handleAllErrors(make_error<CustomError>(42), [&](const CustomError &CE) {
CaughtErrorInfo = CE.getInfo();
});
EXPECT_TRUE(CaughtErrorInfo == 42)
<< "Wrong result from CustomError handler";
}
// Check that handler type deduction also works for handlers
// of the following types:
// void (const Err&)
// Error (const Err&) mutable
// void (const Err&) mutable
// Error (Err&)
// void (Err&)
// Error (Err&) mutable
// void (Err&) mutable
// Error (unique_ptr<Err>)
// void (unique_ptr<Err>)
// Error (unique_ptr<Err>) mutable
// void (unique_ptr<Err>) mutable
handleAllErrors(make_error<CustomError>(42), [](const CustomError &CE) {});
handleAllErrors(
make_error<CustomError>(42),
[](const CustomError &CE) mutable { return Error::success(); });
handleAllErrors(make_error<CustomError>(42),
[](const CustomError &CE) mutable {});
handleAllErrors(make_error<CustomError>(42),
[](CustomError &CE) { return Error::success(); });
handleAllErrors(make_error<CustomError>(42), [](CustomError &CE) {});
handleAllErrors(make_error<CustomError>(42),
[](CustomError &CE) mutable { return Error::success(); });
handleAllErrors(make_error<CustomError>(42), [](CustomError &CE) mutable {});
handleAllErrors(
make_error<CustomError>(42),
[](std::unique_ptr<CustomError> CE) { return Error::success(); });
handleAllErrors(make_error<CustomError>(42),
[](std::unique_ptr<CustomError> CE) {});
handleAllErrors(
make_error<CustomError>(42),
[](std::unique_ptr<CustomError> CE) mutable { return Error::success(); });
handleAllErrors(make_error<CustomError>(42),
[](std::unique_ptr<CustomError> CE) mutable {});
// Check that named handlers of type 'Error (const Err&)' work.
handleAllErrors(make_error<CustomError>(42), handleCustomError);
// Check that named handlers of type 'void (const Err&)' work.
handleAllErrors(make_error<CustomError>(42), handleCustomErrorVoid);
// Check that named handlers of type 'Error (std::unique_ptr<Err>)' work.
handleAllErrors(make_error<CustomError>(42), handleCustomErrorUP);
// Check that named handlers of type 'Error (std::unique_ptr<Err>)' work.
handleAllErrors(make_error<CustomError>(42), handleCustomErrorUPVoid);
// Check that we can handle a custom error with a custom base class.
{
int CaughtErrorInfo = 0;
int CaughtErrorExtraInfo = 0;
handleAllErrors(make_error<CustomSubError>(42, 7),
[&](const CustomSubError &SE) {
CaughtErrorInfo = SE.getInfo();
CaughtErrorExtraInfo = SE.getExtraInfo();
});
EXPECT_TRUE(CaughtErrorInfo == 42 && CaughtErrorExtraInfo == 7)
<< "Wrong result from CustomSubError handler";
}
// Check that we trigger only the first handler that applies.
{
int DummyInfo = 0;
int CaughtErrorInfo = 0;
int CaughtErrorExtraInfo = 0;
handleAllErrors(make_error<CustomSubError>(42, 7),
[&](const CustomSubError &SE) {
CaughtErrorInfo = SE.getInfo();
CaughtErrorExtraInfo = SE.getExtraInfo();
},
[&](const CustomError &CE) { DummyInfo = CE.getInfo(); });
EXPECT_TRUE(CaughtErrorInfo == 42 && CaughtErrorExtraInfo == 7 &&
DummyInfo == 0)
<< "Activated the wrong Error handler(s)";
}
// Check that general handlers shadow specific ones.
{
int CaughtErrorInfo = 0;
int DummyInfo = 0;
int DummyExtraInfo = 0;
handleAllErrors(
make_error<CustomSubError>(42, 7),
[&](const CustomError &CE) { CaughtErrorInfo = CE.getInfo(); },
[&](const CustomSubError &SE) {
DummyInfo = SE.getInfo();
DummyExtraInfo = SE.getExtraInfo();
});
EXPECT_TRUE(CaughtErrorInfo = 42 && DummyInfo == 0 && DummyExtraInfo == 0)
<< "General Error handler did not shadow specific handler";
}
}
// Test utility functions.
TEST(Error, CheckErrorUtilities) {
// Test joinErrors
{
int CustomErrorInfo1 = 0;
int CustomErrorInfo2 = 0;
int CustomErrorExtraInfo = 0;
Error E = joinErrors(make_error<CustomError>(7),
make_error<CustomSubError>(42, 7));
handleAllErrors(std::move(E),
[&](const CustomSubError &SE) {
CustomErrorInfo2 = SE.getInfo();
CustomErrorExtraInfo = SE.getExtraInfo();
},
[&](const CustomError &CE) {
// Assert that the CustomError instance above is handled
// before the
// CustomSubError - joinErrors should preserve error
// ordering.
EXPECT_EQ(CustomErrorInfo2, 0)
<< "CustomErrorInfo2 should be 0 here. "
"joinErrors failed to preserve ordering.\n";
CustomErrorInfo1 = CE.getInfo();
});
EXPECT_TRUE(CustomErrorInfo1 == 7 && CustomErrorInfo2 == 42 &&
CustomErrorExtraInfo == 7)
<< "Failed handling compound Error.";
}
// Test consumeError for both success and error cases.
{
Error E;
consumeError(std::move(E));
}
{
Error E = make_error<CustomError>(7);
consumeError(std::move(E));
}
// Test that handleAllUnhandledErrors crashes if an error is not caught.
// Test runs in debug mode only.
#ifndef NDEBUG
{
auto FailToHandle = []() {
handleAllErrors(make_error<CustomError>(7),
[&](const CustomSubError &SE) {
errs() << "This should never be called";
exit(1);
});
};
EXPECT_DEATH(FailToHandle(), "Program aborted due to an unhandled Error:")
<< "Unhandled Error in handleAllErrors call did not cause an "
"abort()";
}
#endif
// Test that handleAllUnhandledErrors crashes if an error is returned from a
// handler.
// Test runs in debug mode only.
#ifndef NDEBUG
{
auto ReturnErrorFromHandler = []() {
handleAllErrors(make_error<CustomError>(7),
[&](std::unique_ptr<CustomSubError> SE) {
return Error(std::move(SE));
});
};
EXPECT_DEATH(ReturnErrorFromHandler(),
"Program aborted due to an unhandled Error:")
<< " Error returned from handler in handleAllErrors call did not "
"cause abort()";
}
#endif
// Test that we can return values from handleErrors.
{
int ErrorInfo = 0;
Error E = handleErrors(
make_error<CustomError>(7),
[&](std::unique_ptr<CustomError> CE) { return Error(std::move(CE)); });
handleAllErrors(std::move(E),
[&](const CustomError &CE) { ErrorInfo = CE.getInfo(); });
EXPECT_EQ(ErrorInfo, 7)
<< "Failed to handle Error returned from handleErrors.";
}
}
// Test Expected behavior.
TEST(Error, CheckExpected) {
// Check that non-errors convert to 'true'.
{
Expected<int> A = 7;
EXPECT_TRUE(!!A)
<< "Expected with non-error value doesn't convert to 'true'";
}
// Check that non-error values are accessible via operator*.
{
Expected<int> A = 7;
EXPECT_EQ(*A, 7) << "Incorrect Expected non-error value";
}
// Check that errors convert to 'false'.
{
Expected<int> A = make_error<CustomError>(42);
EXPECT_FALSE(!!A) << "Expected with error value doesn't convert to 'false'";
consumeError(A.takeError());
}
// Check that error values are accessible via takeError().
{
Expected<int> A = make_error<CustomError>(42);
Error E = A.takeError();
EXPECT_TRUE(E.isA<CustomError>()) << "Incorrect Expected error value";
consumeError(std::move(E));
}
// Check that an Expected instance with an error value doesn't allow access to
// operator*.
// Test runs in debug mode only.
#ifndef NDEBUG
{
Expected<int> A = make_error<CustomError>(42);
EXPECT_DEATH(*A, "\\(!HasError && \"Cannot get value "
"when an error exists!\"\\)")
<< "Incorrect Expected error value";
consumeError(A.takeError());
}
#endif
// Check that an Expected instance with an error triggers an abort if
// unhandled.
// Test runs in debug mode only.
#ifndef NDEBUG
EXPECT_DEATH({ Expected<int> A = make_error<CustomError>(42); },
"Program aborted due to an unhandled Error:")
<< "Unchecked Expected<T> failure value did not cause an abort()";
#endif
// Test covariance of Expected.
{
class B {};
class D : public B {};
Expected<B *> A1(Expected<D *>(nullptr));
A1 = Expected<D *>(nullptr);
Expected<std::unique_ptr<B>> A2(Expected<std::unique_ptr<D>>(nullptr));
A2 = Expected<std::unique_ptr<D>>(nullptr);
}
}
TEST(Error, ECError) {
// Round-trip a success value to check that it converts correctly.
EXPECT_EQ(errorToErrorCode(errorCodeToError(std::error_code())),
std::error_code())
<< "std::error_code() should round-trip via Error conversions";
// Round-trip an error value to check that it converts correctly.
EXPECT_EQ(errorToErrorCode(errorCodeToError(errc::invalid_argument)),
errc::invalid_argument)
<< "std::error_code error value should round-trip via Error "
"conversions";
}
} // end anon namespace