From e71c338beb115bd6674a15a7f0119d04a609323c Mon Sep 17 00:00:00 2001 From: Tim King Date: Wed, 10 Aug 2022 15:14:19 -0700 Subject: [PATCH] go/ssa/ssautil: initialize info when there is syntax Only initialize *types.Info when there are []*ast.Files available. Previously, when packages x and z, where x imports y and y imports z, are loaded with packages.LoadSyntax and built with ssautil.Packages, then package y will have *types.Info but no files. If y has any globals initialized from a func variable, the bodies of the function literals did not have type information and failed to build. Fixes golang/go#53604 Change-Id: I3cce608d6a127ac44b65947b68cf0d748fc2dfc6 Reviewed-on: https://go-review.googlesource.com/c/tools/+/422639 Reviewed-by: Zvonimir Pavlinovic Reviewed-by: Alan Donovan --- go/ssa/builder.go | 3 ++ go/ssa/ssautil/load.go | 4 ++- go/ssa/ssautil/load_test.go | 56 +++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/go/ssa/builder.go b/go/ssa/builder.go index b36775a4e3..98ed49dfea 100644 --- a/go/ssa/builder.go +++ b/go/ssa/builder.go @@ -2461,6 +2461,9 @@ func (p *Package) build() { } // Initialize package-level vars in correct order. + if len(p.info.InitOrder) > 0 && len(p.files) == 0 { + panic("no source files provided for package. cannot initialize globals") + } for _, varinit := range p.info.InitOrder { if init.Prog.mode&LogSource != 0 { fmt.Fprintf(os.Stderr, "build global initializer %v @ %s\n", diff --git a/go/ssa/ssautil/load.go b/go/ssa/ssautil/load.go index 58d185f672..96d69a20a1 100644 --- a/go/ssa/ssautil/load.go +++ b/go/ssa/ssautil/load.go @@ -77,10 +77,12 @@ func doPackages(initial []*packages.Package, mode ssa.BuilderMode, deps bool) (* packages.Visit(initial, nil, func(p *packages.Package) { if p.Types != nil && !p.IllTyped { var files []*ast.File + var info *types.Info if deps || isInitial[p] { files = p.Syntax + info = p.TypesInfo } - ssamap[p] = prog.CreatePackage(p.Types, files, p.TypesInfo, true) + ssamap[p] = prog.CreatePackage(p.Types, files, info, true) } }) diff --git a/go/ssa/ssautil/load_test.go b/go/ssa/ssautil/load_test.go index f769be273b..707a1d0b69 100644 --- a/go/ssa/ssautil/load_test.go +++ b/go/ssa/ssautil/load_test.go @@ -12,10 +12,12 @@ import ( "go/token" "go/types" "os" + "path" "strings" "testing" "golang.org/x/tools/go/packages" + "golang.org/x/tools/go/packages/packagestest" "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/ssautil" "golang.org/x/tools/internal/testenv" @@ -135,3 +137,57 @@ func TestIssue28106(t *testing.T) { prog, _ := ssautil.Packages(pkgs, ssa.BuilderMode(0)) prog.Build() // no crash } + +func TestIssue53604(t *testing.T) { + // Tests that variable initializers are not added to init() when syntax + // is not present but types.Info is available. + // + // Packages x, y, z are loaded with mode `packages.LoadSyntax`. + // Package x imports y, and y imports z. + // Packages are built using ssautil.Packages() with x and z as roots. + // This setup creates y using CreatePackage(pkg, files, info, ...) + // where len(files) == 0 but info != nil. + // + // Tests that globals from y are not initialized. + e := packagestest.Export(t, packagestest.Modules, []packagestest.Module{ + { + Name: "golang.org/fake", + Files: map[string]interface{}{ + "x/x.go": `package x; import "golang.org/fake/y"; var V = y.F()`, + "y/y.go": `package y; import "golang.org/fake/z"; var F = func () *int { return &z.Z } `, + "z/z.go": `package z; var Z int`, + }, + }, + }) + defer e.Cleanup() + + // Load x and z as entry packages using packages.LoadSyntax + e.Config.Mode = packages.LoadSyntax + pkgs, err := packages.Load(e.Config, path.Join(e.Temp(), "fake/x"), path.Join(e.Temp(), "fake/z")) + if err != nil { + t.Fatal(err) + } + for _, p := range pkgs { + if len(p.Errors) > 0 { + t.Fatalf("%v", p.Errors) + } + } + + prog, _ := ssautil.Packages(pkgs, ssa.BuilderMode(0)) + prog.Build() + + // y does not initialize F. + y := prog.ImportedPackage("golang.org/fake/y") + if y == nil { + t.Fatal("Failed to load intermediate package y") + } + yinit := y.Members["init"].(*ssa.Function) + for _, bb := range yinit.Blocks { + for _, i := range bb.Instrs { + if store, ok := i.(*ssa.Store); ok && store.Addr == y.Var("F") { + t.Errorf("y.init() stores to F %v", store) + } + } + } + +}