Skip to content

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

@theclapp

Description

@theclapp

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.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions