github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/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/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() == "issue58513.go" { 733 continue // uses runtime.Caller; unimplemented by go/ssa/interp 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 898 func TestIssue58491(t *testing.T) { 899 // Test that a local type reaches type param in instantiation. 900 testenv.NeedsGo1Point(t, 18) 901 src := ` 902 package p 903 904 func foo[T any](blocking func() (T, error)) error { 905 type result struct { 906 res T 907 error // ensure the method set of result is non-empty 908 } 909 910 res := make(chan result, 1) 911 go func() { 912 var r result 913 r.res, r.error = blocking() 914 res <- r 915 }() 916 r := <-res 917 err := r // require the rtype for result when instantiated 918 return err 919 } 920 var Inst = foo[int] 921 ` 922 fset := token.NewFileSet() 923 f, err := parser.ParseFile(fset, "p.go", src, 0) 924 if err != nil { 925 t.Error(err) 926 } 927 files := []*ast.File{f} 928 929 pkg := types.NewPackage("p", "") 930 conf := &types.Config{} 931 p, _, err := ssautil.BuildPackage(conf, fset, pkg, files, ssa.SanityCheckFunctions|ssa.InstantiateGenerics) 932 if err != nil { 933 t.Fatalf("unexpected error: %v", err) 934 } 935 936 // Find the local type result instantiated with int. 937 var found bool 938 for _, rt := range p.Prog.RuntimeTypes() { 939 if n, ok := rt.(*types.Named); ok { 940 if u, ok := n.Underlying().(*types.Struct); ok { 941 found = true 942 if got, want := n.String(), "p.result"; got != want { 943 t.Errorf("Expected the name %s got: %s", want, got) 944 } 945 if got, want := u.String(), "struct{res int; error}"; got != want { 946 t.Errorf("Expected the underlying type of %s to be %s. got %s", n, want, got) 947 } 948 } 949 } 950 } 951 if !found { 952 t.Error("Failed to find any Named to struct types") 953 } 954 } 955 956 func TestIssue58491Rec(t *testing.T) { 957 // Roughly the same as TestIssue58491 but with a recursive type. 958 testenv.NeedsGo1Point(t, 18) 959 src := ` 960 package p 961 962 func foo[T any]() error { 963 type result struct { 964 res T 965 next *result 966 error // ensure the method set of result is non-empty 967 } 968 969 r := &result{} 970 err := r // require the rtype for result when instantiated 971 return err 972 } 973 var Inst = foo[int] 974 ` 975 fset := token.NewFileSet() 976 f, err := parser.ParseFile(fset, "p.go", src, 0) 977 if err != nil { 978 t.Error(err) 979 } 980 files := []*ast.File{f} 981 982 pkg := types.NewPackage("p", "") 983 conf := &types.Config{} 984 p, _, err := ssautil.BuildPackage(conf, fset, pkg, files, ssa.SanityCheckFunctions|ssa.InstantiateGenerics) 985 if err != nil { 986 t.Fatalf("unexpected error: %v", err) 987 } 988 989 // Find the local type result instantiated with int. 990 var found bool 991 for _, rt := range p.Prog.RuntimeTypes() { 992 if n, ok := rt.(*types.Named); ok { 993 if u, ok := n.Underlying().(*types.Struct); ok { 994 found = true 995 if got, want := n.String(), "p.result"; got != want { 996 t.Errorf("Expected the name %s got: %s", want, got) 997 } 998 if got, want := u.String(), "struct{res int; next *p.result; error}"; got != want { 999 t.Errorf("Expected the underlying type of %s to be %s. got %s", n, want, got) 1000 } 1001 } 1002 } 1003 } 1004 if !found { 1005 t.Error("Failed to find any Named to struct types") 1006 } 1007 }