[dmd-internals] Throwing Errors
Jonathan M Davis
jmdavisProg at gmx.com
Mon Mar 12 14:06:48 PDT 2012
On Monday, March 12, 2012 21:29:25 Don Clugston wrote:
> I went to a lot of trouble to ensure the exception chaining behaviour
> of Errors vs Exceptions.
>
> Personally, I just don't buy the argument that the entire system is in
> an invalid state when an Error occurs. It's a sign that some subsystem
> is in a invalid state, but how much of the whole app is invalid,
> depends on the particular situation. For example, if you're
> extensively using class invariants and contracts, and you get an
> AssertError, then the possible damage is pretty small. Especially if
> it is an assert failure in a pure @safe function; cleanup will be
> perfectly safe in that situation.
>
> I would have thought that if you had a truly fatal error, you would
> have called exit() or something, instead of throwing an Error.
When an Error is thrown, the program is in a potentially invalid state. How
invalid it is, depends on the Error. And the fact that cleanup isn't
guaranteed _definitely_ puts the program in an invalid state when an Error is
thrown unless it's caught very close to its throw point.
I'm not sure how much it matters whether there is any attempt to cleanup from
Errors except that if the Error _does_ indicate something which would
invalidate the cleanup code, it could cause serious problems. The main one
that I can think of is OutOfMemoryError, since if any cleanup code then tries
to allocate anything, it's going to fail. Of course, that would then result in
_another_ OutOfMemoryError, so it may be that that pretty much takes care of
itself and really isn't a big deal.
Certainly, in the general case, even if cleanup _is_ guaranteed, catching
Errors is a bad idea because of the types of problems that they generally
indicate. But there _are_ cases where catching Errors can be useful - the most
prevalent one being catching AssertErrors in unit tests. Anyone trying to use
a fancier unit testing framework (which I don't personally see any need for,
but some people are _very_ interested in it - e.g. Jacob Carlborg) is going to
have to do that. And in rare cases, even catching OutOfMemoryError makes some
sense. You just have to know what you're doing and be careful.
The _biggest_ thing with Errors is simply that they're not Exceptions, so
catch(Exception e) {}
won't catch them, and nothrow isn't affected by them. That right there provides
their primary benefit. Personally, I'm on the fence as to whether attempting
cleanup when an Error is thrown is a good idea or not. Certainly, in the unit
testing case, it would certainly be desirable. Most of the rest, it probably
doesn't matter all that much, but it would arguably be desirable to skip
cleanup for them so that the amount of weird stuff that can happen on shutting
down the program can be minimized. But of course, it also makes it harder to
guarantee that certain things are done on shutdown, and it's not all that
uncommon to have stuff that you want to _guarantee_ runs on shutdown, even if
it's not a clean shutdown (or even _especially_ if it's not a clean shutdown).
The other thing to consider is the fact that very few people seem to
understand that Errors in flight aren't guaranteed to execute scope statements,
destructors, or finally blocks. So, there's definitely code being written which
relies on behavior that is not guaranteed. The fact that it currently happens
in most (all?) cases just makes it worse, since then there's more stuff that
will break (particularly unit testing frameworks like Jacob's) when it does.
Heck, simply being able to rely on scope(failure) running for AssertErrors
would be valuable for unit testing, since it makes it much easier to output
extra information on failures. And I expect that _that_ is done quite
frequently. I'm pretty sure that I've done it a number of times myself,
forgetting that that's not guaranteed to work. So, there's definitely code out
there which relies on Errors triggering all of the appropriate cleanup stuff
just like Exceptions do.
I'd argue that we should decide whether cleanup on Errors should be guaranteed
or not and then make it so that the compiler _always_ follows this behavior so
that the behavior - whatever it may be - _is_ guaranteed. Having the behavior
be undefined is just causing problems.
- Jonathan M Davis
More information about the dmd-internals
mailing list