Unit tests, asserts and contradictions in the spec

Jacob Carlborg doob at me.com
Wed Feb 6 21:02:05 UTC 2019


In this pull request [1] there's some discussion related to the topic of 
this post, which I would now like to make more visible.

The spec mentions that if an assertion fails the program enters invalid 
state:

"1. The first AssignExpression must evaluate to true. If it does not, an 
Assert Failure has occurred and the program enters an Invalid State." [2]

And:

"Undefined Behavior: Once in an Invalid State the behavior of the 
continuing execution of the program is undefined." [2]

Currently `assert` throws an `AssertError` if an assertion fails. This 
is mentioned in the spec to be implementation defined:

"Implementation Defined: The behavior if the first AssertExpression is 
evaluated and is false is also typically set with a compiler switch and 
may include these options:
5. throwing the AssertError exception in the D runtime library" [2]

The spec also mentions that `assert` has different semantics in 
`unittest` blocks or `in` contract:

"3. AssertExpression has different semantics if it is in a unittest or 
in contract." [2]

And:

"3. Unlike AssertExpressions used elsewhere, the assert is not assumed 
to hold, and upon assert failure the program is still in a defined 
state." [3]

Subclasses of `Error` are not supposed to be caught because they 
represent unrecoverable runtime errors. I failed to find anything about 
this in the spec but the documentation for the `Error` class specifies that:

"This represents the category of Throwable objects that are not safe to 
catch and handle. In principle, one should not catch Error objects, as 
they represent unrecoverable runtime errors. Certain runtime guarantees 
may fail to hold when these errors are thrown, making it unsafe to 
continue execution after catching them." [4]

For as long as I can remember the D runtime has been catching Throwables 
(exceptions and errors) [5]. This might be ok since the application will 
terminate shortly after. The default unit test runner will also catch 
Throwables and continue running the tests in a test fails.

So the problem is that you're not supposed to catch Errors, but you need 
to do that to implement a unit test runner that will continue after a 
failed test. If we only look at what's specified for `assert`, it should 
be fine because according to the spec if an assertion fails in a 
`unittest` block the program is still in a valid state.

The specific problem for this pull request [1] is to allocate memory in 
a function that needs to be `@nogc`, `nothrow`, `pure` and `@safe`.

[1] https://github.com/dlang/druntime/pull/2479
[2] https://dlang.org/spec/expression.html#assert_expressions
[3] https://dlang.org/spec/unittest.html
[4] https://dlang.org/phobos/object.html#.Error
[5] 
https://github.com/dlang/druntime/blob/bc940316b4cd7cf6a76e34b7396de2003867fbef/src/rt/dmain2.d#L480-L484

-- 
/Jacob Carlborg


More information about the Digitalmars-d mailing list