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. |
4 | |
5 | package ssa_test |
6 | |
7 | import ( |
8 | "bytes" |
9 | "fmt" |
10 | "go/ast" |
11 | "go/build" |
12 | "go/importer" |
13 | "go/parser" |
14 | "go/token" |
15 | "go/types" |
16 | "os" |
17 | "path/filepath" |
18 | "reflect" |
19 | "sort" |
20 | "strings" |
21 | "testing" |
22 | |
23 | "golang.org/x/tools/go/buildutil" |
24 | "golang.org/x/tools/go/loader" |
25 | "golang.org/x/tools/go/ssa" |
26 | "golang.org/x/tools/go/ssa/ssautil" |
27 | "golang.org/x/tools/internal/testenv" |
28 | "golang.org/x/tools/internal/typeparams" |
29 | ) |
30 | |
31 | func isEmpty(f *ssa.Function) bool { return f.Blocks == nil } |
32 | |
33 | // Tests that programs partially loaded from gc object files contain |
34 | // functions with no code for the external portions, but are otherwise ok. |
35 | func TestBuildPackage(t *testing.T) { |
36 | testenv.NeedsGoBuild(t) // for importer.Default() |
37 | |
38 | input := ` |
39 | package main |
40 | |
41 | import ( |
42 | "bytes" |
43 | "io" |
44 | "testing" |
45 | ) |
46 | |
47 | func main() { |
48 | var t testing.T |
49 | t.Parallel() // static call to external declared method |
50 | t.Fail() // static call to promoted external declared method |
51 | testing.Short() // static call to external package-level function |
52 | |
53 | var w io.Writer = new(bytes.Buffer) |
54 | w.Write(nil) // interface invoke of external declared method |
55 | } |
56 | ` |
57 | |
58 | // Parse the file. |
59 | fset := token.NewFileSet() |
60 | f, err := parser.ParseFile(fset, "input.go", input, 0) |
61 | if err != nil { |
62 | t.Error(err) |
63 | return |
64 | } |
65 | |
66 | // Build an SSA program from the parsed file. |
67 | // Load its dependencies from gc binary export data. |
68 | mode := ssa.SanityCheckFunctions |
69 | mainPkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset, |
70 | types.NewPackage("main", ""), []*ast.File{f}, mode) |
71 | if err != nil { |
72 | t.Error(err) |
73 | return |
74 | } |
75 | |
76 | // The main package, its direct and indirect dependencies are loaded. |
77 | deps := []string{ |
78 | // directly imported dependencies: |
79 | "bytes", "io", "testing", |
80 | // indirect dependencies mentioned by |
81 | // the direct imports' export data |
82 | "sync", "unicode", "time", |
83 | } |
84 | |
85 | prog := mainPkg.Prog |
86 | all := prog.AllPackages() |
87 | if len(all) <= len(deps) { |
88 | t.Errorf("unexpected set of loaded packages: %q", all) |
89 | } |
90 | for _, path := range deps { |
91 | pkg := prog.ImportedPackage(path) |
92 | if pkg == nil { |
93 | t.Errorf("package not loaded: %q", path) |
94 | continue |
95 | } |
96 | |
97 | // External packages should have no function bodies (except for wrappers). |
98 | isExt := pkg != mainPkg |
99 | |
100 | // init() |
101 | if isExt && !isEmpty(pkg.Func("init")) { |
102 | t.Errorf("external package %s has non-empty init", pkg) |
103 | } else if !isExt && isEmpty(pkg.Func("init")) { |
104 | t.Errorf("main package %s has empty init", pkg) |
105 | } |
106 | |
107 | for _, mem := range pkg.Members { |
108 | switch mem := mem.(type) { |
109 | case *ssa.Function: |
110 | // Functions at package level. |
111 | if isExt && !isEmpty(mem) { |
112 | t.Errorf("external function %s is non-empty", mem) |
113 | } else if !isExt && isEmpty(mem) { |
114 | t.Errorf("function %s is empty", mem) |
115 | } |
116 | |
117 | case *ssa.Type: |
118 | // Methods of named types T. |
119 | // (In this test, all exported methods belong to *T not T.) |
120 | if !isExt { |
121 | t.Fatalf("unexpected name type in main package: %s", mem) |
122 | } |
123 | mset := prog.MethodSets.MethodSet(types.NewPointer(mem.Type())) |
124 | for i, n := 0, mset.Len(); i < n; i++ { |
125 | m := prog.MethodValue(mset.At(i)) |
126 | // For external types, only synthetic wrappers have code. |
127 | expExt := !strings.Contains(m.Synthetic, "wrapper") |
128 | if expExt && !isEmpty(m) { |
129 | t.Errorf("external method %s is non-empty: %s", |
130 | m, m.Synthetic) |
131 | } else if !expExt && isEmpty(m) { |
132 | t.Errorf("method function %s is empty: %s", |
133 | m, m.Synthetic) |
134 | } |
135 | } |
136 | } |
137 | } |
138 | } |
139 | |
140 | expectedCallee := []string{ |
141 | "(*testing.T).Parallel", |
142 | "(*testing.common).Fail", |
143 | "testing.Short", |
144 | "N/A", |
145 | } |
146 | callNum := 0 |
147 | for _, b := range mainPkg.Func("main").Blocks { |
148 | for _, instr := range b.Instrs { |
149 | switch instr := instr.(type) { |
150 | case ssa.CallInstruction: |
151 | call := instr.Common() |
152 | if want := expectedCallee[callNum]; want != "N/A" { |
153 | got := call.StaticCallee().String() |
154 | if want != got { |
155 | t.Errorf("call #%d from main.main: got callee %s, want %s", |
156 | callNum, got, want) |
157 | } |
158 | } |
159 | callNum++ |
160 | } |
161 | } |
162 | } |
163 | if callNum != 4 { |
164 | t.Errorf("in main.main: got %d calls, want %d", callNum, 4) |
165 | } |
166 | } |
167 | |
168 | // TestRuntimeTypes tests that (*Program).RuntimeTypes() includes all necessary types. |
169 | func TestRuntimeTypes(t *testing.T) { |
170 | testenv.NeedsGoBuild(t) // for importer.Default() |
171 | |
172 | tests := []struct { |
173 | input string |
174 | want []string |
175 | }{ |
176 | // An exported package-level type is needed. |
177 | {`package A; type T struct{}; func (T) f() {}`, |
178 | []string{"*p.T", "p.T"}, |
179 | }, |
180 | // An unexported package-level type is not needed. |
181 | {`package B; type t struct{}; func (t) f() {}`, |
182 | nil, |
183 | }, |
184 | // Subcomponents of type of exported package-level var are needed. |
185 | {`package C; import "bytes"; var V struct {*bytes.Buffer}`, |
186 | []string{"*bytes.Buffer", "*struct{*bytes.Buffer}", "struct{*bytes.Buffer}"}, |
187 | }, |
188 | // Subcomponents of type of unexported package-level var are not needed. |
189 | {`package D; import "bytes"; var v struct {*bytes.Buffer}`, |
190 | nil, |
191 | }, |
192 | // Subcomponents of type of exported package-level function are needed. |
193 | {`package E; import "bytes"; func F(struct {*bytes.Buffer}) {}`, |
194 | []string{"*bytes.Buffer", "struct{*bytes.Buffer}"}, |
195 | }, |
196 | // Subcomponents of type of unexported package-level function are not needed. |
197 | {`package F; import "bytes"; func f(struct {*bytes.Buffer}) {}`, |
198 | nil, |
199 | }, |
200 | // Subcomponents of type of exported method of uninstantiated unexported type are not needed. |
201 | {`package G; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v x`, |
202 | nil, |
203 | }, |
204 | // ...unless used by MakeInterface. |
205 | {`package G2; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v interface{} = x{}`, |
206 | []string{"*bytes.Buffer", "*p.x", "p.x", "struct{*bytes.Buffer}"}, |
207 | }, |
208 | // Subcomponents of type of unexported method are not needed. |
209 | {`package I; import "bytes"; type X struct{}; func (X) G(struct {*bytes.Buffer}) {}`, |
210 | []string{"*bytes.Buffer", "*p.X", "p.X", "struct{*bytes.Buffer}"}, |
211 | }, |
212 | // Local types aren't needed. |
213 | {`package J; import "bytes"; func f() { type T struct {*bytes.Buffer}; var t T; _ = t }`, |
214 | nil, |
215 | }, |
216 | // ...unless used by MakeInterface. |
217 | {`package K; import "bytes"; func f() { type T struct {*bytes.Buffer}; _ = interface{}(T{}) }`, |
218 | []string{"*bytes.Buffer", "*p.T", "p.T"}, |
219 | }, |
220 | // Types used as operand of MakeInterface are needed. |
221 | {`package L; import "bytes"; func f() { _ = interface{}(struct{*bytes.Buffer}{}) }`, |
222 | []string{"*bytes.Buffer", "struct{*bytes.Buffer}"}, |
223 | }, |
224 | // MakeInterface is optimized away when storing to a blank. |
225 | {`package M; import "bytes"; var _ interface{} = struct{*bytes.Buffer}{}`, |
226 | nil, |
227 | }, |
228 | } |
229 | |
230 | if typeparams.Enabled { |
231 | tests = append(tests, []struct { |
232 | input string |
233 | want []string |
234 | }{ |
235 | // MakeInterface does not create runtime type for parameterized types. |
236 | {`package N; var g interface{}; func f[S any]() { var v []S; g = v }; `, |
237 | nil, |
238 | }, |
239 | }...) |
240 | } |
241 | for _, test := range tests { |
242 | // Parse the file. |
243 | fset := token.NewFileSet() |
244 | f, err := parser.ParseFile(fset, "input.go", test.input, 0) |
245 | if err != nil { |
246 | t.Errorf("test %q: %s", test.input[:15], err) |
247 | continue |
248 | } |
249 | |
250 | // Create a single-file main package. |
251 | // Load dependencies from gc binary export data. |
252 | mode := ssa.SanityCheckFunctions |
253 | ssapkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset, |
254 | types.NewPackage("p", ""), []*ast.File{f}, mode) |
255 | if err != nil { |
256 | t.Errorf("test %q: %s", test.input[:15], err) |
257 | continue |
258 | } |
259 | |
260 | var typstrs []string |
261 | for _, T := range ssapkg.Prog.RuntimeTypes() { |
262 | typstrs = append(typstrs, T.String()) |
263 | } |
264 | sort.Strings(typstrs) |
265 | |
266 | if !reflect.DeepEqual(typstrs, test.want) { |
267 | t.Errorf("test 'package %s': got %q, want %q", |
268 | f.Name.Name, typstrs, test.want) |
269 | } |
270 | } |
271 | } |
272 | |
273 | // TestInit tests that synthesized init functions are correctly formed. |
274 | // Bare init functions omit calls to dependent init functions and the use of |
275 | // an init guard. They are useful in cases where the client uses a different |
276 | // calling convention for init functions, or cases where it is easier for a |
277 | // client to analyze bare init functions. Both of these aspects are used by |
278 | // the llgo compiler for simpler integration with gccgo's runtime library, |
279 | // and to simplify the analysis whereby it deduces which stores to globals |
280 | // can be lowered to global initializers. |
281 | func TestInit(t *testing.T) { |
282 | tests := []struct { |
283 | mode ssa.BuilderMode |
284 | input, want string |
285 | }{ |
286 | {0, `package A; import _ "errors"; var i int = 42`, |
287 | `# Name: A.init |
288 | # Package: A |
289 | # Synthetic: package initializer |
290 | func init(): |
291 | 0: entry P:0 S:2 |
292 | t0 = *init$guard bool |
293 | if t0 goto 2 else 1 |
294 | 1: init.start P:1 S:1 |
295 | *init$guard = true:bool |
296 | t1 = errors.init() () |
297 | *i = 42:int |
298 | jump 2 |
299 | 2: init.done P:2 S:0 |
300 | return |
301 | |
302 | `}, |
303 | {ssa.BareInits, `package B; import _ "errors"; var i int = 42`, |
304 | `# Name: B.init |
305 | # Package: B |
306 | # Synthetic: package initializer |
307 | func init(): |
308 | 0: entry P:0 S:0 |
309 | *i = 42:int |
310 | return |
311 | |
312 | `}, |
313 | } |
314 | for _, test := range tests { |
315 | // Create a single-file main package. |
316 | var conf loader.Config |
317 | f, err := conf.ParseFile("<input>", test.input) |
318 | if err != nil { |
319 | t.Errorf("test %q: %s", test.input[:15], err) |
320 | continue |
321 | } |
322 | conf.CreateFromFiles(f.Name.Name, f) |
323 | |
324 | lprog, err := conf.Load() |
325 | if err != nil { |
326 | t.Errorf("test 'package %s': Load: %s", f.Name.Name, err) |
327 | continue |
328 | } |
329 | prog := ssautil.CreateProgram(lprog, test.mode) |
330 | mainPkg := prog.Package(lprog.Created[0].Pkg) |
331 | prog.Build() |
332 | initFunc := mainPkg.Func("init") |
333 | if initFunc == nil { |
334 | t.Errorf("test 'package %s': no init function", f.Name.Name) |
335 | continue |
336 | } |
337 | |
338 | var initbuf bytes.Buffer |
339 | _, err = initFunc.WriteTo(&initbuf) |
340 | if err != nil { |
341 | t.Errorf("test 'package %s': WriteTo: %s", f.Name.Name, err) |
342 | continue |
343 | } |
344 | |
345 | if initbuf.String() != test.want { |
346 | t.Errorf("test 'package %s': got %s, want %s", f.Name.Name, initbuf.String(), test.want) |
347 | } |
348 | } |
349 | } |
350 | |
351 | // TestSyntheticFuncs checks that the expected synthetic functions are |
352 | // created, reachable, and not duplicated. |
353 | func TestSyntheticFuncs(t *testing.T) { |
354 | const input = `package P |
355 | type T int |
356 | func (T) f() int |
357 | func (*T) g() int |
358 | var ( |
359 | // thunks |
360 | a = T.f |
361 | b = T.f |
362 | c = (struct{T}).f |
363 | d = (struct{T}).f |
364 | e = (*T).g |
365 | f = (*T).g |
366 | g = (struct{*T}).g |
367 | h = (struct{*T}).g |
368 | |
369 | // bounds |
370 | i = T(0).f |
371 | j = T(0).f |
372 | k = new(T).g |
373 | l = new(T).g |
374 | |
375 | // wrappers |
376 | m interface{} = struct{T}{} |
377 | n interface{} = struct{T}{} |
378 | o interface{} = struct{*T}{} |
379 | p interface{} = struct{*T}{} |
380 | q interface{} = new(struct{T}) |
381 | r interface{} = new(struct{T}) |
382 | s interface{} = new(struct{*T}) |
383 | t interface{} = new(struct{*T}) |
384 | ) |
385 | ` |
386 | // Parse |
387 | var conf loader.Config |
388 | f, err := conf.ParseFile("<input>", input) |
389 | if err != nil { |
390 | t.Fatalf("parse: %v", err) |
391 | } |
392 | conf.CreateFromFiles(f.Name.Name, f) |
393 | |
394 | // Load |
395 | lprog, err := conf.Load() |
396 | if err != nil { |
397 | t.Fatalf("Load: %v", err) |
398 | } |
399 | |
400 | // Create and build SSA |
401 | prog := ssautil.CreateProgram(lprog, ssa.BuilderMode(0)) |
402 | prog.Build() |
403 | |
404 | // Enumerate reachable synthetic functions |
405 | want := map[string]string{ |
406 | "(*P.T).g$bound": "bound method wrapper for func (*P.T).g() int", |
407 | "(P.T).f$bound": "bound method wrapper for func (P.T).f() int", |
408 | |
409 | "(*P.T).g$thunk": "thunk for func (*P.T).g() int", |
410 | "(P.T).f$thunk": "thunk for func (P.T).f() int", |
411 | "(struct{*P.T}).g$thunk": "thunk for func (*P.T).g() int", |
412 | "(struct{P.T}).f$thunk": "thunk for func (P.T).f() int", |
413 | |
414 | "(*P.T).f": "wrapper for func (P.T).f() int", |
415 | "(*struct{*P.T}).f": "wrapper for func (P.T).f() int", |
416 | "(*struct{*P.T}).g": "wrapper for func (*P.T).g() int", |
417 | "(*struct{P.T}).f": "wrapper for func (P.T).f() int", |
418 | "(*struct{P.T}).g": "wrapper for func (*P.T).g() int", |
419 | "(struct{*P.T}).f": "wrapper for func (P.T).f() int", |
420 | "(struct{*P.T}).g": "wrapper for func (*P.T).g() int", |
421 | "(struct{P.T}).f": "wrapper for func (P.T).f() int", |
422 | |
423 | "P.init": "package initializer", |
424 | } |
425 | for fn := range ssautil.AllFunctions(prog) { |
426 | if fn.Synthetic == "" { |
427 | continue |
428 | } |
429 | name := fn.String() |
430 | wantDescr, ok := want[name] |
431 | if !ok { |
432 | t.Errorf("got unexpected/duplicate func: %q: %q", name, fn.Synthetic) |
433 | continue |
434 | } |
435 | delete(want, name) |
436 | |
437 | if wantDescr != fn.Synthetic { |
438 | t.Errorf("(%s).Synthetic = %q, want %q", name, fn.Synthetic, wantDescr) |
439 | } |
440 | } |
441 | for fn, descr := range want { |
442 | t.Errorf("want func: %q: %q", fn, descr) |
443 | } |
444 | } |
445 | |
446 | // TestPhiElimination ensures that dead phis, including those that |
447 | // participate in a cycle, are properly eliminated. |
448 | func TestPhiElimination(t *testing.T) { |
449 | const input = ` |
450 | package p |
451 | |
452 | func f() error |
453 | |
454 | func g(slice []int) { |
455 | for { |
456 | for range slice { |
457 | // e should not be lifted to a dead φ-node. |
458 | e := f() |
459 | h(e) |
460 | } |
461 | } |
462 | } |
463 | |
464 | func h(error) |
465 | ` |
466 | // The SSA code for this function should look something like this: |
467 | // 0: |
468 | // jump 1 |
469 | // 1: |
470 | // t0 = len(slice) |
471 | // jump 2 |
472 | // 2: |
473 | // t1 = phi [1: -1:int, 3: t2] |
474 | // t2 = t1 + 1:int |
475 | // t3 = t2 < t0 |
476 | // if t3 goto 3 else 1 |
477 | // 3: |
478 | // t4 = f() |
479 | // t5 = h(t4) |
480 | // jump 2 |
481 | // |
482 | // But earlier versions of the SSA construction algorithm would |
483 | // additionally generate this cycle of dead phis: |
484 | // |
485 | // 1: |
486 | // t7 = phi [0: nil:error, 2: t8] #e |
487 | // ... |
488 | // 2: |
489 | // t8 = phi [1: t7, 3: t4] #e |
490 | // ... |
491 | |
492 | // Parse |
493 | var conf loader.Config |
494 | f, err := conf.ParseFile("<input>", input) |
495 | if err != nil { |
496 | t.Fatalf("parse: %v", err) |
497 | } |
498 | conf.CreateFromFiles("p", f) |
499 | |
500 | // Load |
501 | lprog, err := conf.Load() |
502 | if err != nil { |
503 | t.Fatalf("Load: %v", err) |
504 | } |
505 | |
506 | // Create and build SSA |
507 | prog := ssautil.CreateProgram(lprog, ssa.BuilderMode(0)) |
508 | p := prog.Package(lprog.Package("p").Pkg) |
509 | p.Build() |
510 | g := p.Func("g") |
511 | |
512 | phis := 0 |
513 | for _, b := range g.Blocks { |
514 | for _, instr := range b.Instrs { |
515 | if _, ok := instr.(*ssa.Phi); ok { |
516 | phis++ |
517 | } |
518 | } |
519 | } |
520 | if phis != 1 { |
521 | g.WriteTo(os.Stderr) |
522 | t.Errorf("expected a single Phi (for the range index), got %d", phis) |
523 | } |
524 | } |
525 | |
526 | // TestGenericDecls ensures that *unused* generic types, methods and functions |
527 | // signatures can be built. |
528 | // |
529 | // TODO(taking): Add calls from non-generic functions to instantiations of generic functions. |
530 | // TODO(taking): Add globals with types that are instantiations of generic functions. |
531 | func TestGenericDecls(t *testing.T) { |
532 | if !typeparams.Enabled { |
533 | t.Skip("TestGenericDecls only works with type parameters enabled.") |
534 | } |
535 | const input = ` |
536 | package p |
537 | |
538 | import "unsafe" |
539 | |
540 | type Pointer[T any] struct { |
541 | v unsafe.Pointer |
542 | } |
543 | |
544 | func (x *Pointer[T]) Load() *T { |
545 | return (*T)(LoadPointer(&x.v)) |
546 | } |
547 | |
548 | func Load[T any](x *Pointer[T]) *T { |
549 | return x.Load() |
550 | } |
551 | |
552 | func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer) |
553 | ` |
554 | // The SSA members for this package should look something like this: |
555 | // func LoadPointer func(addr *unsafe.Pointer) (val unsafe.Pointer) |
556 | // type Pointer struct{v unsafe.Pointer} |
557 | // method (*Pointer[T any]) Load() *T |
558 | // func init func() |
559 | // var init$guard bool |
560 | |
561 | // Parse |
562 | var conf loader.Config |
563 | f, err := conf.ParseFile("<input>", input) |
564 | if err != nil { |
565 | t.Fatalf("parse: %v", err) |
566 | } |
567 | conf.CreateFromFiles("p", f) |
568 | |
569 | // Load |
570 | lprog, err := conf.Load() |
571 | if err != nil { |
572 | t.Fatalf("Load: %v", err) |
573 | } |
574 | |
575 | // Create and build SSA |
576 | prog := ssautil.CreateProgram(lprog, ssa.BuilderMode(0)) |
577 | p := prog.Package(lprog.Package("p").Pkg) |
578 | p.Build() |
579 | |
580 | if load := p.Func("Load"); typeparams.ForSignature(load.Signature).Len() != 1 { |
581 | t.Errorf("expected a single type param T for Load got %q", load.Signature) |
582 | } |
583 | if ptr := p.Type("Pointer"); typeparams.ForNamed(ptr.Type().(*types.Named)).Len() != 1 { |
584 | t.Errorf("expected a single type param T for Pointer got %q", ptr.Type()) |
585 | } |
586 | } |
587 | |
588 | func TestGenericWrappers(t *testing.T) { |
589 | if !typeparams.Enabled { |
590 | t.Skip("TestGenericWrappers only works with type parameters enabled.") |
591 | } |
592 | const input = ` |
593 | package p |
594 | |
595 | type S[T any] struct { |
596 | t *T |
597 | } |
598 | |
599 | func (x S[T]) M() T { |
600 | return *(x.t) |
601 | } |
602 | |
603 | var thunk = S[int].M |
604 | |
605 | var g S[int] |
606 | var bound = g.M |
607 | |
608 | type R[T any] struct{ S[T] } |
609 | |
610 | var indirect = R[int].M |
611 | ` |
612 | // The relevant SSA members for this package should look something like this: |
613 | // var bound func() int |
614 | // var thunk func(S[int]) int |
615 | // var wrapper func(R[int]) int |
616 | |
617 | // Parse |
618 | var conf loader.Config |
619 | f, err := conf.ParseFile("<input>", input) |
620 | if err != nil { |
621 | t.Fatalf("parse: %v", err) |
622 | } |
623 | conf.CreateFromFiles("p", f) |
624 | |
625 | // Load |
626 | lprog, err := conf.Load() |
627 | if err != nil { |
628 | t.Fatalf("Load: %v", err) |
629 | } |
630 | |
631 | for _, mode := range []ssa.BuilderMode{ssa.BuilderMode(0), ssa.InstantiateGenerics} { |
632 | // Create and build SSA |
633 | prog := ssautil.CreateProgram(lprog, mode) |
634 | p := prog.Package(lprog.Package("p").Pkg) |
635 | p.Build() |
636 | |
637 | for _, entry := range []struct { |
638 | name string // name of the package variable |
639 | typ string // type of the package variable |
640 | wrapper string // wrapper function to which the package variable is set |
641 | callee string // callee within the wrapper function |
642 | }{ |
643 | { |
644 | "bound", |
645 | "*func() int", |
646 | "(p.S[int]).M$bound", |
647 | "(p.S[int]).M[int]", |
648 | }, |
649 | { |
650 | "thunk", |
651 | "*func(p.S[int]) int", |
652 | "(p.S[int]).M$thunk", |
653 | "(p.S[int]).M[int]", |
654 | }, |
655 | { |
656 | "indirect", |
657 | "*func(p.R[int]) int", |
658 | "(p.R[int]).M$thunk", |
659 | "(p.S[int]).M[int]", |
660 | }, |
661 | } { |
662 | entry := entry |
663 | t.Run(entry.name, func(t *testing.T) { |
664 | v := p.Var(entry.name) |
665 | if v == nil { |
666 | t.Fatalf("Did not find variable for %q in %s", entry.name, p.String()) |
667 | } |
668 | if v.Type().String() != entry.typ { |
669 | t.Errorf("Expected type for variable %s: %q. got %q", v, entry.typ, v.Type()) |
670 | } |
671 | |
672 | // Find the wrapper for v. This is stored exactly once in init. |
673 | var wrapper *ssa.Function |
674 | for _, bb := range p.Func("init").Blocks { |
675 | for _, i := range bb.Instrs { |
676 | if store, ok := i.(*ssa.Store); ok && v == store.Addr { |
677 | switch val := store.Val.(type) { |
678 | case *ssa.Function: |
679 | wrapper = val |
680 | case *ssa.MakeClosure: |
681 | wrapper = val.Fn.(*ssa.Function) |
682 | } |
683 | } |
684 | } |
685 | } |
686 | if wrapper == nil { |
687 | t.Fatalf("failed to find wrapper function for %s", entry.name) |
688 | } |
689 | if wrapper.String() != entry.wrapper { |
690 | t.Errorf("Expected wrapper function %q. got %q", wrapper, entry.wrapper) |
691 | } |
692 | |
693 | // Find the callee within the wrapper. There should be exactly one call. |
694 | var callee *ssa.Function |
695 | for _, bb := range wrapper.Blocks { |
696 | for _, i := range bb.Instrs { |
697 | if call, ok := i.(*ssa.Call); ok { |
698 | callee = call.Call.StaticCallee() |
699 | } |
700 | } |
701 | } |
702 | if callee == nil { |
703 | t.Fatalf("failed to find callee within wrapper %s", wrapper) |
704 | } |
705 | if callee.String() != entry.callee { |
706 | t.Errorf("Expected callee in wrapper %q is %q. got %q", v, entry.callee, callee) |
707 | } |
708 | }) |
709 | } |
710 | } |
711 | } |
712 | |
713 | // TestTypeparamTest builds SSA over compilable examples in $GOROOT/test/typeparam/*.go. |
714 | |
715 | func TestTypeparamTest(t *testing.T) { |
716 | if !typeparams.Enabled { |
717 | return |
718 | } |
719 | |
720 | // Tests use a fake goroot to stub out standard libraries with delcarations in |
721 | // testdata/src. Decreases runtime from ~80s to ~1s. |
722 | |
723 | dir := filepath.Join(build.Default.GOROOT, "test", "typeparam") |
724 | |
725 | // Collect all of the .go files in |
726 | list, err := os.ReadDir(dir) |
727 | if err != nil { |
728 | t.Fatal(err) |
729 | } |
730 | |
731 | for _, entry := range list { |
732 | if entry.Name() == "issue376214.go" { |
733 | continue // investigate variadic + New signature. |
734 | } |
735 | if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") { |
736 | continue // Consider standalone go files. |
737 | } |
738 | input := filepath.Join(dir, entry.Name()) |
739 | t.Run(entry.Name(), func(t *testing.T) { |
740 | src, err := os.ReadFile(input) |
741 | if err != nil { |
742 | t.Fatal(err) |
743 | } |
744 | // Only build test files that can be compiled, or compiled and run. |
745 | if !bytes.HasPrefix(src, []byte("// run")) && !bytes.HasPrefix(src, []byte("// compile")) { |
746 | t.Skipf("not detected as a run test") |
747 | } |
748 | |
749 | t.Logf("Input: %s\n", input) |
750 | |
751 | ctx := build.Default // copy |
752 | ctx.GOROOT = "testdata" // fake goroot. Makes tests ~1s. tests take ~80s. |
753 | |
754 | reportErr := func(err error) { |
755 | t.Error(err) |
756 | } |
757 | conf := loader.Config{Build: &ctx, TypeChecker: types.Config{Error: reportErr}} |
758 | if _, err := conf.FromArgs([]string{input}, true); err != nil { |
759 | t.Fatalf("FromArgs(%s) failed: %s", input, err) |
760 | } |
761 | |
762 | iprog, err := conf.Load() |
763 | if iprog != nil { |
764 | for _, pkg := range iprog.Created { |
765 | for i, e := range pkg.Errors { |
766 | t.Errorf("Loading pkg %s error[%d]=%s", pkg, i, e) |
767 | } |
768 | } |
769 | } |
770 | if err != nil { |
771 | t.Fatalf("conf.Load(%s) failed: %s", input, err) |
772 | } |
773 | |
774 | mode := ssa.SanityCheckFunctions | ssa.InstantiateGenerics |
775 | prog := ssautil.CreateProgram(iprog, mode) |
776 | prog.Build() |
777 | }) |
778 | } |
779 | } |
780 | |
781 | // TestOrderOfOperations ensures order of operations are as intended. |
782 | func TestOrderOfOperations(t *testing.T) { |
783 | // Testing for the order of operations within an expression is done |
784 | // by collecting the sequence of direct function calls within a *Function. |
785 | // Callees are all external functions so they cannot be safely re-ordered by ssa. |
786 | const input = ` |
787 | package p |
788 | |
789 | func a() int |
790 | func b() int |
791 | func c() int |
792 | |
793 | func slice(s []int) []int { return s[a():b()] } |
794 | func sliceMax(s []int) []int { return s[a():b():c()] } |
795 | |
796 | ` |
797 | |
798 | // Parse |
799 | var conf loader.Config |
800 | f, err := conf.ParseFile("<input>", input) |
801 | if err != nil { |
802 | t.Fatalf("parse: %v", err) |
803 | } |
804 | conf.CreateFromFiles("p", f) |
805 | |
806 | // Load |
807 | lprog, err := conf.Load() |
808 | if err != nil { |
809 | t.Fatalf("Load: %v", err) |
810 | } |
811 | |
812 | // Create and build SSA |
813 | prog := ssautil.CreateProgram(lprog, ssa.BuilderMode(0)) |
814 | p := prog.Package(lprog.Package("p").Pkg) |
815 | p.Build() |
816 | |
817 | for _, item := range []struct { |
818 | fn string |
819 | want string // sequence of calls within the function. |
820 | }{ |
821 | {"sliceMax", "[a() b() c()]"}, |
822 | {"slice", "[a() b()]"}, |
823 | } { |
824 | fn := p.Func(item.fn) |
825 | want := item.want |
826 | t.Run(item.fn, func(t *testing.T) { |
827 | t.Parallel() |
828 | |
829 | var calls []string |
830 | for _, b := range fn.Blocks { |
831 | for _, instr := range b.Instrs { |
832 | if call, ok := instr.(ssa.CallInstruction); ok { |
833 | calls = append(calls, call.String()) |
834 | } |
835 | } |
836 | } |
837 | if got := fmt.Sprint(calls); got != want { |
838 | fn.WriteTo(os.Stderr) |
839 | t.Errorf("Expected sequence of function calls in %s was %s. got %s", fn, want, got) |
840 | } |
841 | }) |
842 | } |
843 | } |
844 | |
845 | // TestGenericFunctionSelector ensures generic functions from other packages can be selected. |
846 | func TestGenericFunctionSelector(t *testing.T) { |
847 | if !typeparams.Enabled { |
848 | t.Skip("TestGenericFunctionSelector uses type parameters.") |
849 | } |
850 | |
851 | pkgs := map[string]map[string]string{ |
852 | "main": {"m.go": `package main; import "a"; func main() { a.F[int](); a.G[int,string](); a.H(0) }`}, |
853 | "a": {"a.go": `package a; func F[T any](){}; func G[S, T any](){}; func H[T any](a T){} `}, |
854 | } |
855 | |
856 | for _, mode := range []ssa.BuilderMode{ |
857 | ssa.SanityCheckFunctions, |
858 | ssa.SanityCheckFunctions | ssa.InstantiateGenerics, |
859 | } { |
860 | conf := loader.Config{ |
861 | Build: buildutil.FakeContext(pkgs), |
862 | } |
863 | conf.Import("main") |
864 | |
865 | lprog, err := conf.Load() |
866 | if err != nil { |
867 | t.Errorf("Load failed: %s", err) |
868 | } |
869 | if lprog == nil { |
870 | t.Fatalf("Load returned nil *Program") |
871 | } |
872 | // Create and build SSA |
873 | prog := ssautil.CreateProgram(lprog, mode) |
874 | p := prog.Package(lprog.Package("main").Pkg) |
875 | p.Build() |
876 | |
877 | var callees []string // callees of the CallInstruction.String() in main(). |
878 | for _, b := range p.Func("main").Blocks { |
879 | for _, i := range b.Instrs { |
880 | if call, ok := i.(ssa.CallInstruction); ok { |
881 | if callee := call.Common().StaticCallee(); call != nil { |
882 | callees = append(callees, callee.String()) |
883 | } else { |
884 | t.Errorf("CallInstruction without StaticCallee() %q", call) |
885 | } |
886 | } |
887 | } |
888 | } |
889 | sort.Strings(callees) // ignore the order in the code. |
890 | |
891 | want := "[a.F[int] a.G[int string] a.H[int]]" |
892 | if got := fmt.Sprint(callees); got != want { |
893 | t.Errorf("Expected main() to contain calls %v. got %v", want, got) |
894 | } |
895 | } |
896 | } |
897 |
Members
TestGenericWrappers.RangeStmt_17037.BlockStmt.RangeStmt_17253.BlockStmt.BlockStmt.RangeStmt_18248.bb