1 // Copyright 2013 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
20 "golang.org/x/tools/go/loader"
21 "golang.org/x/tools/go/ssa"
22 "golang.org/x/tools/go/ssa/ssautil"
25 func isEmpty(f *ssa.Function) bool { return f.Blocks == nil }
27 // Tests that programs partially loaded from gc object files contain
28 // functions with no code for the external portions, but are otherwise ok.
29 func TestBuildPackage(t *testing.T) {
41 t.Parallel() // static call to external declared method
42 t.Fail() // static call to promoted external declared method
43 testing.Short() // static call to external package-level function
45 var w io.Writer = new(bytes.Buffer)
46 w.Write(nil) // interface invoke of external declared method
51 fset := token.NewFileSet()
52 f, err := parser.ParseFile(fset, "input.go", input, 0)
58 // Build an SSA program from the parsed file.
59 // Load its dependencies from gc binary export data.
60 mainPkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset,
61 types.NewPackage("main", ""), []*ast.File{f}, ssa.SanityCheckFunctions)
67 // The main package, its direct and indirect dependencies are loaded.
69 // directly imported dependencies:
70 "bytes", "io", "testing",
71 // indirect dependencies mentioned by
72 // the direct imports' export data
73 "sync", "unicode", "time",
77 all := prog.AllPackages()
78 if len(all) <= len(deps) {
79 t.Errorf("unexpected set of loaded packages: %q", all)
81 for _, path := range deps {
82 pkg := prog.ImportedPackage(path)
84 t.Errorf("package not loaded: %q", path)
88 // External packages should have no function bodies (except for wrappers).
89 isExt := pkg != mainPkg
92 if isExt && !isEmpty(pkg.Func("init")) {
93 t.Errorf("external package %s has non-empty init", pkg)
94 } else if !isExt && isEmpty(pkg.Func("init")) {
95 t.Errorf("main package %s has empty init", pkg)
98 for _, mem := range pkg.Members {
99 switch mem := mem.(type) {
101 // Functions at package level.
102 if isExt && !isEmpty(mem) {
103 t.Errorf("external function %s is non-empty", mem)
104 } else if !isExt && isEmpty(mem) {
105 t.Errorf("function %s is empty", mem)
109 // Methods of named types T.
110 // (In this test, all exported methods belong to *T not T.)
112 t.Fatalf("unexpected name type in main package: %s", mem)
114 mset := prog.MethodSets.MethodSet(types.NewPointer(mem.Type()))
115 for i, n := 0, mset.Len(); i < n; i++ {
116 m := prog.MethodValue(mset.At(i))
117 // For external types, only synthetic wrappers have code.
118 expExt := !strings.Contains(m.Synthetic, "wrapper")
119 if expExt && !isEmpty(m) {
120 t.Errorf("external method %s is non-empty: %s",
122 } else if !expExt && isEmpty(m) {
123 t.Errorf("method function %s is empty: %s",
131 expectedCallee := []string{
132 "(*testing.T).Parallel",
133 "(*testing.common).Fail",
138 for _, b := range mainPkg.Func("main").Blocks {
139 for _, instr := range b.Instrs {
140 switch instr := instr.(type) {
141 case ssa.CallInstruction:
142 call := instr.Common()
143 if want := expectedCallee[callNum]; want != "N/A" {
144 got := call.StaticCallee().String()
146 t.Errorf("call #%d from main.main: got callee %s, want %s",
155 t.Errorf("in main.main: got %d calls, want %d", callNum, 4)
159 // TestRuntimeTypes tests that (*Program).RuntimeTypes() includes all necessary types.
160 func TestRuntimeTypes(t *testing.T) {
165 // An exported package-level type is needed.
166 {`package A; type T struct{}; func (T) f() {}`,
167 []string{"*p.T", "p.T"},
169 // An unexported package-level type is not needed.
170 {`package B; type t struct{}; func (t) f() {}`,
173 // Subcomponents of type of exported package-level var are needed.
174 {`package C; import "bytes"; var V struct {*bytes.Buffer}`,
175 []string{"*bytes.Buffer", "*struct{*bytes.Buffer}", "struct{*bytes.Buffer}"},
177 // Subcomponents of type of unexported package-level var are not needed.
178 {`package D; import "bytes"; var v struct {*bytes.Buffer}`,
181 // Subcomponents of type of exported package-level function are needed.
182 {`package E; import "bytes"; func F(struct {*bytes.Buffer}) {}`,
183 []string{"*bytes.Buffer", "struct{*bytes.Buffer}"},
185 // Subcomponents of type of unexported package-level function are not needed.
186 {`package F; import "bytes"; func f(struct {*bytes.Buffer}) {}`,
189 // Subcomponents of type of exported method of uninstantiated unexported type are not needed.
190 {`package G; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v x`,
193 // ...unless used by MakeInterface.
194 {`package G2; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v interface{} = x{}`,
195 []string{"*bytes.Buffer", "*p.x", "p.x", "struct{*bytes.Buffer}"},
197 // Subcomponents of type of unexported method are not needed.
198 {`package I; import "bytes"; type X struct{}; func (X) G(struct {*bytes.Buffer}) {}`,
199 []string{"*bytes.Buffer", "*p.X", "p.X", "struct{*bytes.Buffer}"},
201 // Local types aren't needed.
202 {`package J; import "bytes"; func f() { type T struct {*bytes.Buffer}; var t T; _ = t }`,
205 // ...unless used by MakeInterface.
206 {`package K; import "bytes"; func f() { type T struct {*bytes.Buffer}; _ = interface{}(T{}) }`,
207 []string{"*bytes.Buffer", "*p.T", "p.T"},
209 // Types used as operand of MakeInterface are needed.
210 {`package L; import "bytes"; func f() { _ = interface{}(struct{*bytes.Buffer}{}) }`,
211 []string{"*bytes.Buffer", "struct{*bytes.Buffer}"},
213 // MakeInterface is optimized away when storing to a blank.
214 {`package M; import "bytes"; var _ interface{} = struct{*bytes.Buffer}{}`,
218 for _, test := range tests {
220 fset := token.NewFileSet()
221 f, err := parser.ParseFile(fset, "input.go", test.input, 0)
223 t.Errorf("test %q: %s", test.input[:15], err)
227 // Create a single-file main package.
228 // Load dependencies from gc binary export data.
229 ssapkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset,
230 types.NewPackage("p", ""), []*ast.File{f}, ssa.SanityCheckFunctions)
232 t.Errorf("test %q: %s", test.input[:15], err)
237 for _, T := range ssapkg.Prog.RuntimeTypes() {
238 typstrs = append(typstrs, T.String())
240 sort.Strings(typstrs)
242 if !reflect.DeepEqual(typstrs, test.want) {
243 t.Errorf("test 'package %s': got %q, want %q",
244 f.Name.Name, typstrs, test.want)
249 // TestInit tests that synthesized init functions are correctly formed.
250 // Bare init functions omit calls to dependent init functions and the use of
251 // an init guard. They are useful in cases where the client uses a different
252 // calling convention for init functions, or cases where it is easier for a
253 // client to analyze bare init functions. Both of these aspects are used by
254 // the llgo compiler for simpler integration with gccgo's runtime library,
255 // and to simplify the analysis whereby it deduces which stores to globals
256 // can be lowered to global initializers.
257 func TestInit(t *testing.T) {
262 {0, `package A; import _ "errors"; var i int = 42`,
265 # Synthetic: package initializer
268 t0 = *init$guard bool
270 1: init.start P:1 S:1
271 *init$guard = true:bool
272 t1 = errors.init() ()
279 {ssa.BareInits, `package B; import _ "errors"; var i int = 42`,
282 # Synthetic: package initializer
290 for _, test := range tests {
291 // Create a single-file main package.
292 var conf loader.Config
293 f, err := conf.ParseFile("<input>", test.input)
295 t.Errorf("test %q: %s", test.input[:15], err)
298 conf.CreateFromFiles(f.Name.Name, f)
300 lprog, err := conf.Load()
302 t.Errorf("test 'package %s': Load: %s", f.Name.Name, err)
305 prog := ssautil.CreateProgram(lprog, test.mode)
306 mainPkg := prog.Package(lprog.Created[0].Pkg)
308 initFunc := mainPkg.Func("init")
310 t.Errorf("test 'package %s': no init function", f.Name.Name)
314 var initbuf bytes.Buffer
315 _, err = initFunc.WriteTo(&initbuf)
317 t.Errorf("test 'package %s': WriteTo: %s", f.Name.Name, err)
321 if initbuf.String() != test.want {
322 t.Errorf("test 'package %s': got %s, want %s", f.Name.Name, initbuf.String(), test.want)
327 // TestSyntheticFuncs checks that the expected synthetic functions are
328 // created, reachable, and not duplicated.
329 func TestSyntheticFuncs(t *testing.T) {
330 const input = `package P
352 m interface{} = struct{T}{}
353 n interface{} = struct{T}{}
354 o interface{} = struct{*T}{}
355 p interface{} = struct{*T}{}
356 q interface{} = new(struct{T})
357 r interface{} = new(struct{T})
358 s interface{} = new(struct{*T})
359 t interface{} = new(struct{*T})
363 var conf loader.Config
364 f, err := conf.ParseFile("<input>", input)
366 t.Fatalf("parse: %v", err)
368 conf.CreateFromFiles(f.Name.Name, f)
371 lprog, err := conf.Load()
373 t.Fatalf("Load: %v", err)
376 // Create and build SSA
377 prog := ssautil.CreateProgram(lprog, 0)
380 // Enumerate reachable synthetic functions
381 want := map[string]string{
382 "(*P.T).g$bound": "bound method wrapper for func (*P.T).g() int",
383 "(P.T).f$bound": "bound method wrapper for func (P.T).f() int",
385 "(*P.T).g$thunk": "thunk for func (*P.T).g() int",
386 "(P.T).f$thunk": "thunk for func (P.T).f() int",
387 "(struct{*P.T}).g$thunk": "thunk for func (*P.T).g() int",
388 "(struct{P.T}).f$thunk": "thunk for func (P.T).f() int",
390 "(*P.T).f": "wrapper for func (P.T).f() int",
391 "(*struct{*P.T}).f": "wrapper for func (P.T).f() int",
392 "(*struct{*P.T}).g": "wrapper for func (*P.T).g() int",
393 "(*struct{P.T}).f": "wrapper for func (P.T).f() int",
394 "(*struct{P.T}).g": "wrapper for func (*P.T).g() int",
395 "(struct{*P.T}).f": "wrapper for func (P.T).f() int",
396 "(struct{*P.T}).g": "wrapper for func (*P.T).g() int",
397 "(struct{P.T}).f": "wrapper for func (P.T).f() int",
399 "P.init": "package initializer",
401 for fn := range ssautil.AllFunctions(prog) {
402 if fn.Synthetic == "" {
406 wantDescr, ok := want[name]
408 t.Errorf("got unexpected/duplicate func: %q: %q", name, fn.Synthetic)
413 if wantDescr != fn.Synthetic {
414 t.Errorf("(%s).Synthetic = %q, want %q", name, fn.Synthetic, wantDescr)
417 for fn, descr := range want {
418 t.Errorf("want func: %q: %q", fn, descr)
422 // TestPhiElimination ensures that dead phis, including those that
423 // participate in a cycle, are properly eliminated.
424 func TestPhiElimination(t *testing.T) {
430 func g(slice []int) {
433 // e should not be lifted to a dead φ-node.
442 // The SSA code for this function should look something like this:
449 // t1 = phi [1: -1:int, 3: t2]
452 // if t3 goto 3 else 1
458 // But earlier versions of the SSA construction algorithm would
459 // additionally generate this cycle of dead phis:
462 // t7 = phi [0: nil:error, 2: t8] #e
465 // t8 = phi [1: t7, 3: t4] #e
469 var conf loader.Config
470 f, err := conf.ParseFile("<input>", input)
472 t.Fatalf("parse: %v", err)
474 conf.CreateFromFiles("p", f)
477 lprog, err := conf.Load()
479 t.Fatalf("Load: %v", err)
482 // Create and build SSA
483 prog := ssautil.CreateProgram(lprog, 0)
484 p := prog.Package(lprog.Package("p").Pkg)
489 for _, b := range g.Blocks {
490 for _, instr := range b.Instrs {
491 if _, ok := instr.(*ssa.Phi); ok {
498 t.Errorf("expected a single Phi (for the range index), got %d", phis)