@@ -10,10 +10,14 @@ package typeutil_test
10
10
// (e.g. all types generated by type-checking some body of real code).
11
11
12
12
import (
13
+ "go/ast"
14
+ "go/parser"
15
+ "go/token"
13
16
"go/types"
14
17
"testing"
15
18
16
19
"golang.org/x/tools/go/types/typeutil"
20
+ "golang.org/x/tools/internal/typeparams"
17
21
)
18
22
19
23
var (
@@ -172,3 +176,190 @@ func TestMap(t *testing.T) {
172
176
t .Errorf ("Len(): got %q, want %q" , s , "" )
173
177
}
174
178
}
179
+
180
+ func TestMapGenerics (t * testing.T ) {
181
+ if ! typeparams .Enabled {
182
+ t .Skip ("type params are not enabled at this Go version" )
183
+ }
184
+
185
+ const src = `
186
+ package p
187
+
188
+ // Basic defined types.
189
+ type T1 int
190
+ type T2 int
191
+
192
+ // Identical methods.
193
+ func (T1) M(int) {}
194
+ func (T2) M(int) {}
195
+
196
+ // A constraint interface.
197
+ type C interface {
198
+ ~int | string
199
+ }
200
+
201
+ type I interface {
202
+ }
203
+
204
+ // A generic type.
205
+ type G[P C] int
206
+
207
+ // Generic functions with identical signature.
208
+ func Fa1[P C](p P) {}
209
+ func Fa2[Q C](q Q) {}
210
+
211
+ // Fb1 and Fb2 are identical and should be mapped to the same entry, even if we
212
+ // map their arguments first.
213
+ func Fb1[P any](x *P) {
214
+ var y *P // Map this first.
215
+ _ = y
216
+ }
217
+ func Fb2[Q any](x *Q) {
218
+ }
219
+
220
+ // G1 and G2 are mutally recursive, and have identical methods.
221
+ type G1[P any] struct{
222
+ Field *G2[P]
223
+ }
224
+ func (G1[P]) M(G1[P], G2[P]) {}
225
+ type G2[Q any] struct{
226
+ Field *G1[Q]
227
+ }
228
+ func (G2[P]) M(G1[P], G2[P]) {}
229
+
230
+ // Method type expressions on different generic types are different.
231
+ var ME1 = G1[int].M
232
+ var ME2 = G2[int].M
233
+
234
+ // ME1Type should have identical type as ME1.
235
+ var ME1Type func(G1[int], G1[int], G2[int])
236
+ `
237
+
238
+ fset := token .NewFileSet ()
239
+ file , err := parser .ParseFile (fset , "p.go" , src , 0 )
240
+ if err != nil {
241
+ t .Fatal (err )
242
+ }
243
+
244
+ var conf types.Config
245
+ pkg , err := conf .Check ("" , fset , []* ast.File {file }, nil )
246
+ if err != nil {
247
+ t .Fatal (err )
248
+ }
249
+
250
+ // Collect types.
251
+ scope := pkg .Scope ()
252
+ var (
253
+ T1 = scope .Lookup ("T1" ).Type ().(* types.Named )
254
+ T2 = scope .Lookup ("T2" ).Type ().(* types.Named )
255
+ T1M = T1 .Method (0 ).Type ()
256
+ T2M = T2 .Method (0 ).Type ()
257
+ G = scope .Lookup ("G" ).Type ()
258
+ GInt1 = instantiate (t , G , types .Typ [types .Int ])
259
+ GInt2 = instantiate (t , G , types .Typ [types .Int ])
260
+ GStr = instantiate (t , G , types .Typ [types .String ])
261
+ C = scope .Lookup ("C" ).Type ()
262
+ CI = C .Underlying ().(* types.Interface )
263
+ I = scope .Lookup ("I" ).Type ()
264
+ II = I .Underlying ().(* types.Interface )
265
+ U = CI .EmbeddedType (0 ).(* typeparams.Union )
266
+ Fa1 = scope .Lookup ("Fa1" ).Type ().(* types.Signature )
267
+ Fa2 = scope .Lookup ("Fa2" ).Type ().(* types.Signature )
268
+ Fa1P = typeparams .ForSignature (Fa1 ).At (0 )
269
+ Fa2Q = typeparams .ForSignature (Fa2 ).At (0 )
270
+ Fb1 = scope .Lookup ("Fb1" ).Type ().(* types.Signature )
271
+ Fb1x = Fb1 .Params ().At (0 ).Type ()
272
+ Fb1y = scope .Lookup ("Fb1" ).(* types.Func ).Scope ().Lookup ("y" ).Type ()
273
+ Fb2 = scope .Lookup ("Fb2" ).Type ().(* types.Signature )
274
+ Fb2x = Fb2 .Params ().At (0 ).Type ()
275
+ G1 = scope .Lookup ("G1" ).Type ().(* types.Named )
276
+ G1M = G1 .Method (0 ).Type ()
277
+ G1IntM1 = instantiate (t , G1 , types .Typ [types .Int ]).(* types.Named ).Method (0 ).Type ()
278
+ G1IntM2 = instantiate (t , G1 , types .Typ [types .Int ]).(* types.Named ).Method (0 ).Type ()
279
+ G1StrM = instantiate (t , G1 , types .Typ [types .String ]).(* types.Named ).Method (0 ).Type ()
280
+ G2 = scope .Lookup ("G2" ).Type ()
281
+ // See below.
282
+ // G2M = G2.Method(0).Type()
283
+ G2IntM = instantiate (t , G2 , types .Typ [types .Int ]).(* types.Named ).Method (0 ).Type ()
284
+ ME1 = scope .Lookup ("ME1" ).Type ()
285
+ ME1Type = scope .Lookup ("ME1Type" ).Type ()
286
+ ME2 = scope .Lookup ("ME2" ).Type ()
287
+ )
288
+
289
+ tmap := new (typeutil.Map )
290
+
291
+ steps := []struct {
292
+ typ types.Type
293
+ name string
294
+ newEntry bool
295
+ }{
296
+ {T1 , "T1" , true },
297
+ {T2 , "T2" , true },
298
+ {G , "G" , true },
299
+ {C , "C" , true },
300
+ {CI , "CI" , true },
301
+ {U , "U" , true },
302
+ {I , "I" , true },
303
+ {II , "II" , true }, // should not be identical to CI
304
+
305
+ // Methods can be identical, even with distinct receivers.
306
+ {T1M , "T1M" , true },
307
+ {T2M , "T2M" , false },
308
+
309
+ // Identical instances should map to the same entry.
310
+ {GInt1 , "GInt1" , true },
311
+ {GInt2 , "GInt2" , false },
312
+ // ..but instantiating with different arguments should yield a new entry.
313
+ {GStr , "GStr" , true },
314
+
315
+ // F1 and F2 should have identical signatures.
316
+ {Fa1 , "F1" , true },
317
+ {Fa2 , "F2" , false },
318
+
319
+ // The identity of P and Q should not have been affected by type parameter
320
+ // masking during signature hashing.
321
+ {Fa1P , "F1P" , true },
322
+ {Fa2Q , "F2Q" , true },
323
+
324
+ {Fb1y , "Fb1y" , true },
325
+ {Fb1x , "Fb1x" , false },
326
+ {Fb2x , "Fb2x" , true },
327
+ {Fb1 , "Fb1" , true },
328
+
329
+ // Mapping elements of the function scope should not affect the identity of
330
+ // Fb2 or Fb1.
331
+ {Fb2 , "Fb1" , false },
332
+
333
+ {G1 , "G1" , true },
334
+ {G1M , "G1M" , true },
335
+ {G2 , "G2" , true },
336
+
337
+ // See golang/go#49912: receiver type parameter names should be ignored
338
+ // when comparing method identity.
339
+ // {G2M, "G2M", false},
340
+ {G1IntM1 , "G1IntM1" , true },
341
+ {G1IntM2 , "G1IntM2" , false },
342
+ {G1StrM , "G1StrM" , true },
343
+ {G2IntM , "G2IntM" , false }, // identical to G1IntM1
344
+
345
+ {ME1 , "ME1" , true },
346
+ {ME1Type , "ME1Type" , false },
347
+ {ME2 , "ME2" , true },
348
+ }
349
+
350
+ for _ , step := range steps {
351
+ existing := tmap .At (step .typ )
352
+ if (existing == nil ) != step .newEntry {
353
+ t .Errorf ("At(%s) = %v, want new entry: %t" , step .name , existing , step .newEntry )
354
+ }
355
+ tmap .Set (step .typ , step .name )
356
+ }
357
+ }
358
+
359
+ func instantiate (t * testing.T , origin types.Type , targs ... types.Type ) types.Type {
360
+ inst , err := typeparams .Instantiate (nil , origin , targs , true )
361
+ if err != nil {
362
+ t .Fatal (err )
363
+ }
364
+ return inst
365
+ }
0 commit comments