The following code used to compile but does not compile with DMD >= 2.086.1
File closuregc.d:
```
struct S
{
void delegate() @nogc dg;
@nogc this(scope void delegate() @nogc dg)
{
this.dg = dg;
}
}
@nogc void foo()
{
int num;
scope s = S(() @nogc { auto dummy = num; }); // No error.
}
struct STemplate(T)
{
void delegate() @nogc dg;
@nogc this(scope void delegate() @nogc dg)
{
this.dg = dg;
}
}
@nogc void fooTemplate()
{
int num;
scope s = STemplate!int(() @nogc { auto dummy = num; });
// closuregc.d(27): Error: function closuregc.fooTemplate is @nogc yet allocates closures with the GC
// closuregc.d(30): closuregc.fooTemplate.__lambda1 closes over variable num at closuregc.d(29)
}
```
Compilation with -dip1000 does work, but without it it no longer works.
Note that the error only happens for the templated case. For the non-templated struct, there is no problem.
Comment #1 by johanengelen — 2020-02-22T23:31:53Z
Extra note: the error is only triggered if the delegate is defined in an @nogc function. Without @nogc, this is a silent performance regression (or worse).
Comment #2 by bugzilla — 2020-04-04T10:23:12Z
Minimal test case:
struct S(T) {
void delegate() dg;
this(scope void delegate() dg)
{
this.dg = dg;
}
}
@nogc void fooTemplate() {
int num;
void foo() { int dummy = num; }
scope s = S!int(&foo);
}
Comment #3 by bugzilla — 2020-04-05T07:14:09Z
Here's the problem. There were two PRs:
https://github.com/dlang/dmd/pull/8504
which extended checking of addressing of delegates to constructor calls with the line in expressionsem.d:
if (global.params.vsafe && checkConstructorEscape(sc, cx, false))
return setError();
which winds up calling the line in escape.d:
if (va && va.isScope() && fd.tookAddressOf && global.params.vsafe)
--fd.tookAddressOf;
added by PR:
https://github.com/dlang/dmd/pull/7981
which when combined produces the regression. The regression goes away when the two checks for global.params.vsafe (i.e. dip1000) are removed. The message did not appear before because constructor calls were not checked for escaping arguments.
Comment #4 by dlang-bot — 2020-04-05T07:18:00Z
@WalterBright created dlang/dmd pull request #11006 "fix Issue 20596 - [REG2.086] Error on missed stack allocation for clo…" fixing this issue:
- fix Issue 20596 - [REG2.086] Error on missed stack allocation for closure for template
https://github.com/dlang/dmd/pull/11006
Comment #5 by dlang-bot — 2020-04-14T16:26:04Z
dlang/dmd pull request #11006 "fix Issues 20596, 20967 - [REG2.086] Error on missed stack allocation for clo…" was merged into master:
- 3d746b956b85d07646fe901d5e705306ab5027b9 by Walter Bright:
fix Issues 20596, 20597 - Error on missed stack allocation for closure for template
https://github.com/dlang/dmd/pull/11006
Comment #6 by johanengelen — 2020-08-09T10:30:32Z
Reopening, because the regression is not entirely fixed.
See this slightly modified testcase, where variable `s` is not explicitly marked `scoped`. The compiler does not deduce `scope` for `s`. This used to compile and not GC-allocate (<= 2.085) but no longer does.
```
struct S(T) {
void delegate() dg;
this(scope void delegate() dg)
{
this.dg = dg;
}
}
@nogc void fooTemplate() {
int num;
void foo() { int dummy = num; }
auto s = S!int(&foo); // not explicitly defined as `scope`
}
```
Comment #7 by johanengelen — 2020-08-09T10:42:28Z
Another example that fails compilation:
```
struct S(T) {
void delegate() dg;
this(scope void delegate() dg)
{
this.dg = dg;
}
}
// The explicit `scope` here is not used in `fooTemplate`
@nogc auto dosomething(scope S!int s)
{
}
@nogc auto fooTemplate() {
int num;
void foo() { int dummy = num; }
dosomething(S!int(&foo));
}
```