Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
theclapp opened this issue May 15, 2024 · 1 comment
Assignees
Labels
bug Something isn't working

Comments

@theclapp
Copy link
Contributor

theclapp commented May 15, 2024

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
Copy link
Contributor Author

Sometimes the problem is in assigning to pkg.J, sometimes it's with passing pkg.J to a function. It's like Yaegi decides (in the cfg code, maybe?) that pkg.J is a constant, evaluates it once at compile-time, and doesn't later re-evaluate it when it should (at run-time). The use of *&pkg.J gets around that.

@ldez ldez added the bug Something isn't working label May 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants