golang.org/x/tools@v0.21.1-0.20240520172518-788d39e776b1/go/ssa/builder_test.go (about) 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/analysis/analysistest" 24 "golang.org/x/tools/go/buildutil" 25 "golang.org/x/tools/go/loader" 26 "golang.org/x/tools/go/packages" 27 "golang.org/x/tools/go/ssa" 28 "golang.org/x/tools/go/ssa/ssautil" 29 "golang.org/x/tools/internal/aliases" 30 "golang.org/x/tools/internal/testenv" 31 "golang.org/x/tools/internal/testfiles" 32 ) 33 34 func isEmpty(f *ssa.Function) bool { return f.Blocks == nil } 35 36 // Tests that programs partially loaded from gc object files contain 37 // functions with no code for the external portions, but are otherwise ok. 38 func TestBuildPackage(t *testing.T) { 39 testenv.NeedsGoBuild(t) // for importer.Default() 40 41 input := ` 42 package main 43 44 import ( 45 "bytes" 46 "io" 47 "testing" 48 ) 49 50 func main() { 51 var t testing.T 52 t.Parallel() // static call to external declared method 53 t.Fail() // static call to promoted external declared method 54 testing.Short() // static call to external package-level function 55 56 var w io.Writer = new(bytes.Buffer) 57 w.Write(nil) // interface invoke of external declared method 58 } 59 ` 60 61 // Parse the file. 62 fset := token.NewFileSet() 63 f, err := parser.ParseFile(fset, "input.go", input, 0) 64 if err != nil { 65 t.Fatal(err) 66 return 67 } 68 69 // Build an SSA program from the parsed file. 70 // Load its dependencies from gc binary export data. 71 mode := ssa.SanityCheckFunctions 72 mainPkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset, 73 types.NewPackage("main", ""), []*ast.File{f}, mode) 74 if err != nil { 75 t.Fatal(err) 76 return 77 } 78 79 // The main package, its direct and indirect dependencies are loaded. 80 deps := []string{ 81 // directly imported dependencies: 82 "bytes", "io", "testing", 83 // indirect dependencies mentioned by 84 // the direct imports' export data 85 "sync", "unicode", "time", 86 } 87 88 prog := mainPkg.Prog 89 all := prog.AllPackages() 90 if len(all) <= len(deps) { 91 t.Errorf("unexpected set of loaded packages: %q", all) 92 } 93 for _, path := range deps { 94 pkg := prog.ImportedPackage(path) 95 if pkg == nil { 96 t.Errorf("package not loaded: %q", path) 97 continue 98 } 99 100 // External packages should have no function bodies (except for wrappers). 101 isExt := pkg != mainPkg 102 103 // init() 104 if isExt && !isEmpty(pkg.Func("init")) { 105 t.Errorf("external package %s has non-empty init", pkg) 106 } else if !isExt && isEmpty(pkg.Func("init")) { 107 t.Errorf("main package %s has empty init", pkg) 108 } 109 110 for _, mem := range pkg.Members { 111 switch mem := mem.(type) { 112 case *ssa.Function: 113 // Functions at package level. 114 if isExt && !isEmpty(mem) { 115 t.Errorf("external function %s is non-empty", mem) 116 } else if !isExt && isEmpty(mem) { 117 t.Errorf("function %s is empty", mem) 118 } 119 120 case *ssa.Type: 121 // Methods of named types T. 122 // (In this test, all exported methods belong to *T not T.) 123 if !isExt { 124 t.Fatalf("unexpected name type in main package: %s", mem) 125 } 126 mset := prog.MethodSets.MethodSet(types.NewPointer(mem.Type())) 127 for i, n := 0, mset.Len(); i < n; i++ { 128 m := prog.MethodValue(mset.At(i)) 129 // For external types, only synthetic wrappers have code. 130 expExt := !strings.Contains(m.Synthetic, "wrapper") 131 if expExt && !isEmpty(m) { 132 t.Errorf("external method %s is non-empty: %s", 133 m, m.Synthetic) 134 } else if !expExt && isEmpty(m) { 135 t.Errorf("method function %s is empty: %s", 136 m, m.Synthetic) 137 } 138 } 139 } 140 } 141 } 142 143 expectedCallee := []string{ 144 "(*testing.T).Parallel", 145 "(*testing.common).Fail", 146 "testing.Short", 147 "N/A", 148 } 149 callNum := 0 150 for _, b := range mainPkg.Func("main").Blocks { 151 for _, instr := range b.Instrs { 152 switch instr := instr.(type) { 153 case ssa.CallInstruction: 154 call := instr.Common() 155 if want := expectedCallee[callNum]; want != "N/A" { 156 got := call.StaticCallee().String() 157 if want != got { 158 t.Errorf("call #%d from main.main: got callee %s, want %s", 159 callNum, got, want) 160 } 161 } 162 callNum++ 163 } 164 } 165 } 166 if callNum != 4 { 167 t.Errorf("in main.main: got %d calls, want %d", callNum, 4) 168 } 169 } 170 171 // Tests that methods from indirect dependencies not subject to 172 // CreatePackage are created as needed. 173 func TestNoIndirectCreatePackage(t *testing.T) { 174 testenv.NeedsGoBuild(t) // for go/packages 175 176 dir := testfiles.ExtractTxtarToTmp(t, filepath.Join(analysistest.TestData(), "indirect.txtar")) 177 pkgs, err := loadPackages(dir, "testdata/a") 178 if err != nil { 179 t.Fatal(err) 180 } 181 a := pkgs[0] 182 183 // Create a from syntax, its direct deps b from types, but not indirect deps c. 184 prog := ssa.NewProgram(a.Fset, ssa.SanityCheckFunctions|ssa.PrintFunctions) 185 aSSA := prog.CreatePackage(a.Types, a.Syntax, a.TypesInfo, false) 186 for _, p := range a.Types.Imports() { 187 prog.CreatePackage(p, nil, nil, true) 188 } 189 190 // Build SSA for package a. 191 aSSA.Build() 192 193 // Find the function in the sole call in the sole block of function a.A. 194 var got string 195 for _, instr := range aSSA.Members["A"].(*ssa.Function).Blocks[0].Instrs { 196 if call, ok := instr.(*ssa.Call); ok { 197 f := call.Call.Value.(*ssa.Function) 198 got = fmt.Sprintf("%v # %s", f, f.Synthetic) 199 break 200 } 201 } 202 want := "(testdata/c.C).F # from type information (on demand)" 203 if got != want { 204 t.Errorf("for sole call in a.A, got: <<%s>>, want <<%s>>", got, want) 205 } 206 } 207 208 // loadPackages loads packages from the specified directory, using LoadSyntax. 209 func loadPackages(dir string, patterns ...string) ([]*packages.Package, error) { 210 cfg := &packages.Config{ 211 Dir: dir, 212 Mode: packages.LoadSyntax, 213 Env: append(os.Environ(), 214 "GO111MODULES=on", 215 "GOPATH=", 216 "GOWORK=off", 217 "GOPROXY=off"), 218 } 219 pkgs, err := packages.Load(cfg, patterns...) 220 if err != nil { 221 return nil, err 222 } 223 if packages.PrintErrors(pkgs) > 0 { 224 return nil, fmt.Errorf("there were errors") 225 } 226 return pkgs, nil 227 } 228 229 // TestRuntimeTypes tests that (*Program).RuntimeTypes() includes all necessary types. 230 func TestRuntimeTypes(t *testing.T) { 231 testenv.NeedsGoBuild(t) // for importer.Default() 232 233 // TODO(adonovan): these test cases don't really make logical 234 // sense any more. Rethink. 235 236 tests := []struct { 237 input string 238 want []string 239 }{ 240 // An package-level type is needed. 241 {`package A; type T struct{}; func (T) f() {}; var x any = T{}`, 242 []string{"*p.T", "p.T"}, 243 }, 244 // An unexported package-level type is not needed. 245 {`package B; type t struct{}; func (t) f() {}`, 246 nil, 247 }, 248 // Subcomponents of type of exported package-level var are needed. 249 {`package C; import "bytes"; var V struct {*bytes.Buffer}; var x any = &V`, 250 []string{"*bytes.Buffer", "*struct{*bytes.Buffer}", "struct{*bytes.Buffer}"}, 251 }, 252 // Subcomponents of type of unexported package-level var are not needed. 253 {`package D; import "bytes"; var v struct {*bytes.Buffer}; var x any = v`, 254 []string{"*bytes.Buffer", "struct{*bytes.Buffer}"}, 255 }, 256 // Subcomponents of type of exported package-level function are needed. 257 {`package E; import "bytes"; func F(struct {*bytes.Buffer}) {}; var v any = F`, 258 []string{"*bytes.Buffer", "struct{*bytes.Buffer}"}, 259 }, 260 // Subcomponents of type of unexported package-level function are not needed. 261 {`package F; import "bytes"; func f(struct {*bytes.Buffer}) {}; var v any = f`, 262 []string{"*bytes.Buffer", "struct{*bytes.Buffer}"}, 263 }, 264 // Subcomponents of type of exported method of uninstantiated unexported type are not needed. 265 {`package G; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v x`, 266 nil, 267 }, 268 // ...unless used by MakeInterface. 269 {`package G2; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v interface{} = x{}`, 270 []string{"*bytes.Buffer", "*p.x", "p.x", "struct{*bytes.Buffer}"}, 271 }, 272 // Subcomponents of type of unexported method are not needed. 273 {`package I; import "bytes"; type X struct{}; func (X) G(struct {*bytes.Buffer}) {}; var x any = X{}`, 274 []string{"*bytes.Buffer", "*p.X", "p.X", "struct{*bytes.Buffer}"}, 275 }, 276 // Local types aren't needed. 277 {`package J; import "bytes"; func f() { type T struct {*bytes.Buffer}; var t T; _ = t }`, 278 nil, 279 }, 280 // ...unless used by MakeInterface. 281 {`package K; import "bytes"; func f() { type T struct {*bytes.Buffer}; _ = interface{}(T{}) }`, 282 []string{"*bytes.Buffer", "*p.T", "p.T"}, 283 }, 284 // Types used as operand of MakeInterface are needed. 285 {`package L; import "bytes"; func f() { _ = interface{}(struct{*bytes.Buffer}{}) }`, 286 []string{"*bytes.Buffer", "struct{*bytes.Buffer}"}, 287 }, 288 // MakeInterface is optimized away when storing to a blank. 289 {`package M; import "bytes"; var _ interface{} = struct{*bytes.Buffer}{}`, 290 nil, 291 }, 292 // MakeInterface does not create runtime type for parameterized types. 293 {`package N; var g interface{}; func f[S any]() { var v []S; g = v }; `, 294 nil, 295 }, 296 } 297 for _, test := range tests { 298 // Parse the file. 299 fset := token.NewFileSet() 300 f, err := parser.ParseFile(fset, "input.go", test.input, 0) 301 if err != nil { 302 t.Errorf("test %q: %s", test.input[:15], err) 303 continue 304 } 305 306 // Create a single-file main package. 307 // Load dependencies from gc binary export data. 308 mode := ssa.SanityCheckFunctions 309 ssapkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset, 310 types.NewPackage("p", ""), []*ast.File{f}, mode) 311 if err != nil { 312 t.Errorf("test %q: %s", test.input[:15], err) 313 continue 314 } 315 316 var typstrs []string 317 for _, T := range ssapkg.Prog.RuntimeTypes() { 318 if types.IsInterface(T) || types.NewMethodSet(T).Len() == 0 { 319 continue // skip interfaces and types without methods 320 } 321 typstrs = append(typstrs, T.String()) 322 } 323 sort.Strings(typstrs) 324 325 if !reflect.DeepEqual(typstrs, test.want) { 326 t.Errorf("test 'package %s': got %q, want %q", 327 f.Name.Name, typstrs, test.want) 328 } 329 } 330 } 331 332 // TestInit tests that synthesized init functions are correctly formed. 333 // Bare init functions omit calls to dependent init functions and the use of 334 // an init guard. They are useful in cases where the client uses a different 335 // calling convention for init functions, or cases where it is easier for a 336 // client to analyze bare init functions. Both of these aspects are used by 337 // the llgo compiler for simpler integration with gccgo's runtime library, 338 // and to simplify the analysis whereby it deduces which stores to globals 339 // can be lowered to global initializers. 340 func TestInit(t *testing.T) { 341 tests := []struct { 342 mode ssa.BuilderMode 343 input, want string 344 }{ 345 {0, `package A; import _ "errors"; var i int = 42`, 346 `# Name: A.init 347 # Package: A 348 # Synthetic: package initializer 349 func init(): 350 0: entry P:0 S:2 351 t0 = *init$guard bool 352 if t0 goto 2 else 1 353 1: init.start P:1 S:1 354 *init$guard = true:bool 355 t1 = errors.init() () 356 *i = 42:int 357 jump 2 358 2: init.done P:2 S:0 359 return 360 361 `}, 362 {ssa.BareInits, `package B; import _ "errors"; var i int = 42`, 363 `# Name: B.init 364 # Package: B 365 # Synthetic: package initializer 366 func init(): 367 0: entry P:0 S:0 368 *i = 42:int 369 return 370 371 `}, 372 } 373 for _, test := range tests { 374 // Create a single-file main package. 375 var conf loader.Config 376 f, err := conf.ParseFile("<input>", test.input) 377 if err != nil { 378 t.Errorf("test %q: %s", test.input[:15], err) 379 continue 380 } 381 conf.CreateFromFiles(f.Name.Name, f) 382 383 lprog, err := conf.Load() 384 if err != nil { 385 t.Errorf("test 'package %s': Load: %s", f.Name.Name, err) 386 continue 387 } 388 prog := ssautil.CreateProgram(lprog, test.mode) 389 mainPkg := prog.Package(lprog.Created[0].Pkg) 390 prog.Build() 391 initFunc := mainPkg.Func("init") 392 if initFunc == nil { 393 t.Errorf("test 'package %s': no init function", f.Name.Name) 394 continue 395 } 396 397 var initbuf bytes.Buffer 398 _, err = initFunc.WriteTo(&initbuf) 399 if err != nil { 400 t.Errorf("test 'package %s': WriteTo: %s", f.Name.Name, err) 401 continue 402 } 403 404 if initbuf.String() != test.want { 405 t.Errorf("test 'package %s': got %s, want %s", f.Name.Name, initbuf.String(), test.want) 406 } 407 } 408 } 409 410 // TestSyntheticFuncs checks that the expected synthetic functions are 411 // created, reachable, and not duplicated. 412 func TestSyntheticFuncs(t *testing.T) { 413 const input = `package P 414 type T int 415 func (T) f() int 416 func (*T) g() int 417 var ( 418 // thunks 419 a = T.f 420 b = T.f 421 c = (struct{T}).f 422 d = (struct{T}).f 423 e = (*T).g 424 f = (*T).g 425 g = (struct{*T}).g 426 h = (struct{*T}).g 427 428 // bounds 429 i = T(0).f 430 j = T(0).f 431 k = new(T).g 432 l = new(T).g 433 434 // wrappers 435 m interface{} = struct{T}{} 436 n interface{} = struct{T}{} 437 o interface{} = struct{*T}{} 438 p interface{} = struct{*T}{} 439 q interface{} = new(struct{T}) 440 r interface{} = new(struct{T}) 441 s interface{} = new(struct{*T}) 442 t interface{} = new(struct{*T}) 443 ) 444 ` 445 // Parse 446 var conf loader.Config 447 f, err := conf.ParseFile("<input>", input) 448 if err != nil { 449 t.Fatalf("parse: %v", err) 450 } 451 conf.CreateFromFiles(f.Name.Name, f) 452 453 // Load 454 lprog, err := conf.Load() 455 if err != nil { 456 t.Fatalf("Load: %v", err) 457 } 458 459 // Create and build SSA 460 prog := ssautil.CreateProgram(lprog, ssa.BuilderMode(0)) 461 prog.Build() 462 463 // Enumerate reachable synthetic functions 464 want := map[string]string{ 465 "(*P.T).g$bound": "bound method wrapper for func (*P.T).g() int", 466 "(P.T).f$bound": "bound method wrapper for func (P.T).f() int", 467 468 "(*P.T).g$thunk": "thunk for func (*P.T).g() int", 469 "(P.T).f$thunk": "thunk for func (P.T).f() int", 470 "(struct{*P.T}).g$thunk": "thunk for func (*P.T).g() int", 471 "(struct{P.T}).f$thunk": "thunk for func (P.T).f() int", 472 473 "(*P.T).f": "wrapper for func (P.T).f() int", 474 "(*struct{*P.T}).f": "wrapper for func (P.T).f() int", 475 "(*struct{*P.T}).g": "wrapper for func (*P.T).g() int", 476 "(*struct{P.T}).f": "wrapper for func (P.T).f() int", 477 "(*struct{P.T}).g": "wrapper for func (*P.T).g() int", 478 "(struct{*P.T}).f": "wrapper for func (P.T).f() int", 479 "(struct{*P.T}).g": "wrapper for func (*P.T).g() int", 480 "(struct{P.T}).f": "wrapper for func (P.T).f() int", 481 482 "P.init": "package initializer", 483 } 484 var seen []string // may contain dups 485 for fn := range ssautil.AllFunctions(prog) { 486 if fn.Synthetic == "" { 487 continue 488 } 489 name := fn.String() 490 wantDescr, ok := want[name] 491 if !ok { 492 t.Errorf("got unexpected/duplicate func: %q: %q", name, fn.Synthetic) 493 continue 494 } 495 seen = append(seen, name) 496 497 if wantDescr != fn.Synthetic { 498 t.Errorf("(%s).Synthetic = %q, want %q", name, fn.Synthetic, wantDescr) 499 } 500 } 501 502 for _, name := range seen { 503 delete(want, name) 504 } 505 for fn, descr := range want { 506 t.Errorf("want func: %q: %q", fn, descr) 507 } 508 } 509 510 // TestPhiElimination ensures that dead phis, including those that 511 // participate in a cycle, are properly eliminated. 512 func TestPhiElimination(t *testing.T) { 513 const input = ` 514 package p 515 516 func f() error 517 518 func g(slice []int) { 519 for { 520 for range slice { 521 // e should not be lifted to a dead φ-node. 522 e := f() 523 h(e) 524 } 525 } 526 } 527 528 func h(error) 529 ` 530 // The SSA code for this function should look something like this: 531 // 0: 532 // jump 1 533 // 1: 534 // t0 = len(slice) 535 // jump 2 536 // 2: 537 // t1 = phi [1: -1:int, 3: t2] 538 // t2 = t1 + 1:int 539 // t3 = t2 < t0 540 // if t3 goto 3 else 1 541 // 3: 542 // t4 = f() 543 // t5 = h(t4) 544 // jump 2 545 // 546 // But earlier versions of the SSA construction algorithm would 547 // additionally generate this cycle of dead phis: 548 // 549 // 1: 550 // t7 = phi [0: nil:error, 2: t8] #e 551 // ... 552 // 2: 553 // t8 = phi [1: t7, 3: t4] #e 554 // ... 555 556 // Parse 557 var conf loader.Config 558 f, err := conf.ParseFile("<input>", input) 559 if err != nil { 560 t.Fatalf("parse: %v", err) 561 } 562 conf.CreateFromFiles("p", f) 563 564 // Load 565 lprog, err := conf.Load() 566 if err != nil { 567 t.Fatalf("Load: %v", err) 568 } 569 570 // Create and build SSA 571 prog := ssautil.CreateProgram(lprog, ssa.BuilderMode(0)) 572 p := prog.Package(lprog.Package("p").Pkg) 573 p.Build() 574 g := p.Func("g") 575 576 phis := 0 577 for _, b := range g.Blocks { 578 for _, instr := range b.Instrs { 579 if _, ok := instr.(*ssa.Phi); ok { 580 phis++ 581 } 582 } 583 } 584 if phis != 1 { 585 g.WriteTo(os.Stderr) 586 t.Errorf("expected a single Phi (for the range index), got %d", phis) 587 } 588 } 589 590 // TestGenericDecls ensures that *unused* generic types, methods and functions 591 // signatures can be built. 592 // 593 // TODO(taking): Add calls from non-generic functions to instantiations of generic functions. 594 // TODO(taking): Add globals with types that are instantiations of generic functions. 595 func TestGenericDecls(t *testing.T) { 596 const input = ` 597 package p 598 599 import "unsafe" 600 601 type Pointer[T any] struct { 602 v unsafe.Pointer 603 } 604 605 func (x *Pointer[T]) Load() *T { 606 return (*T)(LoadPointer(&x.v)) 607 } 608 609 func Load[T any](x *Pointer[T]) *T { 610 return x.Load() 611 } 612 613 func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer) 614 ` 615 // The SSA members for this package should look something like this: 616 // func LoadPointer func(addr *unsafe.Pointer) (val unsafe.Pointer) 617 // type Pointer struct{v unsafe.Pointer} 618 // method (*Pointer[T any]) Load() *T 619 // func init func() 620 // var init$guard bool 621 622 // Parse 623 var conf loader.Config 624 f, err := conf.ParseFile("<input>", input) 625 if err != nil { 626 t.Fatalf("parse: %v", err) 627 } 628 conf.CreateFromFiles("p", f) 629 630 // Load 631 lprog, err := conf.Load() 632 if err != nil { 633 t.Fatalf("Load: %v", err) 634 } 635 636 // Create and build SSA 637 prog := ssautil.CreateProgram(lprog, ssa.BuilderMode(0)) 638 p := prog.Package(lprog.Package("p").Pkg) 639 p.Build() 640 641 if load := p.Func("Load"); load.Signature.TypeParams().Len() != 1 { 642 t.Errorf("expected a single type param T for Load got %q", load.Signature) 643 } 644 if ptr := p.Type("Pointer"); ptr.Type().(*types.Named).TypeParams().Len() != 1 { 645 t.Errorf("expected a single type param T for Pointer got %q", ptr.Type()) 646 } 647 } 648 649 func TestGenericWrappers(t *testing.T) { 650 const input = ` 651 package p 652 653 type S[T any] struct { 654 t *T 655 } 656 657 func (x S[T]) M() T { 658 return *(x.t) 659 } 660 661 var thunk = S[int].M 662 663 var g S[int] 664 var bound = g.M 665 666 type R[T any] struct{ S[T] } 667 668 var indirect = R[int].M 669 ` 670 // The relevant SSA members for this package should look something like this: 671 // var bound func() int 672 // var thunk func(S[int]) int 673 // var wrapper func(R[int]) int 674 675 // Parse 676 var conf loader.Config 677 f, err := conf.ParseFile("<input>", input) 678 if err != nil { 679 t.Fatalf("parse: %v", err) 680 } 681 conf.CreateFromFiles("p", f) 682 683 // Load 684 lprog, err := conf.Load() 685 if err != nil { 686 t.Fatalf("Load: %v", err) 687 } 688 689 for _, mode := range []ssa.BuilderMode{ssa.BuilderMode(0), ssa.InstantiateGenerics} { 690 // Create and build SSA 691 prog := ssautil.CreateProgram(lprog, mode) 692 p := prog.Package(lprog.Package("p").Pkg) 693 p.Build() 694 695 for _, entry := range []struct { 696 name string // name of the package variable 697 typ string // type of the package variable 698 wrapper string // wrapper function to which the package variable is set 699 callee string // callee within the wrapper function 700 }{ 701 { 702 "bound", 703 "*func() int", 704 "(p.S[int]).M$bound", 705 "(p.S[int]).M[int]", 706 }, 707 { 708 "thunk", 709 "*func(p.S[int]) int", 710 "(p.S[int]).M$thunk", 711 "(p.S[int]).M[int]", 712 }, 713 { 714 "indirect", 715 "*func(p.R[int]) int", 716 "(p.R[int]).M$thunk", 717 "(p.S[int]).M[int]", 718 }, 719 } { 720 entry := entry 721 t.Run(entry.name, func(t *testing.T) { 722 v := p.Var(entry.name) 723 if v == nil { 724 t.Fatalf("Did not find variable for %q in %s", entry.name, p.String()) 725 } 726 if v.Type().String() != entry.typ { 727 t.Errorf("Expected type for variable %s: %q. got %q", v, entry.typ, v.Type()) 728 } 729 730 // Find the wrapper for v. This is stored exactly once in init. 731 var wrapper *ssa.Function 732 for _, bb := range p.Func("init").Blocks { 733 for _, i := range bb.Instrs { 734 if store, ok := i.(*ssa.Store); ok && v == store.Addr { 735 switch val := store.Val.(type) { 736 case *ssa.Function: 737 wrapper = val 738 case *ssa.MakeClosure: 739 wrapper = val.Fn.(*ssa.Function) 740 } 741 } 742 } 743 } 744 if wrapper == nil { 745 t.Fatalf("failed to find wrapper function for %s", entry.name) 746 } 747 if wrapper.String() != entry.wrapper { 748 t.Errorf("Expected wrapper function %q. got %q", wrapper, entry.wrapper) 749 } 750 751 // Find the callee within the wrapper. There should be exactly one call. 752 var callee *ssa.Function 753 for _, bb := range wrapper.Blocks { 754 for _, i := range bb.Instrs { 755 if call, ok := i.(*ssa.Call); ok { 756 callee = call.Call.StaticCallee() 757 } 758 } 759 } 760 if callee == nil { 761 t.Fatalf("failed to find callee within wrapper %s", wrapper) 762 } 763 if callee.String() != entry.callee { 764 t.Errorf("Expected callee in wrapper %q is %q. got %q", v, entry.callee, callee) 765 } 766 }) 767 } 768 } 769 } 770 771 // TestTypeparamTest builds SSA over compilable examples in $GOROOT/test/typeparam/*.go. 772 773 func TestTypeparamTest(t *testing.T) { 774 // Tests use a fake goroot to stub out standard libraries with delcarations in 775 // testdata/src. Decreases runtime from ~80s to ~1s. 776 777 dir := filepath.Join(build.Default.GOROOT, "test", "typeparam") 778 779 // Collect all of the .go files in 780 list, err := os.ReadDir(dir) 781 if err != nil { 782 t.Fatal(err) 783 } 784 785 for _, entry := range list { 786 if entry.Name() == "issue58513.go" { 787 continue // uses runtime.Caller; unimplemented by go/ssa/interp 788 } 789 if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") { 790 continue // Consider standalone go files. 791 } 792 input := filepath.Join(dir, entry.Name()) 793 t.Run(entry.Name(), func(t *testing.T) { 794 src, err := os.ReadFile(input) 795 if err != nil { 796 t.Fatal(err) 797 } 798 // Only build test files that can be compiled, or compiled and run. 799 if !bytes.HasPrefix(src, []byte("// run")) && !bytes.HasPrefix(src, []byte("// compile")) { 800 t.Skipf("not detected as a run test") 801 } 802 803 t.Logf("Input: %s\n", input) 804 805 ctx := build.Default // copy 806 ctx.GOROOT = "testdata" // fake goroot. Makes tests ~1s. tests take ~80s. 807 808 reportErr := func(err error) { 809 t.Error(err) 810 } 811 conf := loader.Config{Build: &ctx, TypeChecker: types.Config{Error: reportErr}} 812 if _, err := conf.FromArgs([]string{input}, true); err != nil { 813 t.Fatalf("FromArgs(%s) failed: %s", input, err) 814 } 815 816 iprog, err := conf.Load() 817 if iprog != nil { 818 for _, pkg := range iprog.Created { 819 for i, e := range pkg.Errors { 820 t.Errorf("Loading pkg %s error[%d]=%s", pkg, i, e) 821 } 822 } 823 } 824 if err != nil { 825 t.Fatalf("conf.Load(%s) failed: %s", input, err) 826 } 827 828 mode := ssa.SanityCheckFunctions | ssa.InstantiateGenerics 829 prog := ssautil.CreateProgram(iprog, mode) 830 prog.Build() 831 }) 832 } 833 } 834 835 // TestOrderOfOperations ensures order of operations are as intended. 836 func TestOrderOfOperations(t *testing.T) { 837 // Testing for the order of operations within an expression is done 838 // by collecting the sequence of direct function calls within a *Function. 839 // Callees are all external functions so they cannot be safely re-ordered by ssa. 840 const input = ` 841 package p 842 843 func a() int 844 func b() int 845 func c() int 846 847 func slice(s []int) []int { return s[a():b()] } 848 func sliceMax(s []int) []int { return s[a():b():c()] } 849 850 ` 851 852 // Parse 853 var conf loader.Config 854 f, err := conf.ParseFile("<input>", input) 855 if err != nil { 856 t.Fatalf("parse: %v", err) 857 } 858 conf.CreateFromFiles("p", f) 859 860 // Load 861 lprog, err := conf.Load() 862 if err != nil { 863 t.Fatalf("Load: %v", err) 864 } 865 866 // Create and build SSA 867 prog := ssautil.CreateProgram(lprog, ssa.BuilderMode(0)) 868 p := prog.Package(lprog.Package("p").Pkg) 869 p.Build() 870 871 for _, item := range []struct { 872 fn string 873 want string // sequence of calls within the function. 874 }{ 875 {"sliceMax", "[a() b() c()]"}, 876 {"slice", "[a() b()]"}, 877 } { 878 fn := p.Func(item.fn) 879 want := item.want 880 t.Run(item.fn, func(t *testing.T) { 881 t.Parallel() 882 883 var calls []string 884 for _, b := range fn.Blocks { 885 for _, instr := range b.Instrs { 886 if call, ok := instr.(ssa.CallInstruction); ok { 887 calls = append(calls, call.String()) 888 } 889 } 890 } 891 if got := fmt.Sprint(calls); got != want { 892 fn.WriteTo(os.Stderr) 893 t.Errorf("Expected sequence of function calls in %s was %s. got %s", fn, want, got) 894 } 895 }) 896 } 897 } 898 899 // TestGenericFunctionSelector ensures generic functions from other packages can be selected. 900 func TestGenericFunctionSelector(t *testing.T) { 901 pkgs := map[string]map[string]string{ 902 "main": {"m.go": `package main; import "a"; func main() { a.F[int](); a.G[int,string](); a.H(0) }`}, 903 "a": {"a.go": `package a; func F[T any](){}; func G[S, T any](){}; func H[T any](a T){} `}, 904 } 905 906 for _, mode := range []ssa.BuilderMode{ 907 ssa.SanityCheckFunctions, 908 ssa.SanityCheckFunctions | ssa.InstantiateGenerics, 909 } { 910 conf := loader.Config{ 911 Build: buildutil.FakeContext(pkgs), 912 } 913 conf.Import("main") 914 915 lprog, err := conf.Load() 916 if err != nil { 917 t.Errorf("Load failed: %s", err) 918 } 919 if lprog == nil { 920 t.Fatalf("Load returned nil *Program") 921 } 922 // Create and build SSA 923 prog := ssautil.CreateProgram(lprog, mode) 924 p := prog.Package(lprog.Package("main").Pkg) 925 p.Build() 926 927 var callees []string // callees of the CallInstruction.String() in main(). 928 for _, b := range p.Func("main").Blocks { 929 for _, i := range b.Instrs { 930 if call, ok := i.(ssa.CallInstruction); ok { 931 if callee := call.Common().StaticCallee(); call != nil { 932 callees = append(callees, callee.String()) 933 } else { 934 t.Errorf("CallInstruction without StaticCallee() %q", call) 935 } 936 } 937 } 938 } 939 sort.Strings(callees) // ignore the order in the code. 940 941 want := "[a.F[int] a.G[int string] a.H[int]]" 942 if got := fmt.Sprint(callees); got != want { 943 t.Errorf("Expected main() to contain calls %v. got %v", want, got) 944 } 945 } 946 } 947 948 func TestIssue58491(t *testing.T) { 949 // Test that a local type reaches type param in instantiation. 950 src := ` 951 package p 952 953 func foo[T any](blocking func() (T, error)) error { 954 type result struct { 955 res T 956 error // ensure the method set of result is non-empty 957 } 958 959 res := make(chan result, 1) 960 go func() { 961 var r result 962 r.res, r.error = blocking() 963 res <- r 964 }() 965 r := <-res 966 err := r // require the rtype for result when instantiated 967 return err 968 } 969 var Inst = foo[int] 970 ` 971 fset := token.NewFileSet() 972 f, err := parser.ParseFile(fset, "p.go", src, 0) 973 if err != nil { 974 t.Fatal(err) 975 } 976 files := []*ast.File{f} 977 978 pkg := types.NewPackage("p", "") 979 conf := &types.Config{} 980 p, _, err := ssautil.BuildPackage(conf, fset, pkg, files, ssa.SanityCheckFunctions|ssa.InstantiateGenerics) 981 if err != nil { 982 t.Fatalf("unexpected error: %v", err) 983 } 984 985 // Find the local type result instantiated with int. 986 var found bool 987 for _, rt := range p.Prog.RuntimeTypes() { 988 if n, ok := rt.(*types.Named); ok { 989 if u, ok := n.Underlying().(*types.Struct); ok { 990 found = true 991 if got, want := n.String(), "p.result"; got != want { 992 t.Errorf("Expected the name %s got: %s", want, got) 993 } 994 if got, want := u.String(), "struct{res int; error}"; got != want { 995 t.Errorf("Expected the underlying type of %s to be %s. got %s", n, want, got) 996 } 997 } 998 } 999 } 1000 if !found { 1001 t.Error("Failed to find any Named to struct types") 1002 } 1003 } 1004 1005 func TestIssue58491Rec(t *testing.T) { 1006 // Roughly the same as TestIssue58491 but with a recursive type. 1007 src := ` 1008 package p 1009 1010 func foo[T any]() error { 1011 type result struct { 1012 res T 1013 next *result 1014 error // ensure the method set of result is non-empty 1015 } 1016 1017 r := &result{} 1018 err := r // require the rtype for result when instantiated 1019 return err 1020 } 1021 var Inst = foo[int] 1022 ` 1023 fset := token.NewFileSet() 1024 f, err := parser.ParseFile(fset, "p.go", src, 0) 1025 if err != nil { 1026 t.Fatal(err) 1027 } 1028 files := []*ast.File{f} 1029 1030 pkg := types.NewPackage("p", "") 1031 conf := &types.Config{} 1032 p, _, err := ssautil.BuildPackage(conf, fset, pkg, files, ssa.SanityCheckFunctions|ssa.InstantiateGenerics) 1033 if err != nil { 1034 t.Fatalf("unexpected error: %v", err) 1035 } 1036 1037 // Find the local type result instantiated with int. 1038 var found bool 1039 for _, rt := range p.Prog.RuntimeTypes() { 1040 if n, ok := aliases.Unalias(rt).(*types.Named); ok { 1041 if u, ok := n.Underlying().(*types.Struct); ok { 1042 found = true 1043 if got, want := n.String(), "p.result"; got != want { 1044 t.Errorf("Expected the name %s got: %s", want, got) 1045 } 1046 if got, want := u.String(), "struct{res int; next *p.result; error}"; got != want { 1047 t.Errorf("Expected the underlying type of %s to be %s. got %s", n, want, got) 1048 } 1049 } 1050 } 1051 } 1052 if !found { 1053 t.Error("Failed to find any Named to struct types") 1054 } 1055 } 1056 1057 // TestSyntax ensures that a function's Syntax is available. 1058 func TestSyntax(t *testing.T) { 1059 const input = `package p 1060 1061 type P int 1062 func (x *P) g() *P { return x } 1063 1064 func F[T ~int]() *T { 1065 type S1 *T 1066 type S2 *T 1067 type S3 *T 1068 f1 := func() S1 { 1069 f2 := func() S2 { 1070 return S2(nil) 1071 } 1072 return S1(f2()) 1073 } 1074 f3 := func() S3 { 1075 return S3(f1()) 1076 } 1077 return (*T)(f3()) 1078 } 1079 var g = F[int] 1080 var _ = F[P] // unreferenced => not instantiated 1081 ` 1082 1083 // Parse 1084 var conf loader.Config 1085 f, err := conf.ParseFile("<input>", input) 1086 if err != nil { 1087 t.Fatalf("parse: %v", err) 1088 } 1089 conf.CreateFromFiles("p", f) 1090 1091 // Load 1092 lprog, err := conf.Load() 1093 if err != nil { 1094 t.Fatalf("Load: %v", err) 1095 } 1096 1097 // Create and build SSA 1098 prog := ssautil.CreateProgram(lprog, ssa.InstantiateGenerics) 1099 prog.Build() 1100 1101 // Collect syntax information for all of the functions. 1102 got := make(map[string]string) 1103 for fn := range ssautil.AllFunctions(prog) { 1104 if fn.Name() == "init" { 1105 continue 1106 } 1107 syntax := fn.Syntax() 1108 if got[fn.Name()] != "" { 1109 t.Error("dup") 1110 } 1111 got[fn.Name()] = fmt.Sprintf("%T : %s @ %d", syntax, fn.Signature, prog.Fset.Position(syntax.Pos()).Line) 1112 } 1113 1114 want := map[string]string{ 1115 "g": "*ast.FuncDecl : func() *p.P @ 4", 1116 "F": "*ast.FuncDecl : func[T ~int]() *T @ 6", 1117 "F$1": "*ast.FuncLit : func() p.S1 @ 10", 1118 "F$1$1": "*ast.FuncLit : func() p.S2 @ 11", 1119 "F$2": "*ast.FuncLit : func() p.S3 @ 16", 1120 "F[int]": "*ast.FuncDecl : func() *int @ 6", 1121 "F[int]$1": "*ast.FuncLit : func() p.S1 @ 10", 1122 "F[int]$1$1": "*ast.FuncLit : func() p.S2 @ 11", 1123 "F[int]$2": "*ast.FuncLit : func() p.S3 @ 16", 1124 // ...but no F[P] etc as they are unreferenced. 1125 // (NB: GlobalDebug mode would cause them to be referenced.) 1126 } 1127 if !reflect.DeepEqual(got, want) { 1128 t.Errorf("Expected the functions with signature to be:\n\t%#v.\n Got:\n\t%#v", want, got) 1129 } 1130 } 1131 1132 func TestGo117Builtins(t *testing.T) { 1133 tests := []struct { 1134 name string 1135 src string 1136 importer types.Importer 1137 }{ 1138 {"slice to array pointer", "package p; var s []byte; var _ = (*[4]byte)(s)", nil}, 1139 {"unsafe slice", `package p; import "unsafe"; var _ = unsafe.Add(nil, 0)`, importer.Default()}, 1140 {"unsafe add", `package p; import "unsafe"; var _ = unsafe.Slice((*int)(nil), 0)`, importer.Default()}, 1141 } 1142 1143 for _, tc := range tests { 1144 tc := tc 1145 t.Run(tc.name, func(t *testing.T) { 1146 t.Parallel() 1147 fset := token.NewFileSet() 1148 f, err := parser.ParseFile(fset, "p.go", tc.src, parser.ParseComments) 1149 if err != nil { 1150 t.Error(err) 1151 } 1152 files := []*ast.File{f} 1153 1154 pkg := types.NewPackage("p", "") 1155 conf := &types.Config{Importer: tc.importer} 1156 if _, _, err := ssautil.BuildPackage(conf, fset, pkg, files, ssa.SanityCheckFunctions); err != nil { 1157 t.Error(err) 1158 } 1159 }) 1160 } 1161 } 1162 1163 // TestLabels just tests that anonymous labels are handled. 1164 func TestLabels(t *testing.T) { 1165 tests := []string{ 1166 `package main 1167 func main() { _:println(1) }`, 1168 `package main 1169 func main() { _:println(1); _:println(2)}`, 1170 } 1171 for _, test := range tests { 1172 conf := loader.Config{Fset: token.NewFileSet()} 1173 f, err := parser.ParseFile(conf.Fset, "<input>", test, 0) 1174 if err != nil { 1175 t.Errorf("parse error: %s", err) 1176 return 1177 } 1178 conf.CreateFromFiles("main", f) 1179 iprog, err := conf.Load() 1180 if err != nil { 1181 t.Error(err) 1182 continue 1183 } 1184 prog := ssautil.CreateProgram(iprog, ssa.BuilderMode(0)) 1185 pkg := prog.Package(iprog.Created[0].Pkg) 1186 pkg.Build() 1187 } 1188 }