RFC: Change what assert does on error

Jonathan M Davis newsgroup.d at jmdavisprog.com
Sat Jul 5 06:20:09 UTC 2025


On Friday, July 4, 2025 11:48:15 AM Mountain Daylight Time Walter Bright via Digitalmars-d wrote:
> On 7/4/2025 12:21 AM, Jonathan M Davis wrote:
> > Even if recovering is not acceptable, if the proper clean up is done when
> > the stack is unwound, then it's possible to use destructors, scope
> > statements, and catch blocks to get additional information about the state
> > of the program as the stack unwinds. If the proper clean up is not done as
> > the stack unwinds, then those destructors, scope statements, and catch
> > statements will either not be run (meaning that any debugging information
> > which could have been obtained from them wouldn't be), and/or only some of
> > them will be run. And of course, for each such piece of clean up code that's
> > skipped, the more invalid the state of the program becomes, making it that
> > much riskier for any of the code that does run while the stack unwinds to
> > log any information about the state of the program.
>
> Executing clean up code (i.e. destructors) is not at all about logging errors,
> because they happen through the normal error-free operation of the program. If
> you were logging normal usage, you'd want the logs from before the fault
> happened, not after.

The programmer isn't necessarily looking to log normal usage. In plenty of
cases, they may be trying to get additional information specifically because
an Error was thrown, and they want that information in order to have some
hope of debugging the problem (especially if this isn't a common problem or
it's a user who isn't a programmer who encounters it).

Timon uses stuff like scope(failure) and catch(Error e) { ... throw e; }
right now for getting information out of his program when an Error is
thrown, with the caveat that not all of the clean up code gets run, making
the whole endeavor riskier than it would be otherwise. I don't know exactly
what information he gets out of it, but if I understand correctly, it's used
to produce the information that gets put into an error window in the UI
which the user is then supposed to copy the information from in a bug report
(and then presumably, when they close that window, it closes the program,
since Timon isn't trying to make the program continue to run after that).
This isn't stuff that gets logged during normal operation. It's specifically
for getting information about what the program was doing that resulted in
the Error so that he has some hope of debugging it in spite of the fact that
he's dealing with a user who isn't tech-savvy.

And just in general, if scope(failure) is being used, the programmer may
want to log addtional information that they wouldn't have wanted to log
otherwise. For instance, I do this in unit tests when the assertion failure
is inside nested loops, and I need to know which iterations each loop is in
when the failure occurs (and certainly wouldn't want to log anything if
there weren't a failure).

Destructors probably aren't going to be used to get additional information,
since those do run when there are no Errors, but scope(failure) and
catch(Error e) { ... throw e; } can certainly be used specifically for when
a Throwable of some kind is thrown, and having those be skipped at any
point means missing out on whatever information the programmer was trying to
get on failure, and having the destructors skipped would mean that the code
with scope(failure) or catch(Error) would then be dealing with code that was
potentially in a state that wasn't memory safe, because clean up code was
skipped, whereas if the clean up code hadn't been skipped, it might have
been perfectly memory safe even though an Error was in flight.

Even if you think that it's too risky to have the clean up code run for
Errors as the default behavior, there are clearly use cases where getting
additional information about the state of the program is worth far more than
the risk that doing the clean up code might cause further problems -
especially when in many cases, Errors are thrown _before_ anything that
isn't memory safe is done (e.g. array bounds checking throws an Error before
accessing the memory out-of-bounds, not after). And at least having the
option to configure a program such that the full clean up code is run even
with Errors would make getting information out of a program as it terminates
due to an Error more memory safe and less error-prone - as well as simply
making it so that more information can be got out of the program, because
the clean up code that's used to get information is actually all run. So,
the folks who need that behavior can have it even if it's not the default.

- Jonathan M Davis






More information about the Digitalmars-d mailing list