GoPLS Viewer

Home|gopls/go/ssa/instantiate_test.go
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
5package ssa
6
7// Note: Tests use unexported method _Instances.
8
9import (
10    "bytes"
11    "fmt"
12    "go/types"
13    "reflect"
14    "sort"
15    "strings"
16    "testing"
17
18    "golang.org/x/tools/go/loader"
19    "golang.org/x/tools/internal/typeparams"
20)
21
22// loadProgram creates loader.Program out of p.
23func loadProgram(p string) (*loader.Programerror) {
24    // Parse
25    var conf loader.Config
26    ferr := conf.ParseFile("<input>"p)
27    if err != nil {
28        return nilfmt.Errorf("parse: %v"err)
29    }
30    conf.CreateFromFiles("p"f)
31
32    // Load
33    lprogerr := conf.Load()
34    if err != nil {
35        return nilfmt.Errorf("Load: %v"err)
36    }
37    return lprognil
38}
39
40// buildPackage builds and returns ssa representation of package pkg of lprog.
41func buildPackage(lprog *loader.Programpkg stringmode BuilderMode) *Package {
42    prog := NewProgram(lprog.Fsetmode)
43
44    for _info := range lprog.AllPackages {
45        prog.CreatePackage(info.Pkginfo.Files, &info.Infoinfo.Importable)
46    }
47
48    p := prog.Package(lprog.Package(pkg).Pkg)
49    p.Build()
50    return p
51}
52
53// TestNeedsInstance ensures that new method instances can be created via needsInstance,
54// that TypeArgs are as expected, and can be accessed via _Instances.
55func TestNeedsInstance(t *testing.T) {
56    if !typeparams.Enabled {
57        return
58    }
59    const input = `
60package p
61
62import "unsafe"
63
64type Pointer[T any] struct {
65    v unsafe.Pointer
66}
67
68func (x *Pointer[T]) Load() *T {
69    return (*T)(LoadPointer(&x.v))
70}
71
72func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer)
73`
74    // The SSA members for this package should look something like this:
75    //      func  LoadPointer func(addr *unsafe.Pointer) (val unsafe.Pointer)
76    //      type  Pointer     struct{v unsafe.Pointer}
77    //        method (*Pointer[T any]) Load() *T
78    //      func  init        func()
79    //      var   init$guard  bool
80
81    lprogerr := loadProgram(input)
82    if err != err {
83        t.Fatal(err)
84    }
85
86    for _mode := range []BuilderMode{BuilderMode(0), InstantiateGenerics} {
87        // Create and build SSA
88        p := buildPackage(lprog"p"mode)
89        prog := p.Prog
90
91        ptr := p.Type("Pointer").Type().(*types.Named)
92        if ptr.NumMethods() != 1 {
93            t.Fatalf("Expected Pointer to have 1 method. got %d"ptr.NumMethods())
94        }
95
96        obj := ptr.Method(0)
97        if obj.Name() != "Load" {
98            t.Errorf("Expected Pointer to have method named 'Load'. got %q"obj.Name())
99        }
100
101        meth := prog.FuncValue(obj)
102
103        var cr creator
104        intSliceTyp := types.NewSlice(types.Typ[types.Int])
105        instance := prog.needsInstance(meth, []types.Type{intSliceTyp}, &cr)
106        if len(cr) != 1 {
107            t.Errorf("Expected first instance to create a function. got %d created functions"len(cr))
108        }
109        if instance.Origin() != meth {
110            t.Errorf("Expected Origin of %s to be %s. got %s"instancemethinstance.Origin())
111        }
112        if len(instance.TypeArgs()) != 1 || !types.Identical(instance.TypeArgs()[0], intSliceTyp) {
113            t.Errorf("Expected TypeArgs of %s to be %v. got %v"instance, []types.Type{intSliceTyp}, instance.typeargs)
114        }
115        instances := prog._Instances(meth)
116        if want := []*Function{instance}; !reflect.DeepEqual(instanceswant) {
117            t.Errorf("Expected instances of %s to be %v. got %v"methwantinstances)
118        }
119
120        // A second request with an identical type returns the same Function.
121        second := prog.needsInstance(meth, []types.Type{types.NewSlice(types.Typ[types.Int])}, &cr)
122        if second != instance || len(cr) != 1 {
123            t.Error("Expected second identical instantiation to not create a function")
124        }
125
126        // Add a second instance.
127        inst2 := prog.needsInstance(meth, []types.Type{types.NewSlice(types.Typ[types.Uint])}, &cr)
128        instances = prog._Instances(meth)
129
130        // Note: instance.Name() < inst2.Name()
131        sort.Slice(instances, func(ij intbool {
132            return instances[i].Name() < instances[j].Name()
133        })
134        if want := []*Function{instanceinst2}; !reflect.DeepEqual(instanceswant) {
135            t.Errorf("Expected instances of %s to be %v. got %v"methwantinstances)
136        }
137
138        // build and sanity check manually created instance.
139        var b builder
140        b.buildFunction(instance)
141        var buf bytes.Buffer
142        if !sanityCheck(instance, &buf) {
143            t.Errorf("sanityCheck of %s failed with: %s"instancebuf.String())
144        }
145    }
146}
147
148// TestCallsToInstances checks that calles of calls to generic functions,
149// without monomorphization, are wrappers around the origin generic function.
150func TestCallsToInstances(t *testing.T) {
151    if !typeparams.Enabled {
152        return
153    }
154    const input = `
155package p
156
157type I interface {
158    Foo()
159}
160
161type A int
162func (a A) Foo() {}
163
164type J[T any] interface{ Bar() T }
165type K[T any] struct{ J[T] }
166
167func Id[T any] (t T) T {
168    return t
169}
170
171func Lambda[T I]() func() func(T) {
172    return func() func(T) {
173        return T.Foo
174    }
175}
176
177func NoOp[T any]() {}
178
179func Bar[T interface { Foo(); ~int | ~string }, U any] (t T, u U) {
180    Id[U](u)
181    Id[T](t)
182}
183
184func Make[T any]() interface{} {
185    NoOp[K[T]]()
186    return nil
187}
188
189func entry(i int, a A) int {
190    Lambda[A]()()(a)
191
192    x := Make[int]()
193    if j, ok := x.(interface{ Bar() int }); ok {
194        print(j)
195    }
196
197    Bar[A, int](a, i)
198
199    return Id[int](i)
200}
201`
202    lprogerr := loadProgram(input)
203    if err != err {
204        t.Fatal(err)
205    }
206
207    p := buildPackage(lprog"p"SanityCheckFunctions)
208    prog := p.Prog
209
210    for _ti := range []struct {
211        orig         string
212        instance     string
213        tparams      string
214        targs        string
215        chTypeInstrs int // number of ChangeType instructions in f's body
216    }{
217        {"Id""Id[int]""[T]""[int]"2},
218        {"Lambda""Lambda[p.A]""[T]""[p.A]"1},
219        {"Make""Make[int]""[T]""[int]"0},
220        {"NoOp""NoOp[p.K[T]]""[T]""[p.K[T]]"0},
221    } {
222        test := ti
223        t.Run(test.instance, func(t *testing.T) {
224            f := p.Members[test.orig].(*Function)
225            if f == nil {
226                t.Fatalf("origin function not found")
227            }
228
229            i := instanceOf(ftest.instanceprog)
230            if i == nil {
231                t.Fatalf("instance not found")
232            }
233
234            // for logging on failures
235            var body strings.Builder
236            i.WriteTo(&body)
237            t.Log(body.String())
238
239            if len(i.Blocks) != 1 {
240                t.Fatalf("body has more than 1 block")
241            }
242
243            if instrs := changeTypeInstrs(i.Blocks[0]); instrs != test.chTypeInstrs {
244                t.Errorf("want %v instructions; got %v"test.chTypeInstrsinstrs)
245            }
246
247            if test.tparams != tparams(i) {
248                t.Errorf("want %v type params; got %v"test.tparamstparams(i))
249            }
250
251            if test.targs != targs(i) {
252                t.Errorf("want %v type arguments; got %v"test.targstargs(i))
253            }
254        })
255    }
256}
257
258func instanceOf(f *Functionname stringprog *Program) *Function {
259    for _i := range prog._Instances(f) {
260        if i.Name() == name {
261            return i
262        }
263    }
264    return nil
265}
266
267func tparams(f *Functionstring {
268    tplist := f.TypeParams()
269    var tps []string
270    for i := 0i < tplist.Len(); i++ {
271        tps = append(tpstplist.At(i).String())
272    }
273    return fmt.Sprint(tps)
274}
275
276func targs(f *Functionstring {
277    var tas []string
278    for _ta := range f.TypeArgs() {
279        tas = append(tasta.String())
280    }
281    return fmt.Sprint(tas)
282}
283
284func changeTypeInstrs(b *BasicBlockint {
285    cnt := 0
286    for _i := range b.Instrs {
287        if _ok := i.(*ChangeType); ok {
288            cnt++
289        }
290    }
291    return cnt
292}
293
294func TestInstanceUniqueness(t *testing.T) {
295    if !typeparams.Enabled {
296        return
297    }
298    const input = `
299package p
300
301func H[T any](t T) {
302    print(t)
303}
304
305func F[T any](t T) {
306    H[T](t)
307    H[T](t)
308    H[T](t)
309}
310
311func G[T any](t T) {
312    H[T](t)
313    H[T](t)
314}
315
316func Foo[T any, S any](t T, s S) {
317    Foo[S, T](s, t)
318    Foo[T, S](t, s)
319}
320`
321    lprogerr := loadProgram(input)
322    if err != err {
323        t.Fatal(err)
324    }
325
326    p := buildPackage(lprog"p"SanityCheckFunctions)
327    prog := p.Prog
328
329    for _test := range []struct {
330        orig      string
331        instances string
332    }{
333        {"H""[p.H[T] p.H[T]]"},
334        {"Foo""[p.Foo[S T] p.Foo[T S]]"},
335    } {
336        t.Run(test.orig, func(t *testing.T) {
337            f := p.Members[test.orig].(*Function)
338            if f == nil {
339                t.Fatalf("origin function not found")
340            }
341
342            instances := prog._Instances(f)
343            sort.Slice(instances, func(ij intbool { return instances[i].Name() < instances[j].Name() })
344
345            if got := fmt.Sprintf("%v"instances); !reflect.DeepEqual(gottest.instances) {
346                t.Errorf("got %v instances, want %v"gottest.instances)
347            }
348        })
349    }
350}
351
352// instancesStr returns a sorted slice of string
353// representation of instances.
354func instancesStr(instances []*Function) []string {
355    var is []string
356    for _i := range instances {
357        is = append(isfmt.Sprintf("%v"i))
358    }
359    sort.Strings(is)
360    return is
361}
362
MembersX
instancesStr.is
loadProgram
TestNeedsInstance.RangeStmt_1997.BlockStmt.second
instanceOf
instanceOf.prog
instancesStr
loadProgram.p
buildPackage.prog
TestNeedsInstance.RangeStmt_1997.BlockStmt.buf
TestCallsToInstances.RangeStmt_5267.BlockStmt.BlockStmt.instrs
TestInstanceUniqueness
tparams
changeTypeInstrs.cnt
buildPackage.pkg
TestCallsToInstances.t
TestInstanceUniqueness.t
buildPackage.RangeStmt_971.info
TestCallsToInstances
TestCallsToInstances.p
TestCallsToInstances.prog
instanceOf.RangeStmt_6539.i
buildPackage.mode
buildPackage.p
TestCallsToInstances.input
targs.f
targs
TestInstanceUniqueness.err
loadProgram.lprog
TestNeedsInstance.RangeStmt_1997.BlockStmt.p
TestCallsToInstances.err
instanceOf.name
tparams.tplist
TestInstanceUniqueness.RangeStmt_7575.BlockStmt.BlockStmt.instances
TestInstanceUniqueness.RangeStmt_7575.BlockStmt.BlockStmt.got
loadProgram.conf
TestNeedsInstance.RangeStmt_1997.mode
TestNeedsInstance.RangeStmt_1997.BlockStmt.instance
tparams.i
changeTypeInstrs
TestInstanceUniqueness.p
TestInstanceUniqueness.RangeStmt_7575.test
instancesStr.RangeStmt_8316.i
TestNeedsInstance.t
TestNeedsInstance.err
TestCallsToInstances.lprog
TestCallsToInstances.RangeStmt_5267.ti
changeTypeInstrs.b
TestNeedsInstance.lprog
TestCallsToInstances.RangeStmt_5267.BlockStmt.test
loadProgram.f
loadProgram.err
buildPackage
buildPackage.lprog
TestNeedsInstance.input
TestNeedsInstance.RangeStmt_1997.BlockStmt.cr
TestNeedsInstance.RangeStmt_1997.BlockStmt.want
TestCallsToInstances.RangeStmt_5267.BlockStmt.BlockStmt.body
TestInstanceUniqueness.prog
TestNeedsInstance.RangeStmt_1997.BlockStmt.obj
TestNeedsInstance.RangeStmt_1997.BlockStmt.instances
TestCallsToInstances.RangeStmt_5267.BlockStmt.BlockStmt.i
targs.tas
TestInstanceUniqueness.lprog
TestNeedsInstance.RangeStmt_1997.BlockStmt.b
tparams.f
instancesStr.instances
TestNeedsInstance
TestNeedsInstance.RangeStmt_1997.BlockStmt.intSliceTyp
instanceOf.f
TestInstanceUniqueness.input
changeTypeInstrs.RangeStmt_7028.i
TestNeedsInstance.RangeStmt_1997.BlockStmt.prog
TestNeedsInstance.RangeStmt_1997.BlockStmt.meth
TestNeedsInstance.RangeStmt_1997.BlockStmt.inst2
tparams.tps
targs.RangeStmt_6877.ta
Members
X