...

Source file src/golang.org/x/tools/go/ssa/builder_test.go

Documentation: golang.org/x/tools/go/ssa

     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  }
  1189  
  1190  // TestRangeOverFuncNoPanic ensures that go1.23 range-over-func doesn't
  1191  // panic the SSA builder. (Instead, it compiles to a panic instruction.)
  1192  // This is a short-term stopgap until Tim's principled fix in CL 555075 lands.
  1193  func TestRangeOverFuncNoPanic(t *testing.T) {
  1194  	testenv.NeedsGoBuild(t)      // for go/packages
  1195  	testenv.NeedsGo1Point(t, 23) // for range-over-func
  1196  
  1197  	dir := t.TempDir()
  1198  	if err := os.WriteFile(filepath.Join(dir, "rangeoverfunc.go"), []byte(`
  1199  package main
  1200  
  1201  func main() {
  1202  	for x := range ten {
  1203  		println(x)
  1204  	}
  1205  }
  1206  
  1207  func ten(yield func(int) bool) {
  1208  	for i := range 10 {
  1209  		if !yield(i) {
  1210  			return
  1211  		}
  1212  	}
  1213  }
  1214  `), 0666); err != nil {
  1215  		t.Fatal(err)
  1216  	}
  1217  	pkgs, err := loadPackages(dir, "./rangeoverfunc.go")
  1218  	if err != nil {
  1219  		t.Fatal(err)
  1220  	}
  1221  	pkg := pkgs[0]
  1222  
  1223  	const mode = ssa.SanityCheckFunctions // add ssa.PrintFunctions to debug
  1224  	prog := ssa.NewProgram(pkg.Fset, mode)
  1225  	ssapkg := prog.CreatePackage(pkg.Types, pkg.Syntax, pkg.TypesInfo, false)
  1226  	ssapkg.Build()
  1227  
  1228  	// Expected body of main:
  1229  	//
  1230  	// # Name: main.main
  1231  	// func main():
  1232  	// 0:                                                                entry P:0 S:0
  1233  	// 	panic "go1.23 range-over-func (etc)"
  1234  	main := ssapkg.Members["main"].(*ssa.Function)
  1235  	firstBlock := main.Blocks[0]
  1236  	lastInstr := firstBlock.Instrs[len(firstBlock.Instrs)-1]
  1237  	if _, ok := lastInstr.(*ssa.Panic); !ok {
  1238  		t.Errorf("expected range-over-func to compile to panic, got %T", lastInstr)
  1239  	}
  1240  }
  1241  

View as plain text