Assertions getting corrupted

Jonathan M Davis newsgroup.d at jmdavisprog.com
Thu Oct 26 06:27:53 UTC 2017


On Thursday, October 26, 2017 09:04:56 Shachar Shemesh via Digitalmars-d 
wrote:
> I'm going to hate myself for suggesting this, but here goes.
>
> There's a fundamental problem with scope(exit) and scope(failure).
> Consider the following code:
>
> {
>    a = allocate_something();
>    scope(exit) a.cleanup();
>
>    ...
>
>    assert(a.nothingHorribleWentWrong);
> }
>
> Ideally, if that assert fails, you'd want the core dump and backtrace
> from the assertion point. That's the earliest point in which the problem
> is visible.
>
> Except, all too often, what will happen is that the assert will throw an
> AssertionError. The scope(exit) will run, and then a.cleanup will
> segfault because, well, something horrible *did* go wrong. This makes it
> much more difficult to find out what actually went wrong.
>
> Target: scope(failure) and scope(exit) should not run when the exception
> thrown is an AssertError.
>
> Which leaves the floor open to two questions:
> 1. What other exceptions shouldn't run scope(exit) and scope(failure)?
> 2. Do we want scope(something) that *will* run on AssertError?
>
> Obviously, the answer to 2 is linked to 1.
>
> I think a reasonable approach is to say "scope(failxit) should on all
> Throwables except Errors". Note that this is not the same as saying
> "scope(failxit) runs only on Exceptions".
>
> As for 2, that's the part I'm going to hate myself for. I will not
> object to adding "scope(fatal_error)", that do run on those cases
> (though I think just adding catch for those rare cases ought to be
> enough).

The language guarantees that scope(exit), scope(failure), and destructors
will be run when an Exception is thrown. It does _not_ guarantee that any of
those will be run when an Error is thrown (one case where they definitely
aren't is when an Error is thrown from a function that's nothrow).

Throwables that aren't Exceptions or Errors really aren't considered in
these discussions normally, since almost no one ever derives from Throwable,
and I don't think it's really intended that anyone do so much as it is
possible (and I'm not sure why you would), but on the whole, the compiler
cares about Exceptions vs other Throwables, and whatever is said for Errors
normally applies to any Throwables other than Exceptions. Certainly, given
the nature of nothrow, the language can't guarantee that cleanup is run for
anything that is thrown that is not derived from Exception, regardless of
whether it's derived from Error.

Walter believes that it's worse to do cleanup when an Error is thrown than
it is to not do cleanup, because the program is an unknown and invalid
state, and the cleanup code could do more harm than good. Others have argued
that it's better to run as much cleanup code as possible and that it's worse
to skip cleanup, since usually, the program will be in a valid enough state
that the cleanup will work and other problems will be avoided by doing that
cleanup.

Originally, cleanup did _not_ occur when anything other than an Exception
was thrown, but several years ago, someone went and "fixed" druntime so that
the cleanup did occur when it could, meaning that most any cleanup not
involving nothrow will be run for Errors and other non-Exceptions. As I
understand it, Walter disagrees with that change, but he's never mandated
that it be undone or made the effort to undo it himself.

Personally, overall, I agree with Walter and think that no cleanup should be
done when any Error is thrown with the exception that having cleanup run for
AssertErrors in unit tests is quite useful (scope(failure) in particular,
since it makes it easy to print out information when a failure occurs and
only when a failure occurs). So, I'd hate to see cleanup being skipped it
unittest blocks when an assertion there fails. However, this topic has been
hotly debated on several occasions, and IIRC, the majority of the folks who
got involved in those discussions disagreed with Walter. It does sound like
you at least partially agree with him though.

If you want Errors and Throwables not derived from Exception to be treated
differently, I think that you're going to need a really good argument for
why, and the nature of nothrow means that it's impossible for cleanup to be
guaranteed for anything other than Exceptions - though clearly, the current
situation shows that it's possible to do clean-up for non-Exceptions at
least some of the time, even if the language technically does not guarantee
that cleanup is done for any Throwables which aren't Exceptions.

- Jonathan M Davis



More information about the Digitalmars-d mailing list