yaegi icon indicating copy to clipboard operation
yaegi copied to clipboard

Still problems assigning to variables in other packages from `main`, whether in "binary" or fully-interpreted code

Open theclapp opened this issue 1 month ago • 1 comments

The following test case in interp/interp_eval_test.go triggers an unexpected result

func TestIssue1632(t *testing.T) {
	var j int

	i := interp.New(interp.Options{})
	if err := i.Use(interp.Exports{
		"pkg/pkg": map[string]reflect.Value{
			"J": reflect.ValueOf(&j).Elem(),
		},
	}); err != nil {
		t.Fatal(err)
	}
	i.ImportUsed()

	_, err := i.Eval(`func f(i int) int { return i }`)
	if err != nil {
		t.Fatal(err)
	}
	testJ := func(initJ int, src, expected string) {
		t.Helper()
		t.Run(src, func(t *testing.T) {
			t.Helper()
			j = initJ
			assertEval(t, i, src, "", expected)
		})
	}

	// These all work.
	testJ(0, "pkg.J = 1; pkg.J", "1")
	testJ(0, "pkg.J = pkg.J + 1; pkg.J", "1")
	testJ(0, "f(1)", "1")
	testJ(1, "f(pkg.J)", "1")
	testJ(0, "pkg.J += 1; pkg.J", "1")
	testJ(0, "pkg.J += 1; f(*&pkg.J)", "1")
	testJ(0, "pkg.J++; f(*&pkg.J)", "1")
	testJ(0, "*&pkg.J = f(1); pkg.J", "1")
	testJ(0, "k := 1; pkg.J = k; pkg.J", "1")
	testJ(0, "k := 1; *&pkg.J = f(k); pkg.J", "1")
	testJ(0, "pkg.J += 1; f(*&pkg.J+1)", "2")

	// These all fail
	testJ(0, "pkg.J += 1; f(pkg.J)", "1")            // get 0
	testJ(0, "pkg.J += 1; f(pkg.J+1)", "2")          // get 1
	testJ(0, "pkg.J++; f(pkg.J)", "1")               // get 0
	testJ(0, "pkg.J = pkg.J + 1; f(pkg.J)", "1")     // get 0
	testJ(1, "pkg.J = pkg.J + pkg.J; f(pkg.J)", "2") // get 1
	testJ(2, "pkg.J = f(1); pkg.J", "1")             // get 2 (this one's especially surprising)
	testJ(0, "k := 1; pkg.J = f(k); pkg.J", "1")     // get 0
	testJ(0, "pkg.J = 1; k := pkg.J; k", "1")        // get 0

	// Try these tests with strictly interpreted code
	i = interp.New(interp.Options{})
	_, err = i.Eval(`package pkg; var J int`)
	if err != nil {
		t.Fatal(err)
	}
	_, err = i.Eval(`package main
import "pkg"
func f(i int) int { return i }`)
	if err != nil {
		t.Fatal(err)
	}

	testJ = func(initJ int, src, expected string) {
		t.Helper()
		t.Run(src, func(t *testing.T) {
			t.Helper()

			res, err := i.Eval(fmt.Sprintf("pkg.J = %d; pkg.J", initJ))
			if err != nil {
				t.Fatal(err)
			}
			if res.Interface().(int) != initJ {
				t.Fatalf("Expected pkg.J to be %d, got %v", initJ, res)
			}
			assertEval(t, i, src, "", expected)
		})
	}

	// These all still succeed
	testJ(0, "pkg.J = 1; pkg.J", "1")
	testJ(0, "f(1)", "1")
	testJ(1, "f(pkg.J)", "1")
	testJ(0, "pkg.J += 1; pkg.J", "1")
	testJ(0, "pkg.J += 1; f(*&pkg.J)", "1")
	testJ(0, "pkg.J++; f(*&pkg.J)", "1")
	testJ(0, "*&pkg.J = f(1); pkg.J", "1")
	testJ(0, "k := 1; pkg.J = k; pkg.J", "1")
	testJ(0, "k := 1; *&pkg.J = f(k); pkg.J", "1")
	testJ(0, "pkg.J += 1; f(*&pkg.J+1)", "2")

	// These all succeed but don't above
	testJ(0, "pkg.J += 1; f(pkg.J)", "1")
	testJ(0, "pkg.J += 1; f(pkg.J+1)", "2")
	testJ(0, "pkg.J++; f(pkg.J)", "1")
	testJ(0, "pkg.J = 1; k := pkg.J; k", "1")

	// This fails but didn't used to
	testJ(0, "pkg.J = pkg.J + 1; pkg.J", "1") // get 0

	// These all still fail
	testJ(0, "pkg.J = pkg.J + 1; f(pkg.J)", "1")     // get 0
	testJ(1, "pkg.J = pkg.J + pkg.J; f(pkg.J)", "2") // get 1
	testJ(2, "pkg.J = f(1); pkg.J", "1")             // get 2
	testJ(0, "k := 1; pkg.J = f(k); pkg.J", "1")     // get 0
}

Expected result

All tests pass

Got

--- FAIL: TestIssue1632 (0.00s)
    --- FAIL: TestIssue1632/pkg.J_+=_1;_f(pkg.J) (0.00s)
        interp_eval_test.go:1974: got 0, want 1
    --- FAIL: TestIssue1632/pkg.J_+=_1;_f(pkg.J+1) (0.00s)
        interp_eval_test.go:1975: got 1, want 2
    --- FAIL: TestIssue1632/pkg.J++;_f(pkg.J) (0.00s)
        interp_eval_test.go:1976: got 0, want 1
    --- FAIL: TestIssue1632/pkg.J_=_pkg.J_+_1;_f(pkg.J) (0.00s)
        interp_eval_test.go:1977: got 0, want 1
    --- FAIL: TestIssue1632/pkg.J_=_pkg.J_+_pkg.J;_f(pkg.J) (0.00s)
        interp_eval_test.go:1978: got 1, want 2
    --- FAIL: TestIssue1632/pkg.J_=_f(1);_pkg.J (0.00s)
        interp_eval_test.go:1979: got 2, want 1
    --- FAIL: TestIssue1632/k_:=_1;_pkg.J_=_f(k);_pkg.J (0.00s)
        interp_eval_test.go:1980: got 0, want 1
    --- FAIL: TestIssue1632/pkg.J_=_1;_k_:=_pkg.J;_k (0.00s)
        interp_eval_test.go:1981: got 0, want 1
    --- FAIL: TestIssue1632/pkg.J_=_pkg.J_+_1;_pkg.J#01 (0.00s)
        interp_eval_test.go:2031: got 0, want 1
    --- FAIL: TestIssue1632/pkg.J_=_pkg.J_+_1;_f(pkg.J)#01 (0.00s)
        interp_eval_test.go:2034: got 0, want 1
    --- FAIL: TestIssue1632/pkg.J_=_pkg.J_+_pkg.J;_f(pkg.J)#01 (0.00s)
        interp_eval_test.go:2035: got 1, want 2
    --- FAIL: TestIssue1632/pkg.J_=_f(1);_pkg.J#01 (0.00s)
        interp_eval_test.go:2036: got 2, want 1
    --- FAIL: TestIssue1632/k_:=_1;_pkg.J_=_f(k);_pkg.J#01 (0.00s)
        interp_eval_test.go:2037: got 0, want 1
FAIL
FAIL    github.com/traefik/yaegi/interp 0.357s
FAIL

Yaegi Version

381e045

Additional Notes

Related: #1623 .

Obviously I changed assertEval to use Errorf instead of Fatalf.

theclapp avatar May 15 '24 20:05 theclapp