On Wednesday, 2 November 2022 at 22:01:24 UTC, Paul Backus wrote:
> I'm inclined to say the implementation is right, since the code does not actually violate memory safety.
According to the latest D Foundation meeting summary, Dennis Korpel is looking into making @safe
inference work properly for recursive functions, so it's possible that this limitation will be lifted entirely in the future.
Right.
When a function calls itself directly, attribute checks for that call are ignored.
However, when there's one or more functions in between, the reasoning goes "I'm calling a function that can't finish attribute inference right now, so conservatively assume all attributes failed to infer to be on the safe side." It seems the specification was modified to document this shortcoming of the implementation.
I wish we could simply assume all attributes were inferred, which would make this work:
void fun1()() { fun2(); }
void fun2()() { fun1(); }
void main() @safe pure nothrow @nogc
{
fun1(); // currently fails
}
But here's a more tricky case:
@system void systemFunc();
void fun1()()
{
alias T = typeof(fun2());
systemFunc();
}
void fun2()()
{
fun1();
}
void main0() @system
{
fun1();
}
void main() @safe
{
fun2(); // not system!
}
The compiler analyzes fun1
, then fun2
, and then needs to decide if the call to fun1
is safe.
If it assumes 'yes', then later when it sees the call to systemFunc()
, it would need to go back and make fun2()
@system, and re-analyze the function body of fun1
where T
is now a @system function.
DMD is not suited to do this kind of backtracking, and that would potentially be very slow as well, so this probably won't be fixed in the general case.
However, in the absence of typeof() and other function type dependencies, a list of callees for each function can be maintained to solve the most common case of mutual function dependencies through simple function calls.