...

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

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

     1  // Copyright 2022 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/parser"
    11  	"go/token"
    12  	"reflect"
    13  	"sort"
    14  	"testing"
    15  
    16  	"golang.org/x/tools/go/expect"
    17  	"golang.org/x/tools/go/loader"
    18  	"golang.org/x/tools/go/ssa"
    19  )
    20  
    21  // TestGenericBodies tests that bodies of generic functions and methods containing
    22  // different constructs can be built in BuilderMode(0).
    23  //
    24  // Each test specifies the contents of package containing a single go file.
    25  // Each call print(arg0, arg1, ...) to the builtin print function
    26  // in ssa is correlated a comment at the end of the line of the form:
    27  //
    28  //	//@ types(a, b, c)
    29  //
    30  // where a, b and c are the types of the arguments to the print call
    31  // serialized using go/types.Type.String().
    32  // See x/tools/go/expect for details on the syntax.
    33  func TestGenericBodies(t *testing.T) {
    34  	for _, contents := range []string{
    35  		`
    36  		package p00
    37  
    38  		func f(x int) {
    39  			var i interface{}
    40  			print(i, 0) //@ types("interface{}", int)
    41  			print()     //@ types()
    42  			print(x)    //@ types(int)
    43  		}
    44  		`,
    45  		`
    46  		package p01
    47  
    48  		func f[T any](x T) {
    49  			print(x) //@ types(T)
    50  		}
    51  		`,
    52  		`
    53  		package p02
    54  
    55  		func f[T ~int]() {
    56  			var x T
    57  			print(x) //@ types(T)
    58  		}
    59  		`,
    60  		`
    61  		package p03
    62  
    63  		func a[T ~[4]byte](x T) {
    64  			for k, v := range x {
    65  				print(x, k, v) //@ types(T, int, byte)
    66  			}
    67  		}
    68  		func b[T ~*[4]byte](x T) {
    69  			for k, v := range x {
    70  				print(x, k, v) //@ types(T, int, byte)
    71  			}
    72  		}
    73  		func c[T ~[]byte](x T) {
    74  			for k, v := range x {
    75  				print(x, k, v) //@ types(T, int, byte)
    76  			}
    77  		}
    78  		func d[T ~string](x T) {
    79  			for k, v := range x {
    80  				print(x, k, v) //@ types(T, int, rune)
    81  			}
    82  		}
    83  		func e[T ~map[int]string](x T) {
    84  			for k, v := range x {
    85  				print(x, k, v) //@ types(T, int, string)
    86  			}
    87  		}
    88  		func f[T ~chan string](x T) {
    89  			for v := range x {
    90  				print(x, v) //@ types(T, string)
    91  			}
    92  		}
    93  
    94  		func From() {
    95  			type A [4]byte
    96  			print(a[A]) //@ types("func(x p03.A)")
    97  
    98  			type B *[4]byte
    99  			print(b[B]) //@ types("func(x p03.B)")
   100  
   101  			type C []byte
   102  			print(c[C]) //@ types("func(x p03.C)")
   103  
   104  			type D string
   105  			print(d[D]) //@ types("func(x p03.D)")
   106  
   107  			type E map[int]string
   108  			print(e[E]) //@ types("func(x p03.E)")
   109  
   110  			type F chan string
   111  			print(f[F]) //@ types("func(x p03.F)")
   112  		}
   113  		`,
   114  		`
   115  		package p05
   116  
   117  		func f[S any, T ~chan S](x T) {
   118  			for v := range x {
   119  				print(x, v) //@ types(T, S)
   120  			}
   121  		}
   122  
   123  		func From() {
   124  			type F chan string
   125  			print(f[string, F]) //@ types("func(x p05.F)")
   126  		}
   127  		`,
   128  		`
   129  		package p06
   130  
   131  		func fibonacci[T ~chan int](c, quit T) {
   132  			x, y := 0, 1
   133  			for {
   134  				select {
   135  				case c <- x:
   136  					x, y = y, x+y
   137  				case <-quit:
   138  					print(c, quit, x, y) //@ types(T, T, int, int)
   139  					return
   140  				}
   141  			}
   142  		}
   143  		func start[T ~chan int](c, quit T) {
   144  			go func() {
   145  				for i := 0; i < 10; i++ {
   146  					print(<-c) //@ types(int)
   147  				}
   148  				quit <- 0
   149  			}()
   150  		}
   151  		func From() {
   152  			type F chan int
   153  			c := make(F)
   154  			quit := make(F)
   155  			print(start[F], c, quit)     //@ types("func(c p06.F, quit p06.F)", "p06.F", "p06.F")
   156  			print(fibonacci[F], c, quit) //@ types("func(c p06.F, quit p06.F)", "p06.F", "p06.F")
   157  		}
   158  		`,
   159  		`
   160  		package p07
   161  
   162  		func f[T ~struct{ x int; y string }](i int) T {
   163  			u := []T{ T{0, "lorem"},  T{1, "ipsum"}}
   164  			return u[i]
   165  		}
   166  		func From() {
   167  			type S struct{ x int; y string }
   168  			print(f[S])     //@ types("func(i int) p07.S")
   169  		}
   170  		`,
   171  		`
   172  		package p08
   173  
   174  		func f[T ~[4]int8](x T, l, h int) []int8 {
   175  			return x[l:h]
   176  		}
   177  		func g[T ~*[4]int16](x T, l, h int) []int16 {
   178  			return x[l:h]
   179  		}
   180  		func h[T ~[]int32](x T, l, h int) T {
   181  			return x[l:h]
   182  		}
   183  		func From() {
   184  			type F [4]int8
   185  			type G *[4]int16
   186  			type H []int32
   187  			print(f[F](F{}, 0, 0))  //@ types("[]int8")
   188  			print(g[G](nil, 0, 0)) //@ types("[]int16")
   189  			print(h[H](nil, 0, 0)) //@ types("p08.H")
   190  		}
   191  		`,
   192  		`
   193  		package p09
   194  
   195  		func h[E any, T ~[]E](x T, l, h int) []E {
   196  			s := x[l:h]
   197  			print(s) //@ types("T")
   198  			return s
   199  		}
   200  		func From() {
   201  			type H []int32
   202  			print(h[int32, H](nil, 0, 0)) //@ types("[]int32")
   203  		}
   204  		`,
   205  		`
   206  		package p10
   207  
   208  		// Test "make" builtin with different forms on core types and
   209  		// when capacities are constants or variable.
   210  		func h[E any, T ~[]E](m, n int) {
   211  			print(make(T, 3))    //@ types(T)
   212  			print(make(T, 3, 5)) //@ types(T)
   213  			print(make(T, m))    //@ types(T)
   214  			print(make(T, m, n)) //@ types(T)
   215  		}
   216  		func i[K comparable, E any, T ~map[K]E](m int) {
   217  			print(make(T))    //@ types(T)
   218  			print(make(T, 5)) //@ types(T)
   219  			print(make(T, m)) //@ types(T)
   220  		}
   221  		func j[E any, T ~chan E](m int) {
   222  			print(make(T))    //@ types(T)
   223  			print(make(T, 6)) //@ types(T)
   224  			print(make(T, m)) //@ types(T)
   225  		}
   226  		func From() {
   227  			type H []int32
   228  			h[int32, H](3, 4)
   229  			type I map[int8]H
   230  			i[int8, H, I](5)
   231  			type J chan I
   232  			j[I, J](6)
   233  		}
   234  		`,
   235  		`
   236  		package p11
   237  
   238  		func h[T ~[4]int](x T) {
   239  			print(len(x), cap(x)) //@ types(int, int)
   240  		}
   241  		func i[T ~[4]byte | []int | ~chan uint8](x T) {
   242  			print(len(x), cap(x)) //@ types(int, int)
   243  		}
   244  		func j[T ~[4]int | any | map[string]int]() {
   245  			print(new(T)) //@ types("*T")
   246  		}
   247  		func k[T ~[4]int | any | map[string]int](x T) {
   248  			print(x) //@ types(T)
   249  			panic(x)
   250  		}
   251  		`,
   252  		`
   253  		package p12
   254  
   255  		func f[E any, F ~func() E](x F) {
   256  			print(x, x()) //@ types(F, E)
   257  		}
   258  		func From() {
   259  			type T func() int
   260  			f[int, T](func() int { return 0 })
   261  			f[int, func() int](func() int { return 1 })
   262  		}
   263  		`,
   264  		`
   265  		package p13
   266  
   267  		func f[E any, M ~map[string]E](m M) {
   268  			y, ok := m["lorem"]
   269  			print(m, y, ok) //@ types(M, E, bool)
   270  		}
   271  		func From() {
   272  			type O map[string][]int
   273  			f(O{"lorem": []int{0, 1, 2, 3}})
   274  		}
   275  		`,
   276  		`
   277  		package p14
   278  
   279  		func a[T interface{ []int64 | [5]int64 }](x T) int64 {
   280  			print(x, x[2], x[3]) //@ types(T, int64, int64)
   281  			x[2] = 5
   282  			return x[3]
   283  		}
   284  		func b[T interface{ []byte | string }](x T) byte {
   285  			print(x, x[3]) //@ types(T, byte)
   286  			return x[3]
   287  		}
   288  		func c[T interface{ []byte }](x T) byte {
   289  			print(x, x[2], x[3]) //@ types(T, byte, byte)
   290  			x[2] = 'b'
   291  			return x[3]
   292  		}
   293  		func d[T interface{ map[int]int64 }](x T) int64 {
   294  			print(x, x[2], x[3]) //@ types(T, int64, int64)
   295  			x[2] = 43
   296  			return x[3]
   297  		}
   298  		func e[T ~string](t T) {
   299  			print(t, t[0]) //@ types(T, uint8)
   300  		}
   301  		func f[T ~string|[]byte](t T) {
   302  			print(t, t[0]) //@ types(T, uint8)
   303  		}
   304  		func g[T []byte](t T) {
   305  			print(t, t[0]) //@ types(T, byte)
   306  		}
   307  		func h[T ~[4]int|[]int](t T) {
   308  			print(t, t[0]) //@ types(T, int)
   309  		}
   310  		func i[T ~[4]int|*[4]int|[]int](t T) {
   311  			print(t, t[0]) //@ types(T, int)
   312  		}
   313  		func j[T ~[4]int|*[4]int|[]int](t T) {
   314  			print(t, &t[0]) //@ types(T, "*int")
   315  		}
   316  		`,
   317  		`
   318  		package p15
   319  
   320  		type MyInt int
   321  		type Other int
   322  		type MyInterface interface{ foo() }
   323  
   324  		// ChangeType tests
   325  		func ct0(x int) { v := MyInt(x);  print(x, v) /*@ types(int, "p15.MyInt")*/ }
   326  		func ct1[T MyInt | Other, S int ](x S) { v := T(x);  print(x, v) /*@ types(S, T)*/ }
   327  		func ct2[T int, S MyInt | int ](x S) { v := T(x); print(x, v) /*@ types(S, T)*/ }
   328  		func ct3[T MyInt | Other, S MyInt | int ](x S) { v := T(x) ; print(x, v) /*@ types(S, T)*/ }
   329  
   330  		// Convert tests
   331  		func co0[T int | int8](x MyInt) { v := T(x); print(x, v) /*@ types("p15.MyInt", T)*/}
   332  		func co1[T int | int8](x T) { v := MyInt(x); print(x, v) /*@ types(T, "p15.MyInt")*/ }
   333  		func co2[S, T int | int8](x T) { v := S(x); print(x, v) /*@ types(T, S)*/ }
   334  
   335  		// MakeInterface tests
   336  		func mi0[T MyInterface](x T) { v := MyInterface(x); print(x, v) /*@ types(T, "p15.MyInterface")*/ }
   337  
   338  		// NewConst tests
   339  		func nc0[T any]() { v := (*T)(nil); print(v) /*@ types("*T")*/}
   340  
   341  		// SliceToArrayPointer
   342  		func sl0[T *[4]int | *[2]int](x []int) { v := T(x); print(x, v) /*@ types("[]int", T)*/ }
   343  		func sl1[T *[4]int | *[2]int, S []int](x S) { v := T(x); print(x, v) /*@ types(S, T)*/ }
   344  		`,
   345  		`
   346  		package p16
   347  
   348  		func c[T interface{ foo() string }](x T) {
   349  			print(x, x.foo, x.foo())  /*@ types(T, "func() string", string)*/
   350  		}
   351  		`,
   352  		`
   353  		package p17
   354  
   355  		func eq[T comparable](t T, i interface{}) bool {
   356  			return t == i
   357  		}
   358  		`,
   359  		// TODO(59983): investigate why writing g.c panics in (*FieldAddr).String.
   360  		`
   361  		package p18
   362  
   363  		type S struct{ f int }
   364  		func c[P *S]() []P { return []P{{f: 1}} }
   365  		`,
   366  		`
   367  		package p19
   368  
   369  		func sign[bytes []byte | string](s bytes) (bool, bool) {
   370  			neg := false
   371  			if len(s) > 0 && (s[0] == '-' || s[0] == '+') {
   372  				neg = s[0] == '-'
   373  				s = s[1:]
   374  			}
   375  			return !neg, len(s) > 0
   376  		}
   377  		`,
   378  		`package p20
   379  
   380  		func digits[bytes []byte | string](s bytes) bool {
   381  			for _, c := range []byte(s) {
   382  				if c < '0' || '9' < c {
   383  					return false
   384  				}
   385  			}
   386  			return true
   387  		}
   388  		`,
   389  		`
   390  		package p21
   391  
   392  		type E interface{}
   393  
   394  		func Foo[T E, PT interface{ *T }]() T {
   395  			pt := PT(new(T))
   396  			x := *pt
   397  			print(x)  /*@ types(T)*/
   398  			return x
   399  		}
   400  		`,
   401  		`
   402  		package p22
   403  
   404  		func f[M any, PM *M](p PM) {
   405  			var m M
   406  			*p = m
   407  			print(m)  /*@ types(M)*/
   408  			print(p)  /*@ types(PM)*/
   409  		}
   410  		`,
   411  		`
   412  		package p23
   413  
   414  		type A struct{int}
   415  		func (*A) Marker() {}
   416  
   417  		type B struct{string}
   418  		func (*B) Marker() {}
   419  
   420  		type C struct{float32}
   421  		func (*C) Marker() {}
   422  
   423  		func process[T interface {
   424  			*A
   425  			*B
   426  			*C
   427  			Marker()
   428  		}](v T) {
   429  			v.Marker()
   430  			a := *(any(v).(*A)); print(a)  /*@ types("p23.A")*/
   431  			b := *(any(v).(*B)); print(b)  /*@ types("p23.B")*/
   432  			c := *(any(v).(*C)); print(c)  /*@ types("p23.C")*/
   433  		}
   434  		`,
   435  		`
   436  		package p24
   437  
   438  		func a[T any](f func() [4]T) {
   439  			x := len(f())
   440  			print(x) /*@ types("int")*/
   441  		}
   442  
   443  		func b[T [4]any](f func() T) {
   444  			x := len(f())
   445  			print(x) /*@ types("int")*/
   446  		}
   447  
   448  		func c[T any](f func() *[4]T) {
   449  			x := len(f())
   450  			print(x) /*@ types("int")*/
   451  		}
   452  
   453  		func d[T *[4]any](f func() T) {
   454  			x := len(f())
   455  			print(x) /*@ types("int")*/
   456  		}
   457  		`,
   458  		`
   459  		package p25
   460  
   461  		func a[T any]() {
   462  			var f func() [4]T
   463  			for i, v := range f() {
   464  				print(i, v) /*@ types("int", "T")*/
   465  			}
   466  		}
   467  
   468  		func b[T [4]any](f func() T) {
   469  			for i, v := range f() {
   470  				print(i, v) /*@ types("int", "any")*/
   471  			}
   472  		}
   473  
   474  		func c[T any](f func() *[4]T) {
   475  			for i, v := range f() {
   476  				print(i, v) /*@ types("int", "T")*/
   477  			}
   478  		}
   479  
   480  		func d[T *[4]any](f func() T) {
   481  			for i, v := range f() {
   482  				print(i, v) /*@ types("int", "any")*/
   483  			}
   484  		}
   485  		`,
   486  		`
   487  		package issue64324
   488  
   489  		type bar[T any] interface {
   490  			Bar(int) T
   491  		}
   492  		type foo[T any] interface {
   493  			bar[[]T]
   494  			*T
   495  		}
   496  		func Foo[T any, F foo[T]](d int) {
   497  			m := new(T)
   498  			f := F(m)
   499  			print(f.Bar(d)) /*@ types("[]T")*/
   500  		}
   501  		`, `
   502  		package issue64324b
   503  
   504  		type bar[T any] interface {
   505  			Bar(int) T
   506  		}
   507  		type baz[T any] interface {
   508  			bar[*int]
   509  			*int
   510  		}
   511  
   512  		func Baz[I baz[string]](d int) {
   513  			m := new(int)
   514  			f := I(m)
   515  			print(f.Bar(d)) /*@ types("*int")*/
   516  		}
   517  		`,
   518  	} {
   519  		contents := contents
   520  		pkgname := packageName(t, contents)
   521  		t.Run(pkgname, func(t *testing.T) {
   522  			// Parse
   523  			conf := loader.Config{ParserMode: parser.ParseComments}
   524  			f, err := conf.ParseFile("file.go", contents)
   525  			if err != nil {
   526  				t.Fatalf("parse: %v", err)
   527  			}
   528  			conf.CreateFromFiles(pkgname, f)
   529  
   530  			// Load
   531  			lprog, err := conf.Load()
   532  			if err != nil {
   533  				t.Fatalf("Load: %v", err)
   534  			}
   535  
   536  			// Create and build SSA
   537  			prog := ssa.NewProgram(lprog.Fset, ssa.SanityCheckFunctions)
   538  			for _, info := range lprog.AllPackages {
   539  				if info.TransitivelyErrorFree {
   540  					prog.CreatePackage(info.Pkg, info.Files, &info.Info, info.Importable)
   541  				}
   542  			}
   543  			p := prog.Package(lprog.Package(pkgname).Pkg)
   544  			p.Build()
   545  
   546  			// Collect all notes in f, i.e. comments starting with "//@ types".
   547  			notes, err := expect.ExtractGo(prog.Fset, f)
   548  			if err != nil {
   549  				t.Errorf("expect.ExtractGo: %v", err)
   550  			}
   551  
   552  			// Collect calls to the builtin print function.
   553  			probes := callsTo(p, "print")
   554  			expectations := matchNotes(prog.Fset, notes, probes)
   555  
   556  			for call := range probes {
   557  				if expectations[call] == nil {
   558  					t.Errorf("Unmatched call: %v", call)
   559  				}
   560  			}
   561  
   562  			// Check each expectation.
   563  			for call, note := range expectations {
   564  				var args []string
   565  				for _, a := range call.Args {
   566  					args = append(args, a.Type().String())
   567  				}
   568  				if got, want := fmt.Sprint(args), fmt.Sprint(note.Args); got != want {
   569  					t.Errorf("Arguments to print() were expected to be %q. got %q", want, got)
   570  					logFunction(t, probes[call])
   571  				}
   572  			}
   573  		})
   574  	}
   575  }
   576  
   577  // callsTo finds all calls to an SSA value named fname,
   578  // and returns a map from each call site to its enclosing function.
   579  func callsTo(p *ssa.Package, fname string) map[*ssa.CallCommon]*ssa.Function {
   580  	callsites := make(map[*ssa.CallCommon]*ssa.Function)
   581  	for _, mem := range p.Members {
   582  		if fn, ok := mem.(*ssa.Function); ok {
   583  			for _, bb := range fn.Blocks {
   584  				for _, i := range bb.Instrs {
   585  					if i, ok := i.(ssa.CallInstruction); ok {
   586  						call := i.Common()
   587  						if call.Value.Name() == fname {
   588  							callsites[call] = fn
   589  						}
   590  					}
   591  				}
   592  			}
   593  		}
   594  	}
   595  	return callsites
   596  }
   597  
   598  // matchNodes returns a mapping from call sites (found by callsTo)
   599  // to the first "//@ note" comment on the same line.
   600  func matchNotes(fset *token.FileSet, notes []*expect.Note, calls map[*ssa.CallCommon]*ssa.Function) map[*ssa.CallCommon]*expect.Note {
   601  	// Matches each probe with a note that has the same line.
   602  	sameLine := func(x, y token.Pos) bool {
   603  		xp := fset.Position(x)
   604  		yp := fset.Position(y)
   605  		return xp.Filename == yp.Filename && xp.Line == yp.Line
   606  	}
   607  	expectations := make(map[*ssa.CallCommon]*expect.Note)
   608  	for call := range calls {
   609  		for _, note := range notes {
   610  			if sameLine(call.Pos(), note.Pos) {
   611  				expectations[call] = note
   612  				break // first match is good enough.
   613  			}
   614  		}
   615  	}
   616  	return expectations
   617  }
   618  
   619  // TestInstructionString tests serializing instructions via Instruction.String().
   620  func TestInstructionString(t *testing.T) {
   621  	// Tests (ssa.Instruction).String(). Instructions are from a single go file.
   622  	// The Instructions tested are those that match a comment of the form:
   623  	//
   624  	//	//@ instrs(f, kind, strs...)
   625  	//
   626  	// where f is the name of the function, kind is the type of the instructions matched
   627  	// within the function, and tests that the String() value for all of the instructions
   628  	// matched of String() is strs (in some order).
   629  	// See x/tools/go/expect for details on the syntax.
   630  
   631  	const contents = `
   632  	package p
   633  
   634  	//@ instrs("f0", "*ssa.TypeAssert")
   635  	//@ instrs("f0", "*ssa.Call", "print(nil:interface{}, 0:int)")
   636  	func f0(x int) { // non-generic smoke test.
   637  		var i interface{}
   638  		print(i, 0)
   639  	}
   640  
   641  	//@ instrs("f1", "*ssa.Alloc", "local T (u)")
   642  	//@ instrs("f1", "*ssa.FieldAddr", "&t0.x [#0]")
   643  	func f1[T ~struct{ x string }]() T {
   644  		u := T{"lorem"}
   645  		return u
   646  	}
   647  
   648  	//@ instrs("f1b", "*ssa.Alloc", "new T (complit)")
   649  	//@ instrs("f1b", "*ssa.FieldAddr", "&t0.x [#0]")
   650  	func f1b[T ~struct{ x string }]() *T {
   651  		u := &T{"lorem"}
   652  		return u
   653  	}
   654  
   655  	//@ instrs("f2", "*ssa.TypeAssert", "typeassert t0.(interface{})")
   656  	//@ instrs("f2", "*ssa.Call", "invoke x.foo()")
   657  	func f2[T interface{ foo() string }](x T) {
   658  		_ = x.foo
   659  		_ = x.foo()
   660  	}
   661  
   662  	//@ instrs("f3", "*ssa.TypeAssert", "typeassert t0.(interface{})")
   663  	//@ instrs("f3", "*ssa.Call", "invoke x.foo()")
   664  	func f3[T interface{ foo() string; comparable }](x T) {
   665  		_ = x.foo
   666  		_ = x.foo()
   667  	}
   668  
   669  	//@ instrs("f4", "*ssa.BinOp", "t1 + 1:int", "t2 < 4:int")
   670  	//@ instrs("f4", "*ssa.Call", "f()", "print(t2, t4)")
   671  	func f4[T [4]string](f func() T) {
   672  		for i, v := range f() {
   673  			print(i, v)
   674  		}
   675  	}
   676  
   677  	//@ instrs("f5", "*ssa.Call", "nil:func()()")
   678  	func f5() {
   679  		var f func()
   680  		f()
   681  	}
   682  
   683  	type S struct{ f int }
   684  
   685  	//@ instrs("f6", "*ssa.Alloc", "new [1]P (slicelit)", "new S (complit)")
   686  	//@ instrs("f6", "*ssa.IndexAddr", "&t0[0:int]")
   687  	//@ instrs("f6", "*ssa.FieldAddr", "&t2.f [#0]")
   688  	func f6[P *S]() []P { return []P{{f: 1}} }
   689  
   690  	//@ instrs("f7", "*ssa.Alloc", "local S (complit)")
   691  	//@ instrs("f7", "*ssa.FieldAddr", "&t0.f [#0]")
   692  	func f7[T any, S struct{f T}](x T) S { return S{f: x} }
   693  
   694  	//@ instrs("f8", "*ssa.Alloc", "new [1]P (slicelit)", "new struct{f T} (complit)")
   695  	//@ instrs("f8", "*ssa.IndexAddr", "&t0[0:int]")
   696  	//@ instrs("f8", "*ssa.FieldAddr", "&t2.f [#0]")
   697  	func f8[T any, P *struct{f T}](x T) []P { return []P{{f: x}} }
   698  
   699  	//@ instrs("f9", "*ssa.Alloc", "new [1]PS (slicelit)", "new S (complit)")
   700  	//@ instrs("f9", "*ssa.IndexAddr", "&t0[0:int]")
   701  	//@ instrs("f9", "*ssa.FieldAddr", "&t2.f [#0]")
   702  	func f9[T any, S struct{f T}, PS *S](x T) {
   703  		_ = []PS{{f: x}}
   704  	}
   705  
   706  	//@ instrs("f10", "*ssa.FieldAddr", "&t0.x [#0]")
   707  	//@ instrs("f10", "*ssa.Store", "*t0 = *new(T):T", "*t1 = 4:int")
   708  	func f10[T ~struct{ x, y int }]() T {
   709  		var u T
   710  		u = T{x: 4}
   711  		return u
   712  	}
   713  
   714  	//@ instrs("f11", "*ssa.FieldAddr", "&t1.y [#1]")
   715  	//@ instrs("f11", "*ssa.Store", "*t1 = *new(T):T", "*t2 = 5:int")
   716  	func f11[T ~struct{ x, y int }, PT *T]() PT {
   717  		var u PT = new(T)
   718  		*u = T{y: 5}
   719  		return u
   720  	}
   721  
   722  	//@ instrs("f12", "*ssa.Alloc", "new struct{f T} (complit)")
   723  	//@ instrs("f12", "*ssa.MakeMap", "make map[P]bool 1:int")
   724  	func f12[T any, P *struct{f T}](x T) map[P]bool { return map[P]bool{{}: true} }
   725  
   726  	//@ instrs("f13", "*ssa.IndexAddr", "&v[0:int]")
   727  	//@ instrs("f13", "*ssa.Store", "*t0 = 7:int", "*v = *new(A):A")
   728  	func f13[A [3]int, PA *A](v PA) {
   729  		*v = A{7}
   730  	}
   731  
   732  	//@ instrs("f14", "*ssa.Call", "invoke t1.Set(0:int)")
   733  	func f14[T any, PT interface {
   734  		Set(int)
   735  		*T
   736  	}]() {
   737  		var t T
   738  		p := PT(&t)
   739  		p.Set(0)
   740  	}
   741  
   742  	//@ instrs("f15", "*ssa.MakeClosure", "make closure (interface{Set(int); *T}).Set$bound [t1]")
   743  	func f15[T any, PT interface {
   744  		Set(int)
   745  		*T
   746  	}]() func(int) {
   747  		var t T
   748  		p := PT(&t)
   749  		return p.Set
   750  	}
   751  	`
   752  
   753  	// Parse
   754  	conf := loader.Config{ParserMode: parser.ParseComments}
   755  	const fname = "p.go"
   756  	f, err := conf.ParseFile(fname, contents)
   757  	if err != nil {
   758  		t.Fatalf("parse: %v", err)
   759  	}
   760  	conf.CreateFromFiles("p", f)
   761  
   762  	// Load
   763  	lprog, err := conf.Load()
   764  	if err != nil {
   765  		t.Fatalf("Load: %v", err)
   766  	}
   767  
   768  	// Create and build SSA
   769  	prog := ssa.NewProgram(lprog.Fset, ssa.SanityCheckFunctions)
   770  	for _, info := range lprog.AllPackages {
   771  		if info.TransitivelyErrorFree {
   772  			prog.CreatePackage(info.Pkg, info.Files, &info.Info, info.Importable)
   773  		}
   774  	}
   775  	p := prog.Package(lprog.Package("p").Pkg)
   776  	p.Build()
   777  
   778  	// Collect all notes in f, i.e. comments starting with "//@ instr".
   779  	notes, err := expect.ExtractGo(prog.Fset, f)
   780  	if err != nil {
   781  		t.Errorf("expect.ExtractGo: %v", err)
   782  	}
   783  
   784  	// Expectation is a {function, type string} -> {want, matches}
   785  	// where matches is all Instructions.String() that match the key.
   786  	// Each expecation is that some permutation of matches is wants.
   787  	type expKey struct {
   788  		function string
   789  		kind     string
   790  	}
   791  	type expValue struct {
   792  		wants   []string
   793  		matches []string
   794  	}
   795  	expectations := make(map[expKey]*expValue)
   796  	for _, note := range notes {
   797  		if note.Name == "instrs" {
   798  			if len(note.Args) < 2 {
   799  				t.Error("Had @instrs annotation without at least 2 arguments")
   800  				continue
   801  			}
   802  			fn, kind := fmt.Sprint(note.Args[0]), fmt.Sprint(note.Args[1])
   803  			var wants []string
   804  			for _, arg := range note.Args[2:] {
   805  				wants = append(wants, fmt.Sprint(arg))
   806  			}
   807  			expectations[expKey{fn, kind}] = &expValue{wants, nil}
   808  		}
   809  	}
   810  
   811  	// Collect all Instructions that match the expectations.
   812  	for _, mem := range p.Members {
   813  		if fn, ok := mem.(*ssa.Function); ok {
   814  			for _, bb := range fn.Blocks {
   815  				for _, i := range bb.Instrs {
   816  					kind := fmt.Sprintf("%T", i)
   817  					if e := expectations[expKey{fn.Name(), kind}]; e != nil {
   818  						e.matches = append(e.matches, i.String())
   819  					}
   820  				}
   821  			}
   822  		}
   823  	}
   824  
   825  	// Check each expectation.
   826  	for key, value := range expectations {
   827  		fn, ok := p.Members[key.function].(*ssa.Function)
   828  		if !ok {
   829  			t.Errorf("Expectation on %s does not match a member in %s", key.function, p.Pkg.Name())
   830  		}
   831  		got, want := value.matches, value.wants
   832  		sort.Strings(got)
   833  		sort.Strings(want)
   834  		if !reflect.DeepEqual(want, got) {
   835  			t.Errorf("Within %s wanted instructions of kind %s: %q. got %q", key.function, key.kind, want, got)
   836  			logFunction(t, fn)
   837  		}
   838  	}
   839  }
   840  
   841  // packageName is a test helper to extract the package name from a string
   842  // containing the content of a go file.
   843  func packageName(t testing.TB, content string) string {
   844  	f, err := parser.ParseFile(token.NewFileSet(), "", content, parser.PackageClauseOnly)
   845  	if err != nil {
   846  		t.Fatalf("parsing the file %q failed with error: %s", content, err)
   847  	}
   848  	return f.Name.Name
   849  }
   850  
   851  func logFunction(t testing.TB, fn *ssa.Function) {
   852  	// TODO: Consider adding a ssa.Function.GoString() so this can be logged to t via '%#v'.
   853  	var buf bytes.Buffer
   854  	ssa.WriteFunction(&buf, fn)
   855  	t.Log(buf.String())
   856  }
   857  

View as plain text