diff --git a/compileopts/options.go b/compileopts/options.go index bd99179c..debdaf08 100644 --- a/compileopts/options.go +++ b/compileopts/options.go @@ -23,6 +23,7 @@ type Options struct { GOOS string // environment variable GOARCH string // environment variable GOARM string // environment variable (only used with GOARCH=arm) + Directory string // working dir, leave it unset to use the current working dir Target string Opt string GC string @@ -48,7 +49,6 @@ type Options struct { Programmer string OpenOCDCommands []string LLVMFeatures string - Directory string PrintJSON bool Monitor bool BaudRate int diff --git a/loader/loader.go b/loader/loader.go index 60d0f7eb..e66812a8 100644 --- a/loader/loader.go +++ b/loader/loader.go @@ -27,6 +27,8 @@ import ( "github.com/tinygo-org/tinygo/goenv" ) +var initFileVersions = func(info *types.Info) {} + // Program holds all packages and some metadata about the program as a whole. type Program struct { config *compileopts.Config @@ -383,7 +385,19 @@ func (p *Package) Check() error { // errors out on language features not supported in that particular // version. checker.GoVersion = "go" + p.Module.GoVersion + } else { + // Version is not known, so use the currently installed Go version. + // This is needed for `tinygo run` for example. + // Normally we'd use goenv.GorootVersionString(), but for compatibility + // with Go 1.20 and below we need a version in the form of "go1.12" (no + // patch version). + major, minor, err := goenv.GetGorootVersion() + if err != nil { + return err + } + checker.GoVersion = fmt.Sprintf("go%d.%d", major, minor) } + initFileVersions(&p.info) // Do typechecking of the package. packageName := p.ImportPath diff --git a/loader/loader_go122.go b/loader/loader_go122.go new file mode 100644 index 00000000..24d06064 --- /dev/null +++ b/loader/loader_go122.go @@ -0,0 +1,17 @@ +//go:build go1.22 + +// types.Info.FileVersions was added in Go 1.22, so we can only initialize it +// when built with Go 1.22. + +package loader + +import ( + "go/ast" + "go/types" +) + +func init() { + initFileVersions = func(info *types.Info) { + info.FileVersions = make(map[*ast.File]string) + } +} diff --git a/main_test.go b/main_test.go index 79115ffa..b3e86420 100644 --- a/main_test.go +++ b/main_test.go @@ -69,6 +69,7 @@ func TestBuild(t *testing.T) { "json.go", "map.go", "math.go", + "oldgo/", "print.go", "reflect.go", "slice.go", @@ -91,7 +92,7 @@ func TestBuild(t *testing.T) { tests = append(tests, "go1.21.go") } if minor >= 22 { - tests = append(tests, "go1.22.go") + tests = append(tests, "go1.22/") } if *testTarget != "" { @@ -336,8 +337,11 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c path := TESTDATA + "/" + name // Get the expected output for this test. txtpath := path[:len(path)-3] + ".txt" + pkgName := "./" + path if path[len(path)-1] == '/' { txtpath = path + "out.txt" + options.Directory = path + pkgName = "." } expected, err := os.ReadFile(txtpath) if err != nil { @@ -351,7 +355,7 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c // Build the test binary. stdout := &bytes.Buffer{} - _, err = buildAndRun("./"+path, config, stdout, cmdArgs, environmentVars, time.Minute, func(cmd *exec.Cmd, result builder.BuildResult) error { + _, err = buildAndRun(pkgName, config, stdout, cmdArgs, environmentVars, time.Minute, func(cmd *exec.Cmd, result builder.BuildResult) error { return cmd.Run() }) if err != nil { diff --git a/testdata/go1.22/go.mod b/testdata/go1.22/go.mod new file mode 100644 index 00000000..fd97e8b2 --- /dev/null +++ b/testdata/go1.22/go.mod @@ -0,0 +1,3 @@ +module github.com/tinygo-org/tinygo/testdata/go1.22 + +go 1.22 diff --git a/testdata/go1.22.go b/testdata/go1.22/main.go similarity index 57% rename from testdata/go1.22.go rename to testdata/go1.22/main.go index 2b02a18e..80f6c887 100644 --- a/testdata/go1.22.go +++ b/testdata/go1.22/main.go @@ -19,15 +19,13 @@ func testLoopVar() { f = func() int { return i } } } - // Prints 1 in Go 1.21, or 0 in Go 1.22. - // TODO: this still prints Go 1.21 even in Go 1.22. We probably need to - // specify the Go version somewhere. + // Variable n is 1 in Go 1.21, or 0 in Go 1.22. n := f() if n == 0 { - println("behaves like Go 1.22") + println("loops behave like Go 1.22") } else if n == 1 { - println("behaves like Go 1.21") + println("loops behave like Go 1.21") } else { - println("unknown behavior") + println("unknown loop behavior") } } diff --git a/testdata/go1.22.txt b/testdata/go1.22/out.txt similarity index 61% rename from testdata/go1.22.txt rename to testdata/go1.22/out.txt index 2695c784..ac9836af 100644 --- a/testdata/go1.22.txt +++ b/testdata/go1.22/out.txt @@ -9,4 +9,4 @@ 2 1 go1.22 has lift-off! -behaves like Go 1.21 +loops behave like Go 1.22 diff --git a/testdata/oldgo/go.mod b/testdata/oldgo/go.mod new file mode 100644 index 00000000..f3c7277a --- /dev/null +++ b/testdata/oldgo/go.mod @@ -0,0 +1,5 @@ +module github.com/tinygo-org/tinygo/testdata/oldgo + +// Go version doesn't matter much, as long as it's old. + +go 1.15 diff --git a/testdata/oldgo/main.go b/testdata/oldgo/main.go new file mode 100644 index 00000000..4bc03044 --- /dev/null +++ b/testdata/oldgo/main.go @@ -0,0 +1,26 @@ +package main + +// This package verifies that the Go language version is correctly picked up +// from the go.mod file. + +func main() { + testLoopVar() +} + +func testLoopVar() { + var f func() int + for i := 0; i < 1; i++ { + if i == 0 { + f = func() int { return i } + } + } + // Variable n is 1 in Go 1.21, or 0 in Go 1.22. + n := f() + if n == 0 { + println("loops behave like Go 1.22") + } else if n == 1 { + println("loops behave like Go 1.21") + } else { + println("unknown loop behavior") + } +} diff --git a/testdata/oldgo/out.txt b/testdata/oldgo/out.txt new file mode 100644 index 00000000..57ef14f1 --- /dev/null +++ b/testdata/oldgo/out.txt @@ -0,0 +1 @@ +loops behave like Go 1.21