2
Vote

GeneratorRewriter incorrectly handles block scoped variables

description

Ast.Loop(
Ast.Block(new[] { closure_var },
    Ast.Call(list_var, typeof(List<Func<int>>).GetMethod("Add"), Ast.Lambda<Func<int>>(closure_var)),
    Ast.PostIncrementAssign(closure_var),
    AstUtils.YieldReturn(genLabel, Ast.Constant(null))
),
break_label
)
 
var l = AstUtils.GeneratorLambda<Func<List<Func<int>>, IEnumerator<object>>>(genLabel, body, new[] { list_var });
 
var list = new List<Func<int>>();
var e = l.Compile()(list);
 
e.MoveNext();
e.MoveNext();
e.MoveNext();
e.MoveNext();
 
foreach (var f in list) {
Console.WriteLine(f());
}
 
This prints 4,4,4,4 – that is a single storage is reused for all closures, expected result is 1, 1, 1, 1.
 
The C# equivalent would be:
    static IEnumerable<Func<int>> Test(int n) {
        for (int i = 0; i < n; i++) {
            int j = i;
            yield return () => j;
        }
    }

comments