Open theclapp opened 1 month ago
Pulling the closure that captures the loop variables out of the arglist of the "binary" function R
works, if that helps:
Change this:
for i, e := range cp.Es {
i, e := i, e
ch = append(ch,
// breaks
R(i, e.E, func() string {
TLogf("inside R's f: %d, %v", i, e.E)
return e.E
}),
)
}
To this:
for i, e := range cp.Es {
i, e := i, e
// works
f := func() string {
TLogf("inside R's f: %d, %v", i, e.E)
return e.E
}
ch = append(ch, R(i, e.E, f))
}
Also, I think that the fact that these are loop variables is a red herring, I think this is probably a problem with any closure being passed as a literal to a "binary" function.
Unfortunately, the code I'm playing with is awash with such things. :)
This issue is very general -- we encountered it as well, and were also able to work around it by assigning a variable to the funLit function literal.
This suggests that the general solution is to add an exec to a funcLit that makes a new reflect.Value copy of the func lit in the frame. Ideally it would detect the outer context of being passed as an arg to another function (doable), in a for loop (probably pretty hard in general).
I just experimented with this and it doesn't seem to work:
in run.go:
func funcLitCopy(n *node) {
next := getExec(n.tnext)
n.exec = func(f *frame) bltn {
ov := f.data[n.findex]
nv := reflect.New(ov.Type()).Elem()
nv.Set(ov)
f.data[n.findex] = nv
return next
}
}
in cfg.go, post-processing:
case funcLit:
n.types, n.scope = sc.types, sc
sc = sc.pop()
err = genRun(n)
n.gen = funcLitCopy
unfortunately, in this test:
func TestForRangeClosure(t *testing.T) {
i := New(Options{})
_, err := i.Eval(`
func main() {
fs := []func()
for i := range 3 {
println(i, &i)
fs = append(fs, func() { println(i, &i) })
}
for _, f := range fs {
f()
}
}
`)
if err != nil {
t.Error(err)
}
}
it triggers a "reflect.Value.Call: call of nil function" -- presumably copying the reflect value doesn't do what one might have hoped it would?
it would probably make more sense to figure out what the process of defining a new var for the funcLit is actually accomplishing, and then just replicate that in the exec. apparently making a reflect clone is not it..
The following test case
TestIssue1634
ininterp/interp_eval_test.go
triggers an unexpected resultExpected result
Got
Yaegi Version
381e045
Additional Notes
No response