[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