I have stumbled upon: https://issues.dlang.org/show_bug.cgi?id=17226
I want to fix it, however, the solution is not obvious.
Take this code:
import std.string;
void foo(int i)
{
// In this case a %s is forgotten but it could be any other trivial error.
assert(i == 42, format("Bad parameter:", i));
}
void main()
{
foo(43);
}
If format
throws, then the Exception is thrown masking the assert error.
Unfortunately, I don't see a clear way to rewrite this to valid D code as to catch the exception and then assert. Ideally, we could do something along the lines of:
assert(i == 42,
(const(char)[] msg,
try { msg = format("Bad parameter:", i); },
catch (Exception e) { msg = "Assert message evaluation has failed";},
msg)
);
This rewrite would be done only if it is determined that the assert message may throw. However, the current dmd-fe implementation does not allow for such a construction and I think that it might be overkill to implement the necessary machinery just to support this case.
The try catch block can also be generated outside of the assert expression:
auto msg;
try { msg = format("Bad parameter:", i);}
catch (Exception e) { msg = "Assert message evaluation has failed";}
assert(i == 42, msg);
The difference between the 2 is that in this case we are evaluating the msg regardless of whether the assert condition is true or false.
Also, we need to take into account the case where the user tries to catch the exception by himself:
void foo(int i)
{
try
{
// In this case a %s is forgotten but it could be any other trivial error.
assert(i == 42, format("Bad parameter:", i));
}
catch(Exception e) { /* do some sort of fix up with the message */}
}
void main()
{
foo(43);
}
Today, this code runs successfully and no AssertError is thrown. If we automatically catch the exception we might break such code (although I would argue it would not be too dramatic).
An alternative solution would be to deprecate having an assert message that may throw. This has the advantage that it avoids complexity inside the compiler and the user is forced to write:
auto msg = /* do whatever you want with throwing code */
assert(cond, msg);
If the user wants to catch the exception or not, it's his/hers business, but then the compiler has defined semantics in all situations.
What do you think? Is deprecating having an assert message that may throw a severe restriction? Are there other rewrites that I am missing?
Cheers,
RazvanN