What should happen when the assert message expression throws?

RazvanN razvan.nitu1305 at gmail.com
Fri Nov 18 12:36:14 UTC 2022


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:

```d
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:

```d
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:

```d
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:

```d
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:

```d

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


More information about the Digitalmars-d mailing list