<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
</head>
<body bgcolor="#FFFFFF" text="#000000">
I've had some experience dealing with rich error descriptions
without exceptions before. The scheme I used was somewhat similar
to what you have. Here are some items to consider.<br>
<br>
* How will the following code be avoided? The answer may be compile
time error, runtime error, style recommendations, or maybe something
else.<br>
<br>
TypedError Err = foo();<br>
// no checking in between<br>
Err = foo();<br>
<br>
* How about this?<br>
<br>
TypedError Err = foo();<br>
functionWithHorribleSideEffects();<br>
if(Err) return;<br>
<br>
* Do you anticipate giving these kinds of errors to out of tree
projects? If so, are there any kind of binary compatibility
guarantee?<br>
<br>
* What about errors that should come out of constructors? Or
<shudder> destructors?<br>
<br>
* If a constructor fails and doesn't establish it's invariant, what
will prevent the use of that invalid object?<br>
<br>
* How many subclasses do you expect to make of TypedError? Less than
10? More than 100?<br>
<br>
* How common is it to want to handle a specific error code in a
non-local way? In my experience, I either want a specific error
handled locally, or a fail / not-failed from farther away. The
answer to this question may influence the number of subclasses you
want to make.<br>
<br>
* Are file, line number, and / or call stack information captured?
I've found file and line number information to be incredibly useful
from a productivity standpoint.<br>
<br>
<div class="moz-cite-prefix">On 2/2/2016 7:29 PM, Lang Hames via
llvm-dev wrote:<br>
</div>
<blockquote
cite="mid:CALLttgo6VZAMFvv=bFF1MwDQuFBwOjKCQNNuO2hfTuRQ9oT8Xw@mail.gmail.com"
type="cite">
<div dir="ltr"><span style="font-size:13px">Hi All,</span>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">I've been thinking lately about how
to improve LLVM's error model and error reporting. A lack of
good error reporting in Orc and MCJIT has forced me to spend a
lot of time investigating hard-to-debug errors that could
easily have been identified if we provided richer error
information to the client, rather than just aborting. Kevin
Enderby has made similar observations about the state of
libObject and the difficulty of producing good error messages
for damaged object files. I expect to encounter more issues
like this as I continue work on the MachO side of LLD. I see
tackling the error modeling problem as a first step towards
improving error handling in general: if we make it easy to
model errors, it may pave the way for better error handling in
many parts of our libraries.</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">At present in LLVM we model errors
with std::error_code (and its helper, ErrorOr) and use
diagnostic streams for error reporting. Neither of these seem
entirely up to the job of providing a solid error-handling
mechanism for library code. Diagnostic streams are great if
all you want to do is report failure to the user and then
terminate, but they can't be used to distinguish between
different kinds of errors, and so are unsuited to many
use-cases (especially error recovery). On the other hand,
std::error_code allows error kinds to be distinguished, but
suffers a number of drawbacks:</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">1. It carries no context: It tells
you what went wrong, but not where or why, making it difficult
to produce good diagnostics.</div>
<div style="font-size:13px">2. It's extremely easy to ignore or
forget: instances can be silently dropped.</div>
<div style="font-size:13px">3. It's not especially debugger
friendly: Most people call the error_code constructors
directly for both success and failure values. Breakpoints have
to be set carefully to avoid stopping when success values are
constructed.</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">In fairness to std::error_code, it
has some nice properties too:</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">1. It's extremely lightweight.</div>
<div style="font-size:13px">2. It's explicit in the API (unlike
exceptions).</div>
<div style="font-size:13px">3. It doesn't require C++ RTTI (a
requirement for use in LLVM).</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">To address these shortcomings I have
prototyped a new error-handling scheme partially inspired by
C++ exceptions. The aim was to preserve the performance and
API visibility of std::error_code, while allowing users to
define custom error classes and inheritance relationships
between them. My hope is that library code could use this
scheme to model errors in a meaningful way, allowing clients
to inspect the error information and recover where possible,
or provide a rich diagnostic when aborting.</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">The scheme has three major "moving
parts":</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">1. A new 'TypedError' class that can
be used as a replacement for std::error_code. E.g.</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px"><font face="monospace, monospace">std::error_code
foo();</font></div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">becomes</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px"><font face="monospace, monospace">TypedError
foo();</font></div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">The TypedError class serves as a
lightweight wrapper for the real error information (see (2)).
It also contains a 'Checked' flag, initially set to false,
that tracks whether the error has been handled or not. If a
TypedError is ever destructed without being checked (or passed
on to someone else) it will call std::terminate(). TypedError
cannot be silently dropped.</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">2. A utility class, TypedErrorInfo,
for building error class hierarchies rooted at
'TypedErrorInfoBase' with custom RTTI. E.g.</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px"><font face="monospace, monospace">//
Define a new error type implicitly inheriting from
TypedErrorInfoBase.</font></div>
<div style="font-size:13px"><font face="monospace, monospace">class
MyCustomError : public TypedErrorInfo<MyCustomError> {</font></div>
<div style="font-size:13px"><font face="monospace, monospace">public:</font></div>
<div style="font-size:13px"><font face="monospace, monospace">
// Custom error info.</font></div>
<div style="font-size:13px"><font face="monospace, monospace">};</font></div>
<div style="font-size:13px"><font face="monospace, monospace"><br>
</font></div>
<div style="font-size:13px"><font face="monospace, monospace">//
Define a subclass of MyCustomError.</font></div>
<div style="font-size:13px"><font face="monospace, monospace">class
MyCustomSubError : public
TypedErrorInfo<MyCustomSubError, MyCustomError> {</font></div>
<div style="font-size:13px"><font face="monospace, monospace">public:</font></div>
<div style="font-size:13px"><font face="monospace, monospace">
// Extends MyCustomError, adds new members.</font></div>
<div style="font-size:13px"><font face="monospace, monospace">};</font></div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">3. A set of utility functions that
use the custom RTTI system to inspect and handle typed errors.
For example 'catchAllTypedErrors' and 'handleTypedError'
cooperate to handle error instances in a type-safe way:</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px"><font face="monospace, monospace">TypedError
foo() {</font></div>
<div style="font-size:13px"><font face="monospace, monospace">
if (SomeFailureCondition)</font></div>
<div style="font-size:13px"><font face="monospace, monospace">
return make_typed_error<MyCustomError>();</font></div>
<div style="font-size:13px"><font face="monospace, monospace">}</font></div>
<div style="font-size:13px"><font face="monospace, monospace"><br>
</font></div>
<div style="font-size:13px"><font face="monospace, monospace">TypedError
Err = foo();</font></div>
<div style="font-size:13px"><font face="monospace, monospace"><br>
</font></div>
<div style="font-size:13px"><font face="monospace, monospace">catchAllTypedErrors(std::move(Err),</font></div>
<div style="font-size:13px"><font face="monospace, monospace">
handleTypedError<MyCustomError>(</font></div>
<div style="font-size:13px"><font face="monospace, monospace">
[](std::unique_ptr<MyCustomError> E) {</font></div>
<div style="font-size:13px"><font face="monospace, monospace">
// Handle the error.</font></div>
<div style="font-size:13px"><font face="monospace, monospace">
return TypedError(); // <- Indicate success from
handler.</font></div>
<div style="font-size:13px"><font face="monospace, monospace">
}</font></div>
<div style="font-size:13px"><font face="monospace, monospace">
)</font></div>
<div style="font-size:13px"><font face="monospace, monospace">);</font></div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">If your initial reaction is "Too
much boilerplate!" I understand, but take comfort: (1) In the
overwhelmingly common case of simply returning errors, the
usage is identical to std::error_code:</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px"><font face="monospace, monospace">if
(TypedError Err = foo())</font></div>
<div style="font-size:13px"><font face="monospace, monospace">
return Err;</font></div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">and (2) the boilerplate for catching
errors is usually easily contained in a handful of utility
functions, and tends not to crowd the rest of your source
code. My initial experiments with this scheme involved
updating many source lines, but did not add much code at all
beyond the new error classes that were introduced.</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">I believe that this scheme addresses
many of the shortcomings of std::error_code while maintaining
the strengths: </div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">1. Context - Custom error classes
enable the user to attach as much contextual information as
desired.</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">2. Difficult to drop - The 'checked'
flag in TypedError ensures that it can't be dropped, it must
be explicitly "handled", even if that only involves catching
the error and doing nothing.</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">3. Debugger friendly - You can set a
breakpoint on any custom error class's constructor to catch
that error being created. Since the error class hierarchy is
rooted you can break on TypedErrorInfoBase::TypedErrorInfoBase
to catch any error being raised.</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">4. Lightweight - Because TypedError
instances are just a pointer and a checked-bit,
move-constructing it is very cheap. We may also want to
consider ignoring the 'checked' bit in release mode, at which
point TypedError should be as cheap as std::error_code.</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">5. Explicit - TypedError is
represented explicitly in the APIs, the same as
std::error_code.</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">6. Does not require C++ RTTI - The
custom RTTI system does not rely on any standard C++ RTTI
features.</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">This scheme also has one attribute
that I haven't seen in previous error handling systems (though
my experience in this area is limited): Errors are not
copyable, due to ownership semantics of TypedError. I think
this actually neatly captures the idea that there is a chain
of responsibility for dealing with any given error.
Responsibility may be transferred (e.g. by returning it to a
caller), but it cannot be duplicated as it doesn't generally
make sense for multiple people to report or attempt to recover
from the same error.</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">I've tested this prototype out by
threading it through the object-creation APIs of libObject and
using custom error classes to report errors in MachO headers.
My initial experience is that this has enabled much richer
error messages than are possible with std::error_code.</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">To enable interaction with APIs that
still use std::error_code I have added a custom ECError class
that wraps a std::error_code, and can be converted back to a
std::error_code using the typedErrorToErrorCode function. For
now, all custom error code classes should (and do, in the
prototype) derive from this utility class. In my experiments,
this has made it easy to thread TypedError selectively through
parts of the API. Eventually my hope is that TypedError could
replace std::error_code for user-facing APIs, at which point
custom errors would no longer need to derive from ECError, and
ECError could be relegated to a utility for interacting with
other codebases that still use std::error_code.</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">So - I look forward to hearing your
thoughts. :)</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">Cheers,</div>
<div style="font-size:13px">Lang.</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">
<div>Attached files:<br>
</div>
<div><br>
</div>
<div>typed_error.patch - Adds
include/llvm/Support/TypedError.h (also adds anchor() method
to lib/Support/ErrorHandling.cpp).</div>
<div><br>
</div>
<div>error_demo.tgz - Stand-alone program demo'ing basic use
of the TypedError API.</div>
</div>
<div style="font-size:13px"><br>
</div>
<div style="font-size:13px">libobject_typed_error_demo.patch -
Threads TypedError through the binary-file creation methods
(createBinary, createObjectFile, etc). Proof-of-concept for
how TypedError can be integrated into an existing system.</div>
<div style="font-size:13px"><br>
</div>
</div>
<br>
<fieldset class="mimeAttachmentHeader"></fieldset>
<br>
<pre wrap="">_______________________________________________
LLVM Developers mailing list
<a class="moz-txt-link-abbreviated" href="mailto:llvm-dev@lists.llvm.org">llvm-dev@lists.llvm.org</a>
<a class="moz-txt-link-freetext" href="http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev">http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev</a>
</pre>
</blockquote>
<br>
<pre class="moz-signature" cols="72">--
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
</pre>
</body>
</html>