void f(lazy int x) @nogc {
x; // Error: @nogc function 'f' cannot call non-@nogc delegate 'x'
}
void g(lazy int x) nothrow {
x; // Error: 'x' is not nothrow
}
There's no way to annotate the implicit delegate is @nogc, nothrow, etc.
Comment #1 by eyal — 2017-06-09T19:33:48Z
This also breaks purity type checking:
@safe:
import std.stdio:writeln;
void f(lazy int x) pure {
x();
}
unittest {
int g() {
writeln("impure func called by pure func!");
return 5;
}
auto xx = (() => g());
writeln("-> f");
f(xx());
writeln("<- f");
}
outputs:
-> f
impure func called by pure func!
<- f
Comment #2 by eyal — 2017-06-09T19:44:40Z
Better bug reproduction:
@safe:
void f(ref int counter, lazy int x) pure {
x();
counter++;
x();
}
void main() {
int counter;
int g() {
import std.stdio:writeln;
writeln("counter: ", counter);
return 5;
}
f(counter, g);
}
// prints:
counter: 0
counter: 1
Making it clearer that we're being allowed to pass an impure delegate into a pure function that then calls it.
The solution, clearly is to treat "lazy" is mere syntactic sugar to passing delegates - thus adding syntactic support for @nogc/etc attribute annotation on "lazy" parameters - and specifying their attributes just like is done for delegates.
The assumption that lazy parameters are all pure is wrong (thus this ticket). The assumption that lazy parameters are all gc/throw is wrong (thus the other ticket).
It *cannot* be inferred unless lazy parameters make everything a template, which would be unfortunate. Just like delegate parameter attributes cannot be inferred - you have to manually specify them or templatize over the delegate type.
Comment #5 by robert.schadek — 2024-12-13T18:52:31Z