Generically, I like this idea.
However, regarding context, I wonder about the best model. When we designed the diagnostic reporting interface (by which I mean the bits in include/llvm/IR/DiagnosticInfo.h and include/llvm/IR/DiagnosticPrinter.h), the ability to carry context was very important. There, however, because the objects are being passed via a callback to the user-installed handler, they can carry pointers/references to objects (Values, Functions, etc.) that will go away once the object that detected the error is destroyed. In the model you're proposing, all of the context must be contained within the error object itself (because, by the time the context is useful, an arbitrary amount of the call stack to the error-detection point has already been unwound). This greatly limits the amount of information that can be efficiently stored as context in the error object. Depending on the use cases, it might be better to pass the context to some kind of error-handler callback than to try to pack it all into a long-lived error object. Thoughts?
Thanks again,
Hal
> _______________________________________________
> LLVM Developers mailing list
> llvm...@lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
>
--
Hal Finkel
Assistant Computational Scientist
Leadership Computing Facility
Argonne National Laboratory
_______________________________________________
LLVM Developers mailing list
llvm...@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
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.
It seems to me that "[[clang::warn_unused_result]] class TypedError" is probably sufficient for ensuring people check a status return value; I'm not sure runtime checking really brings much additional value there.
On Feb 2, 2016, at 5:29 PM, Lang Hames via llvm-dev <llvm...@lists.llvm.org> wrote:Hi All,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.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:1. It carries no context: It tells you what went wrong, but not where or why, making it difficult to produce good diagnostics.2. It's extremely easy to ignore or forget: instances can be silently dropped.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.In fairness to std::error_code, it has some nice properties too:1. It's extremely lightweight.2. It's explicit in the API (unlike exceptions).3. It doesn't require C++ RTTI (a requirement for use in LLVM).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.The scheme has three major "moving parts":1. A new 'TypedError' class that can be used as a replacement for std::error_code. E.g.std::error_code foo();becomesTypedError foo();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.
2. A utility class, TypedErrorInfo, for building error class hierarchies rooted at 'TypedErrorInfoBase' with custom RTTI. E.g.// Define a new error type implicitly inheriting from TypedErrorInfoBase.class MyCustomError : public TypedErrorInfo<MyCustomError> {public:// Custom error info.};// Define a subclass of MyCustomError.class MyCustomSubError : public TypedErrorInfo<MyCustomSubError, MyCustomError> {public:// Extends MyCustomError, adds new members.};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:TypedError foo() {if (SomeFailureCondition)return make_typed_error<MyCustomError>();}TypedError Err = foo();catchAllTypedErrors(std::move(Err),handleTypedError<MyCustomError>([](std::unique_ptr<MyCustomError> E) {// Handle the error.return TypedError(); // <- Indicate success from handler.
}));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:if (TypedError Err = foo())return Err;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.I believe that this scheme addresses many of the shortcomings of std::error_code while maintaining the strengths:1. Context - Custom error classes enable the user to attach as much contextual information as desired.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.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.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.
5. Explicit - TypedError is represented explicitly in the APIs, the same as std::error_code.6. Does not require C++ RTTI - The custom RTTI system does not rely on any standard C++ RTTI features.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.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.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.So - I look forward to hearing your thoughts. :)
Cheers,Lang.Attached files:typed_error.patch - Adds include/llvm/Support/TypedError.h (also adds anchor() method to lib/Support/ErrorHandling.cpp).error_demo.tgz - Stand-alone program demo'ing basic use of the TypedError API.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.
<typed_error.patch><error_demo.tgz><thread_typederror_through_object_creation.patch>_______________________________________________
However, regarding context, I wonder about the best model. When we designed the diagnostic reporting interface (by which I mean the bits in include/llvm/IR/DiagnosticInfo.h and include/llvm/IR/DiagnosticPrinter.h), the ability to carry context was very important. There, however, because the objects are being passed via a callback to the user-installed handler, they can carry pointers/references to objects (Values, Functions, etc.) that will go away once the object that detected the error is destroyed. In the model you're proposing, all of the context must be contained within the error object itself (because, by the time the context is useful, an arbitrary amount of the call stack to the error-detection point has already been unwound). This greatly limits the amount of information that can be efficiently stored as context in the error object. Depending on the use cases, it might be better to pass the context to some kind of error-handler callback than to try to pack it all into a long-lived error object. Thoughts?
On Feb 2, 2016, at 10:42 PM, Lang Hames <lha...@gmail.com> wrote:Hi Mehdi,> I’m not sure to understand this claim? You are supposed to be able to extend and subclass the type of diagnostics? (I remember doing it for an out-of-tree LLVM-based project).You can subclass diagnostics, but subclassing (on its own) only lets you change the behaviour of the diagnostic/error itself. What we need, and what this patch supplies, is a way to choose a particular handler based on the type of the error.
For that you need RTTI, so this patch introduces a new RTTI scheme that I think is more suitable for errors types*, since unlike LLVM's existing RTTI system it doesn't require you to enumerate the types up-front.
> Is your call to catchAllTypedErrors(…) actually like a switch on the type of the error? What about a syntax that looks like a switch?>> switchErr(std::move(Err))> .case< MyCustomError>([] () { /* … */ })> .case< MyOtherCustomError>([] () { /* … */ })> .default([] () { /* … */ })It's similar to a switch, but it's actually more like a list of regular C++ exception catch blocks (the name 'catchTypedError' is a nod to this).The big difference is that you're not trying to find "the matching handler" in the set of options. Instead, the list of handlers is evaluated in order until one is found that fits, then that handler alone is executed. So if you had the following:class MyBaseError : public TypedErrorInfo<MyBaseError> {};class MyDerivedError : public TypedErrorInfo<MyDerivedError, MyBaseError> {}; // <- MyDerivedError inherits from MyBaseError.
and you wrote something like this:
catchTypedErrors(std::move(Err),handleTypedError<MyBaseError>([&](std::unique_ptr<MyBaseError> B) {}),handleTypedError<MyDerivedError>([&](std::unique_ptr<MyDerivedError> D) {}));The second handler will never run: All 'Derived' errors are 'Base' errors, the first handler fits, so it's the one that will be run.We could go for something more like a switch, but then you have to define the notion of "best fit" for a type, which might be difficult (especially if I extend this to support multiple inheritance in error hierarchies. ;). I think it's easier to reason about "first handler that fits”.
_______________________________________________ LLVM Developers mailing list llvm...@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
-- Employee of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
I see the attribute as complimentary. The runtime check provides a stronger guarantee: the error cannot be dropped on any path, rather than just "the result is used". The attribute can help you catch obvious violations of this at compile time.
On Feb 2, 2016, at 11:33 PM, Lang Hames <lha...@gmail.com> wrote:
Hi Mehdi,
> If you subclass a diagnostic right now, isn’t the RTTI information available to the handler, which can then achieve the same dispatching / custom handling per type of diagnostic?> (I’m not advocating the diagnostic system, which I found less convenient to use than what you are proposing)I have to confess I haven't looked at the diagnostic classes closely. I'll take a look and get back to you on this one. :)> > For that you need RTTI, so this patch introduces a new RTTI scheme that I think is more suitable for errors types*, since unlike LLVM's existing RTTI system it doesn't require you to enumerate the types up-front.> It looks like I’m missing a piece of something as it is not clear why is this strictly needed. I may have to actually look closer at the code itself.LLVM's RTTI requires you to define an enum value for each type in your hierarchy (see http://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html), which means you need to know about all your potential subclasses up-front. That's not an option for a generic error class that might be subclassed in arbitrary ways.
The new RTTI system uses something closer to LLVM's Pass IDs: TypedErrorInfo (a utility which all errors must inherit from) introduces a new static char for each error type and uses its address as an ID. When you ask an error value "are you a subclass of type T" (via the isSameOrSubClass method) the call goes to the parent TypedErrorInfo object, which compares T's ID with its own. If it matches it returns true, if it doesn't match then the call gets forwarded to the parent class, then its parent class, and so on. If you hit the root of the type-tree (i.e. TypedErrorInfoBase) without matching the ID, then you weren't a subclass of T.
> Sure, this case shows “success” of the handler, now what is a failure of the handler and how is it handled?Sorry - that was a bad example to choose: That was actually showcasing failure, not success. Success looks like this:
TypedError bar() {
TypedError Err = foo;
if (auto E2 =
catchTypedErrors(std::move(Err),
handleTypedError<MyError>([&](std::unique_ptr<MyError> M) {// Deal with 'M' somehow.
return TypedError();
}))
return E2;// Proceed with 'bar' if 'Err' is handled.
}A key observation is that catchTypedErrors itself returns an error. It has to, because you may not have provided it with an exhaustive list of handlers, and it needs a way to return unhanded errors. So: If no handler gets invoked, catchTypedErrors will just return 'Err' again. If 'Err' was an error, then E2 will also be an error, and you'll immediately return from 'bar', passing responsibility for the error up the stack. So far so good. Now consider what we should do if, instead, we *did* invoke a handler. One option would be to say that if a handler gets invoked then catchTypedErrors always returns 'TypedError()', indicating success, but that's an assertion that any error that's caught is definitely resolved. Sadly, we can't rely on that, so instead we allow the handler to supply the return value for catchTypedErrors. If the handler supplies 'TypedError()' then the error is considered truly 'handled' - E2 becomes TypedError(), the if condition is false (indicating there is no error) and the function goes on its way. If the handler supplies an error, then E2 becomes an error, the if condition is true, and we exit the function immediately, returning the error to the caller.Hope that clears things up a bit. It takes a bit of staring at the first time, but I find it helpful to think of it as being analogous to a 're-throw' in an exception handler: Returning success (i.e. TypedError()) means continue the function, anything else means re-throw the error.
> 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.
+ 1, I'd like to use this throughout lib/ProfileData. It's a bit frustrating to see crashers which simply state: "Malformed profile data". It'd great to know _where_ the issue actually is.
vedant
On Feb 3, 2016, at 10:48 AM, Lang Hames <lha...@gmail.com> wrote:
Hi Mehdi,
> For a generic error class it is not an option indeed, but I was looking at it in the context of LLVM internal use, so just like our RTTI is not an option for “generic RTTI” but fits our need, we could (not should) do the same with ErrorHandling.Definitely. If this was LLVM only there'd be a strong case for using the existing RTTI system. The reason for the new RTTI system is that it would let us re-use this error class in other LLVM projects (LLD, LLDB, etc) without having to enumerate their errors in LLVM.
> Nice, and since this is on the error path we don’t care if it is not “as fast as” the custom LLVM RTTI.Yep.
On Feb 3, 2016, at 11:32 AM, Lang Hames <lha...@gmail.com> wrote:
Hi Mehdi,
> Side question on the related work because I’m curious: did you look for similar generic scheme for error handling offered by other C++ libraries? Maybe the common scheme is to use C++ exceptions but since many folks disable them (hello Google ;-) ) there has been probably many other attempts to address this.I did look around, but not very hard. Among the people I asked, everyone was either using exceptions, std::error_code, or had turned on the diagnostic that James Knight suggested. I did some preliminary web-searching, but that didn't turn up anything interesting. If anybody has seen other error handling schemes of note I'd love to hear about them.> On this topic of not paying the price on the non-error code path, it would be nice to not pay for constructing all the lambda captures when there is no error.If your catchTypedErrors call is under an if-test then the lambdas are in a scope that won't be entered on the non-error path:if (auto Err = foo())if (auto E2 = catchTypedErrors(std::move(Errs), <lambdas here>))return;
I think (though I haven't tested this) that most lambdas should inline away to next to nothing, so I don't expect the overhead to be noticeable in either case.
Hi All,
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.
The main thing I like about the diagnostic system is that it lets us
differentiate two related but independent concepts:
* Giving the human using the program diagnostics about what went wrong.
* Propagating an error to the caller so that the upper library layer
can handle it or pass it up the stack.
For example, when given a broken bitcode file we are able to produce
the diagnostic "Unknown attribute kind (52)" which shows exactly what
we are complaining about. We are able to do that because the
diagnostic is produced close to the spot where the error is detected.
That is way more than what we need to pass to the caller. In fact, the
only cases we need to differentiate are "this is not a bitcode file at
all" and "the file is broken", which we do with the following enum +
std::error_code.
enum class BitcodeError { InvalidBitcodeSignature = 1, CorruptedBitcode };
So we use the context to produce a diagnostic, but the error doesn't
need to store it. Compare that with what we had before r225562.
Note that with error_code one can define arbitrary error categories.
Other advantages are:
* It is easy to use fatal error in simple programs by just using a
diganostic handler that exits.
* Very debugger friendly. It is easy to put a breakpoint at the diag handler.
Given that distinction, what I agree that is really missing is
infrastructure to make sure std::error_code is handled and for
filtering it.
So, could you achieve your objectives by:
* Moving the diagnostic handling code to Support so that all of llvm can use it.
* Defining TypedError as a wrapper over std::error_code with a
destructor that verifies the error was handled?
Thanks,
Rafael
Given that distinction, what I agree that is really missing is
infrastructure to make sure std::error_code is handled and for
filtering it.
So, could you achieve your objectives by:
* Moving the diagnostic handling code to Support so that all of llvm can use it.
Well, everything *could* use it, but things like clang would probably
stay with the more specialized tools they have.
Cheers,
But you do in the diag handler. For example, if you are trying to open
multiple files, some of which are bitcode, you know to ignore
InvalidBitcodeSignature.
> That's the idea behind the 'log' method on TypedErrorInfo: It lets you
> transform meaningful information about the problem into a log message
> *after* you know whether it can be handled or not. Recoverable errors can be
> logged (if the client wants to do so), but they don't have to be.
...
> By contrast, in my system this would be:
>
> class InvalidBitcodeSignature : TypedErrorInfo<InvalidBitcodeSignature> {};
> class CorruptedBitcode : TypedErrorInfo<CorruptedBitcode> {
> public:
> CorruptedBitcode(std::string Msg) : Msg(Msg) {}
> void log(raw_ostream &OS) const override { OS << Msg; }
> private:
> std::string Msg;
> };
This highlights why I think it is important to keep diagnostics and
errors separate. In the solution you have there is a need to allocate
a std::string, even if that is never used. If it was always a
std::string it would not be that bad, but the framework seems designed
to allow even more context to be stored, opening the way for fun cases
of error handling interfering with lifetime management.
> Consider someone who wants to use libObject to write an object-file repair
> tool: A "bad_symbol" error code tells you what went wrong, but not where or
> why, so it's not much use in attempting a fix. On the other hand "bad_symbol
> { idx = 10 }" gives you enough information to pop up a dialog, ask the user
> what the symbol at index 10 should be, then re-write that symbol table
> entry.
Using error results to find deep information you want to patch seems
like a bad idea. A tool that wants to patch symbol indexes should be
reading them directly.
>
>> Other advantages are:
>>
>> * It is easy to use fatal error in simple programs by just using a
>> diganostic handler that exits.
>> * Very debugger friendly. It is easy to put a breakpoint at the diag
>> handler.
>
> This proposal provides the same advantages. I noted the second point in the
> original RFC. The first is easily implemented using an idiom I've seen in
> llvm-objdump and elsewhere:
>
> void check(TypedError Err) {
> if (!Err)
> return;
>
> logAllUnhandledErrors(std::move(Err), errs(), "<tool name>");
> exit(1);
> }
That is not the same that you get with a diagnostic handler. What you
get is an exit after the error was propagated over the library layer,
instead of an exit at the point where the issue is found.
We use the above code now because lib/Object has no diagnostic handler.
> Side-note: you can actually go one better and replace this idiom with:
>
> template <typename T>
> T check(TypedErrorOr<T> ValOrErr) {
> if (!ValOrErr) {
> logAllUnhandledErrors(ValOrErr.takeError(), errs(), "<tool name>");
> exit(1);
> }
> return std::move(*ValOrErr);
> }
Yes, we do pretty much that in ELF/COFF lld.
> Using TypedError as a wrapper for std::error_code would prevent errors from
> be silently dropped, which is a huge step forward. (Though TypedError would
> be a misnomer - we'd need a new name. :)
CheckedError?
> Using a diagnostic handler would allow richer diagnostics from libObject,
> but it supports a narrower class of error-recovery than my proposal. I'm
> inclined to favor my proposal as a strictly more general solution.
I would prefer the diagnostic handler for exactly the same reason :-)
Generality has a cost, and I don't think we should invest on it until
we have a concrete case where that is needed. Given that I would
suggest going the more incremental way. Add a CheckedError that wraps
error_code and use to make sure the errors are checked. When a better
diagnostic is needed, pass in a diagnostic handler.
If we ever get to the point were we really want to *return* more than
an error_code, it will be a much smaller refactoring to
s/CheckedError/TypedError/.
Cheers,
> > I don't think these are really independent. Whether or not you need to emit
> > a diagnostic depends on whether a caller can handle the corresponding error,
> > which isn't something you know at the point where the error is raised.
> But you do in the diag handler. For example, if you are trying to open
> multiple files, some of which are bitcode, you know to ignore
> InvalidBitcodeSignature.
That means anticipating the kinds of errors you do/don't want to recover from at the point at which you insatiate your diagnostic handler. At that point you may not have the information that you need to know whether you want to recover. E.g. What if one path through library code can recover, and another can't?
Worse still, what if the divergence point between these two paths is in library code itself? Now you need to thread two diagnostic handlers up to that point. This scales very poorly.Simply put: diagnostic handlers aren't library friendly. It's tough enough to get all libraries to agree on an error type, without having them all agree on a diagnostic handlers too, and thread them everywhere.
But I think it makes total sense for Clang to use a diagnostic handling approach for the most part (all the possible ways that C++ can be written incorrectly essentially amount to the same thing for a consumer of Clang - code was bad & I need some text (and fixits, notes, etc) to tell the user why), the IR verifier similarly - and possibly LLD, for example, depending on how broad the use cases become.
On Feb 9, 2016, at 3:27 PM, Rafael Espíndola via llvm-dev <llvm...@lists.llvm.org> wrote:If we ever get to the point were we really want to *return* more than
an error_code, it will be a much smaller refactoring to
s/CheckedError/TypedError/.
But they are always created, even if it as error the caller wants to
ignore. For example, you will always create a "file foo.o in bar.a is
not a bitcode" message (or copy sufficient information for that to be
created). With a dignostic handler no copying is needed, since the
call happens in the context where the error is found. It is easy to
see us in a position where a lot of context is copied because some
client somewhere might want it.
So I am worried we are coding for the hypothetical and adding
complexity. In particular, if we are going this way I think it is
critical that your patch *removes* the existing diagnostic handlers
(while making sure test/Bitcode/invalid.ll still passes) so that we
don't end up with two overlapping solutions. We were still not even
done converting away from bool+std::string :-(
>> This highlights why I think it is important to keep diagnostics and
>> errors separate. In the solution you have there is a need to allocate
>> a std::string, even if that is never used.
>
> Errors are only constructed on the error path. There is no construction on
> the success path.
But they are always created, even if it as error the caller wants to
ignore. For example, you will always create a "file foo.o in bar.a is
not a bitcode" message (or copy sufficient information for that to be
created). With a dignostic handler no copying is needed, since the
call happens in the context where the error is found. It is easy to
see us in a position where a lot of context is copied because some
client somewhere might want it.
So I am worried we are coding for the hypothetical and adding
complexity. In particular, if we are going this way I think it is
critical that your patch *removes* the existing diagnostic handlers
(while making sure test/Bitcode/invalid.ll still passes) so that we
don't end up with two overlapping solutions.
In which case we do need a plain checked error_code. Right now we use
a diagnostic handler in the BitcodeReader, but it is really easy to
miss propagating an error out. We shouldn't be in a position where we
have to decide to use an diagnostic handler or have checked errors. It
should be possible to have both if it is not desired that the new
error handling replaces the use of diagnostic handling.
What prevents you from using a diag handler in the jit server that
sends errors/warnings over the RPCChannel?
What prevents you from using a diag handler in the jit server that
sends errors/warnings over the RPCChannel?
What prevents you from using a diag handler in the jit server that
sends errors/warnings over the RPCChannel?
That means we will forever have an incomplete transition.
We still haven't finished moving to the "new" naming style. Moving to
the current path handling took from Nov 2009 to Jun 2013.
I am still not convinced that the new system is better than a
asserting wrapper over std::error_code with diag handlers, but I don't
have the time to change Orc to use to show it. Given that I also
failed to convince you otherwise, we will probably have to go with the
view of who can actually code this.
But *please*, make an effort to use it everywhere. We still have a mix
of bool+std::string, std::error_code and ErrorOr. Are each of these
better than what you are proposing in one case or the other? Probably.
Is it worth it having unique snow flakes? I don't think so.
In particular, please don't change just the "MachO-specific part of
X". In the past I have made an effort to keep MachO bits current when
working in MC and lib/Object. Please do the corresponding effort. For
example, if you change llvm-nm, please change all of it
FWIW, I support the effort Lang is leading, so it is a +1 on my side to move on!
And I also agree with Rafael that it would be really better to limit as much as possible the mix of multiple reporting inside a component.
Lang: please CC me on the reviews, I'll be happy to follow the development.
--
Mehdi
No, but it is even less to assume it will re-write itself.
A long time ago "incomplete transitions" was listed as one of the main
issues in the gcc codebase
(ftp://ftp.cygwinports.org/pub/gcc/summit/2003/A%20maintenance%20programmer's%20view%20of%20gcc.pdf)
I don't thing our situation is as bad, but it is getting uncomfortable.
I like this idea in general. It's a better implementation of what
ErrorOr originally was before we removed the custom error support
because it wasn't used. In fact I would actually support outright
replacing ErrorOr with this if it can be done safely, as I find the
name TypedErrorOr a bit long.
- Michael Spencer
The main differences are
* This will hopefully be used.
* TypedErrorOr is really a TypedError or T. The situation before was
not that ErrorOr was std::error_code or T.
Since we are adding it, I would also support replacing every use of
ErrorOr with TypedErrorOr and renaming it.
Cheers,
Rafael
> I like this idea in general. It's a better implementation of what
> ErrorOr originally was before we removed the custom error support
> because it wasn't used. In fact I would actually support outright
> replacing ErrorOr with this if it can be done safely, as I find the
> name TypedErrorOr a bit long.
The main differences are
* This will hopefully be used.
* TypedErrorOr is really a TypedError or T. The situation before was
not that ErrorOr was std::error_code or T.
Since we are adding it, I would also support replacing every use of
ErrorOr with TypedErrorOr and renaming it.
That is the case now. Sorry, Mchiael and I were talking about ancient
history when ErrorOr<T> was *not* an std::error_code or T.