Comment #0 by su+dlangissues — 2022-05-16T07:54:43Z
the following code allocates, bypassing @nogc and -vgc.
struct Forward(alias F) {
auto opCall(Ts...)(Ts args) {
return F(args);
}
}
auto bar(T)(T a) @nogc nothrow @safe {
auto f()
{
return a;
}
Forward!f val;
return val;
}
Comment #1 by dkorpel — 2022-05-16T11:31:18Z
An uninstantiated template generates no code, so here's the test modified a bit:
```
struct Forward(alias F)
{
auto call()()
{
return F();
}
}
auto bar(int a) @nogc nothrow @safe
{
auto f()
{
return a;
}
return Forward!f();
}
extern(C) void main() @nogc
{
assert(bar(3).call() == 3);
}
```
Compile with -betterC and you get: undefined reference to '_d_allocmemory'
Comment #2 by bugzilla — 2022-06-05T03:24:09Z
(In reply to Dennis from comment #1)
> auto call()()
Rewriting as:
> auto call()
and it gives the error:
test2.d(8): Error: function `test2.bar` is `@nogc` yet allocates closures with the GC
test2.d(10): test2.bar.f closes over variable a at test2.d(8)
which is what we expect. The trouble with the template version is the semantic3() of bar() should run the semantic3() of Forward.call, which is necessary to discover that bar() requires a closure. But it does not do the semantic3() of Forward.call until after semantic3() for bar() has completed.
Comment #3 by bugzilla — 2022-06-05T04:31:19Z
Some further study.
The semantic analysis for call()() cannot be done during the semantic analysis for bar() or Forward, because the template arguments to call()() are as yet unknown. We only find out the arguments when main() takes the return value (an instance of Forward) and calls it.
To fix this, the argument `f` supplied to the parameter `alias F` of `Forward` must be regarded as escaping when the instance of `Forward` is returned from `bar()`.
Not doing this is a major hole in the @safe system.
Regarding `f` as escaping produces far too many false positives.
1. semantic3 (the function body) analysis is done for a function. No gc use is found, and no escapes of an aliased reference are found, so the function is inferred @nogc. This also enables some NRVO optimizations.
2. code is generated that depends on the @nogc inferred attribute of the function, and/or does NRVO.
3. code is generated via another template getting expanded that escapes the alias reference.
4. this gets detected by the code the decides whether to generate a closure or not, and now (2) is invalid, but it's too late.
Comment #7 by dlang-bot — 2022-07-22T00:55:21Z
dlang/dmd pull request #14183 "fix Issue 23112 - code passes @nogc, allocates anyway" was merged into master:
- 7797df8b69ef91210753293060ca9454f078d35d by Walter Bright:
fix Issue 23112 - code passes @nogc, allocates anyway
https://github.com/dlang/dmd/pull/14183