Comment #0 by john.loughran.colvin — 2021-07-28T16:06:01Z
void foo(int[] a, int b) @nogc {
import std.algorithm.iteration : map;
size_t i = 0;
foreach (el; a.map!(x => x + b))
a[i++] = el;
}
onlineapp.d(1): Error: function `onlineapp.foo` is `@nogc` yet allocates closures with the GC
onlineapp.d(4): onlineapp.foo.__lambda4 closes over variable b at onlineapp.d(1)
or the more common case for us:
a.map!(x => a + b).array;
which ends up allocating an unnecessary extra closure, even if `a.length` is 0.
It is not clear to me exactly how this could be fixed even conceptually, but it is worth documenting as a real problem.
I want to use the GC freely, but I also want to be in control. Random closures that you can't spot easily cause a lot of problems. Using `@nogc` is way too restrictive for these use cases, e.g. you might be totally fine with GC allocation *inside* the lambda, you just don't want to do another one to capture. Every allocation is individually expensive, often dwarfing the rest of the code in the function.
Comment #1 by kinke — 2021-07-29T05:12:42Z
LDC optimizes it away with -O for the 2 given testcases (via the GC2Stack IR pass) - conceptually, it can prove that the closure doesn't escape after inlining *everything*, and moves it back to the stack.
Comment #2 by robert.schadek — 2024-12-01T16:39:05Z