2
2
// Use of this source code is governed by a BSD-style
3
3
// license that can be found in the LICENSE file.
4
4
5
- package ssa
6
-
7
- // Note: Tests use unexported method _Instances.
5
+ package ssa_test
8
6
9
7
import (
10
- "bytes"
11
8
"fmt"
12
9
"go/types"
13
10
"reflect"
@@ -16,6 +13,8 @@ import (
16
13
"testing"
17
14
18
15
"golang.org/x/tools/go/loader"
16
+ "golang.org/x/tools/go/ssa"
17
+ "golang.org/x/tools/go/ssa/ssautil"
19
18
)
20
19
21
20
// loadProgram creates loader.Program out of p.
@@ -37,8 +36,8 @@ func loadProgram(p string) (*loader.Program, error) {
37
36
}
38
37
39
38
// buildPackage builds and returns ssa representation of package pkg of lprog.
40
- func buildPackage (lprog * loader.Program , pkg string , mode BuilderMode ) * Package {
41
- prog := NewProgram (lprog .Fset , mode )
39
+ func buildPackage (lprog * loader.Program , pkg string , mode ssa. BuilderMode ) * ssa. Package {
40
+ prog := ssa . NewProgram (lprog .Fset , mode )
42
41
43
42
for _ , info := range lprog .AllPackages {
44
43
prog .CreatePackage (info .Pkg , info .Files , & info .Info , info .Importable )
@@ -49,8 +48,7 @@ func buildPackage(lprog *loader.Program, pkg string, mode BuilderMode) *Package
49
48
return p
50
49
}
51
50
52
- // TestNeedsInstance ensures that new method instances can be created via needsInstance,
53
- // that TypeArgs are as expected, and can be accessed via _Instances.
51
+ // TestNeedsInstance ensures that new method instances can be created via MethodValue.
54
52
func TestNeedsInstance (t * testing.T ) {
55
53
const input = `
56
54
package p
@@ -79,7 +77,10 @@ func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer)
79
77
t .Fatal (err )
80
78
}
81
79
82
- for _ , mode := range []BuilderMode {BuilderMode (0 ), InstantiateGenerics } {
80
+ for _ , mode := range []ssa.BuilderMode {
81
+ ssa .SanityCheckFunctions ,
82
+ ssa .SanityCheckFunctions | ssa .InstantiateGenerics ,
83
+ } {
83
84
// Create and build SSA
84
85
p := buildPackage (lprog , "p" , mode )
85
86
prog := p .Prog
@@ -96,48 +97,39 @@ func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer)
96
97
97
98
meth := prog .FuncValue (obj )
98
99
99
- b := & builder {}
100
- intSliceTyp := types .NewSlice (types .Typ [types .Int ])
101
- instance := meth .instance ([]types.Type {intSliceTyp }, b )
102
- if len (b .fns ) != 1 {
103
- t .Errorf ("Expected first instance to create a function. got %d created functions" , len (b .fns ))
100
+ // instantiateLoadMethod returns the first method (Load) of the instantiation *Pointer[T].
101
+ instantiateLoadMethod := func (T types.Type ) * ssa.Function {
102
+ ptrT , err := types .Instantiate (nil , ptr , []types.Type {T }, false )
103
+ if err != nil {
104
+ t .Fatalf ("Failed to Instantiate %q by %q" , ptr , T )
105
+ }
106
+ methods := types .NewMethodSet (types .NewPointer (ptrT ))
107
+ if methods .Len () != 1 {
108
+ t .Fatalf ("Expected 1 method for %q. got %d" , ptrT , methods .Len ())
109
+ }
110
+ return prog .MethodValue (methods .At (0 ))
104
111
}
112
+
113
+ intSliceTyp := types .NewSlice (types .Typ [types .Int ])
114
+ instance := instantiateLoadMethod (intSliceTyp ) // (*Pointer[[]int]).Load
105
115
if instance .Origin () != meth {
106
116
t .Errorf ("Expected Origin of %s to be %s. got %s" , instance , meth , instance .Origin ())
107
117
}
108
118
if len (instance .TypeArgs ()) != 1 || ! types .Identical (instance .TypeArgs ()[0 ], intSliceTyp ) {
109
- t .Errorf ("Expected TypeArgs of %s to be %v. got %v" , instance , []types.Type {intSliceTyp }, instance .typeargs )
110
- }
111
- instances := allInstances (meth )
112
- if want := []* Function {instance }; ! reflect .DeepEqual (instances , want ) {
113
- t .Errorf ("Expected instances of %s to be %v. got %v" , meth , want , instances )
119
+ t .Errorf ("Expected TypeArgs of %s to be %v. got %v" , instance , []types.Type {intSliceTyp }, instance .TypeArgs ())
114
120
}
115
121
116
122
// A second request with an identical type returns the same Function.
117
- second := meth .instance ([]types.Type {types .NewSlice (types .Typ [types .Int ])}, b )
118
- if second != instance || len (b .fns ) != 1 {
119
- t .Error ("Expected second identical instantiation to not create a function" )
120
- }
121
-
122
- // Add a second instance.
123
- inst2 := meth .instance ([]types.Type {types .NewSlice (types .Typ [types .Uint ])}, b )
124
- instances = allInstances (meth )
125
-
126
- // Note: instance.Name() < inst2.Name()
127
- sort .Slice (instances , func (i , j int ) bool {
128
- return instances [i ].Name () < instances [j ].Name ()
129
- })
130
- if want := []* Function {instance , inst2 }; ! reflect .DeepEqual (instances , want ) {
131
- t .Errorf ("Expected instances of %s to be %v. got %v" , meth , want , instances )
123
+ second := instantiateLoadMethod (types .NewSlice (types .Typ [types .Int ]))
124
+ if second != instance {
125
+ t .Error ("Expected second identical instantiation to be the same function" )
132
126
}
133
127
134
- // TODO(adonovan): tests should not rely on unexported functions.
128
+ // (*Pointer[[]uint]).Load
129
+ inst2 := instantiateLoadMethod (types .NewSlice (types .Typ [types .Uint ]))
135
130
136
- // build and sanity check manually created instance.
137
- b .buildFunction (instance )
138
- var buf bytes.Buffer
139
- if ! sanityCheck (instance , & buf ) {
140
- t .Errorf ("sanityCheck of %s failed with: %s" , instance , buf .String ())
131
+ if instance .Name () >= inst2 .Name () {
132
+ t .Errorf ("Expected name of instance %s to be before instance %v" , instance , inst2 )
141
133
}
142
134
}
143
135
}
@@ -198,8 +190,8 @@ func entry(i int, a A) int {
198
190
t .Fatal (err )
199
191
}
200
192
201
- p := buildPackage (lprog , "p" , SanityCheckFunctions )
202
- prog := p .Prog
193
+ p := buildPackage (lprog , "p" , ssa . SanityCheckFunctions )
194
+ all := ssautil . AllFunctions ( p .Prog )
203
195
204
196
for _ , ti := range []struct {
205
197
orig string
@@ -215,12 +207,18 @@ func entry(i int, a A) int {
215
207
} {
216
208
test := ti
217
209
t .Run (test .instance , func (t * testing.T ) {
218
- f := p .Members [test .orig ].(* Function )
210
+ f := p .Members [test .orig ].(* ssa. Function )
219
211
if f == nil {
220
212
t .Fatalf ("origin function not found" )
221
213
}
222
214
223
- i := instanceOf (f , test .instance , prog )
215
+ var i * ssa.Function
216
+ for _ , fn := range instancesOf (all , f ) {
217
+ if fn .Name () == test .instance {
218
+ i = fn
219
+ break
220
+ }
221
+ }
224
222
if i == nil {
225
223
t .Fatalf ("instance not found" )
226
224
}
@@ -249,16 +247,7 @@ func entry(i int, a A) int {
249
247
}
250
248
}
251
249
252
- func instanceOf (f * Function , name string , prog * Program ) * Function {
253
- for _ , i := range allInstances (f ) {
254
- if i .Name () == name {
255
- return i
256
- }
257
- }
258
- return nil
259
- }
260
-
261
- func tparams (f * Function ) string {
250
+ func tparams (f * ssa.Function ) string {
262
251
tplist := f .TypeParams ()
263
252
var tps []string
264
253
for i := 0 ; i < tplist .Len (); i ++ {
@@ -267,18 +256,18 @@ func tparams(f *Function) string {
267
256
return fmt .Sprint (tps )
268
257
}
269
258
270
- func targs (f * Function ) string {
259
+ func targs (f * ssa. Function ) string {
271
260
var tas []string
272
261
for _ , ta := range f .TypeArgs () {
273
262
tas = append (tas , ta .String ())
274
263
}
275
264
return fmt .Sprint (tas )
276
265
}
277
266
278
- func changeTypeInstrs (b * BasicBlock ) int {
267
+ func changeTypeInstrs (b * ssa. BasicBlock ) int {
279
268
cnt := 0
280
269
for _ , i := range b .Instrs {
281
- if _ , ok := i .(* ChangeType ); ok {
270
+ if _ , ok := i .(* ssa. ChangeType ); ok {
282
271
cnt ++
283
272
}
284
273
}
@@ -314,8 +303,9 @@ func Foo[T any, S any](t T, s S) {
314
303
t .Fatal (err )
315
304
}
316
305
317
- p := buildPackage (lprog , "p" , SanityCheckFunctions )
306
+ p := buildPackage (lprog , "p" , ssa . SanityCheckFunctions )
318
307
308
+ all := ssautil .AllFunctions (p .Prog )
319
309
for _ , test := range []struct {
320
310
orig string
321
311
instances string
@@ -324,12 +314,12 @@ func Foo[T any, S any](t T, s S) {
324
314
{"Foo" , "[p.Foo[S T] p.Foo[T S]]" },
325
315
} {
326
316
t .Run (test .orig , func (t * testing.T ) {
327
- f := p .Members [test .orig ].(* Function )
317
+ f := p .Members [test .orig ].(* ssa. Function )
328
318
if f == nil {
329
319
t .Fatalf ("origin function not found" )
330
320
}
331
321
332
- instances := allInstances ( f )
322
+ instances := instancesOf ( all , f )
333
323
sort .Slice (instances , func (i , j int ) bool { return instances [i ].Name () < instances [j ].Name () })
334
324
335
325
if got := fmt .Sprintf ("%v" , instances ); ! reflect .DeepEqual (got , test .instances ) {
@@ -339,22 +329,14 @@ func Foo[T any, S any](t T, s S) {
339
329
}
340
330
}
341
331
342
- // allInstances returns a new unordered array of all instances of the
343
- // specified function, if generic, or nil otherwise.
344
- //
345
- // Thread-safe.
346
- //
347
- // TODO(adonovan): delete this. The tests should be intensional (e.g.
348
- // "what instances of f are reachable?") not representational (e.g.
349
- // "what is the history of calls to Function.instance?").
350
- //
351
- // Acquires fn.generic.instancesMu.
352
- func allInstances (fn * Function ) []* Function {
353
- if fn .generic == nil {
354
- return nil
332
+ // instancesOf returns a new unordered slice of all instances of the
333
+ // specified function g in fns.
334
+ func instancesOf (fns map [* ssa.Function ]bool , g * ssa.Function ) []* ssa.Function {
335
+ var instances []* ssa.Function
336
+ for fn := range fns {
337
+ if fn != g && fn .Origin () == g {
338
+ instances = append (instances , fn )
339
+ }
355
340
}
356
-
357
- fn .generic .instancesMu .Lock ()
358
- defer fn .generic .instancesMu .Unlock ()
359
- return mapValues (fn .generic .instances )
341
+ return instances
360
342
}
0 commit comments