@nogc and Exceptions

Quirin Schroll qs.il.paperinik at gmail.com
Thu Sep 29 12:12:34 UTC 2022


In the [Q&A of Átila’s talk at DConf at 
1:13:45](https://youtu.be/ksNGwLTe0Ps?t=4425), someone asks if 
allocating and throwing exceptions might be a the case where you 
wouldn’t mind the GC even in supposed `@nogc` code: When an 
`Error` is thrown, the application is doomed anyways; when an 
`Exception` is thrown, you already subscribed to inefficient 
execution.

There’s a few options:
* Ignore the issue at a language level and just lie (code below): 
Make an `Exception` allocating function and cast it to `@nogc`. I 
do not know if that is UB.
* Make `@nogc` not apply to a `Throwable` allocated in a `throw` 
expression.
* Introduce Yet Another Damn Function Attribute (YADMA) 
`@nogcUnlessThrown`.

The code to lie:
```d
template throw_new(E : Throwable, Args...)
{
     alias doAllocate = function E(Args args) => new E(args);
     noreturn throw_new(Args args) @nogc
     {
         import std.algorithm.comparison : among;
         enum isSafe = 
"@safe".among(__traits(getFunctionAttributes, doAllocate)) > 0;
         enum isPure =  
"pure".among(__traits(getFunctionAttributes, doAllocate)) > 0;
         alias FP = mixin("E function(Args) @nogc ",
             isSafe ? "@safe " : "", isPure ? "pure" : "");
         immutable hackedAllocate = (() @trusted => 
cast(FP)(doAllocate))();
         throw hackedAllocate(args);
     }
}

void test() @nogc @safe pure
{
     import std.format : FormatException;
     throw_new!FormatException("msg");
}

void main() @safe
{
     import std.format : FormatException;
     try
     {
     	test();
     }
     catch (FormatException e)
     {
         import std.stdio;
         writeln(e.msg);
     }
}
```
Some explanation for why it is like this:
* One has to use `alias doAllocate = function E(Args args) => new 
E(args);` instead of a normal function definition because a 
normal function definition does not infer attributes.
   `throw_new` has attributes inferred.
* `isSafe` and `isPure` take care that those attributes are 
carried through if `E`’s constructor happens to have them.
* `hackedAllocate` is created via a `@trusted` block because 
adding `@nogc` is not `@safe`. It only trusts the cast, not the 
call.
* Perfect forwarding for the arguments is not trivial (I tried), 
but probably not needed anyway.


More information about the Digitalmars-d mailing list