1 | // Copyright 2014 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 typeutil_test |
6 | |
7 | // TODO(adonovan): |
8 | // - test use of explicit hasher across two maps. |
9 | // - test hashcodes are consistent with equals for a range of types |
10 | // (e.g. all types generated by type-checking some body of real code). |
11 | |
12 | import ( |
13 | "go/ast" |
14 | "go/parser" |
15 | "go/token" |
16 | "go/types" |
17 | "testing" |
18 | |
19 | "golang.org/x/tools/go/types/typeutil" |
20 | "golang.org/x/tools/internal/typeparams" |
21 | ) |
22 | |
23 | var ( |
24 | tStr = types.Typ[types.String] // string |
25 | tPStr1 = types.NewPointer(tStr) // *string |
26 | tPStr2 = types.NewPointer(tStr) // *string, again |
27 | tInt = types.Typ[types.Int] // int |
28 | tChanInt1 = types.NewChan(types.RecvOnly, tInt) // <-chan int |
29 | tChanInt2 = types.NewChan(types.RecvOnly, tInt) // <-chan int, again |
30 | ) |
31 | |
32 | func checkEqualButNotIdentical(t *testing.T, x, y types.Type, comment string) { |
33 | if !types.Identical(x, y) { |
34 | t.Errorf("%s: not equal: %s, %s", comment, x, y) |
35 | } |
36 | if x == y { |
37 | t.Errorf("%s: identical: %v, %v", comment, x, y) |
38 | } |
39 | } |
40 | |
41 | func TestAxioms(t *testing.T) { |
42 | checkEqualButNotIdentical(t, tPStr1, tPStr2, "tPstr{1,2}") |
43 | checkEqualButNotIdentical(t, tChanInt1, tChanInt2, "tChanInt{1,2}") |
44 | } |
45 | |
46 | func TestMap(t *testing.T) { |
47 | var tmap *typeutil.Map |
48 | |
49 | // All methods but Set are safe on on (*T)(nil). |
50 | tmap.Len() |
51 | tmap.At(tPStr1) |
52 | tmap.Delete(tPStr1) |
53 | tmap.KeysString() |
54 | _ = tmap.String() |
55 | |
56 | tmap = new(typeutil.Map) |
57 | |
58 | // Length of empty map. |
59 | if l := tmap.Len(); l != 0 { |
60 | t.Errorf("Len() on empty Map: got %d, want 0", l) |
61 | } |
62 | // At of missing key. |
63 | if v := tmap.At(tPStr1); v != nil { |
64 | t.Errorf("At() on empty Map: got %v, want nil", v) |
65 | } |
66 | // Deletion of missing key. |
67 | if tmap.Delete(tPStr1) { |
68 | t.Errorf("Delete() on empty Map: got true, want false") |
69 | } |
70 | // Set of new key. |
71 | if prev := tmap.Set(tPStr1, "*string"); prev != nil { |
72 | t.Errorf("Set() on empty Map returned non-nil previous value %s", prev) |
73 | } |
74 | |
75 | // Now: {*string: "*string"} |
76 | |
77 | // Length of non-empty map. |
78 | if l := tmap.Len(); l != 1 { |
79 | t.Errorf("Len(): got %d, want 1", l) |
80 | } |
81 | // At via insertion key. |
82 | if v := tmap.At(tPStr1); v != "*string" { |
83 | t.Errorf("At(): got %q, want \"*string\"", v) |
84 | } |
85 | // At via equal key. |
86 | if v := tmap.At(tPStr2); v != "*string" { |
87 | t.Errorf("At(): got %q, want \"*string\"", v) |
88 | } |
89 | // Iteration over sole entry. |
90 | tmap.Iterate(func(key types.Type, value interface{}) { |
91 | if key != tPStr1 { |
92 | t.Errorf("Iterate: key: got %s, want %s", key, tPStr1) |
93 | } |
94 | if want := "*string"; value != want { |
95 | t.Errorf("Iterate: value: got %s, want %s", value, want) |
96 | } |
97 | }) |
98 | |
99 | // Setion with key equal to present one. |
100 | if prev := tmap.Set(tPStr2, "*string again"); prev != "*string" { |
101 | t.Errorf("Set() previous value: got %s, want \"*string\"", prev) |
102 | } |
103 | |
104 | // Setion of another association. |
105 | if prev := tmap.Set(tChanInt1, "<-chan int"); prev != nil { |
106 | t.Errorf("Set() previous value: got %s, want nil", prev) |
107 | } |
108 | |
109 | // Now: {*string: "*string again", <-chan int: "<-chan int"} |
110 | |
111 | want1 := "{*string: \"*string again\", <-chan int: \"<-chan int\"}" |
112 | want2 := "{<-chan int: \"<-chan int\", *string: \"*string again\"}" |
113 | if s := tmap.String(); s != want1 && s != want2 { |
114 | t.Errorf("String(): got %s, want %s", s, want1) |
115 | } |
116 | |
117 | want1 = "{*string, <-chan int}" |
118 | want2 = "{<-chan int, *string}" |
119 | if s := tmap.KeysString(); s != want1 && s != want2 { |
120 | t.Errorf("KeysString(): got %s, want %s", s, want1) |
121 | } |
122 | |
123 | // Keys(). |
124 | I := types.Identical |
125 | switch k := tmap.Keys(); { |
126 | case I(k[0], tChanInt1) && I(k[1], tPStr1): // ok |
127 | case I(k[1], tChanInt1) && I(k[0], tPStr1): // ok |
128 | default: |
129 | t.Errorf("Keys(): got %v, want %s", k, want2) |
130 | } |
131 | |
132 | if l := tmap.Len(); l != 2 { |
133 | t.Errorf("Len(): got %d, want 1", l) |
134 | } |
135 | // At via original key. |
136 | if v := tmap.At(tPStr1); v != "*string again" { |
137 | t.Errorf("At(): got %q, want \"*string again\"", v) |
138 | } |
139 | hamming := 1 |
140 | tmap.Iterate(func(key types.Type, value interface{}) { |
141 | switch { |
142 | case I(key, tChanInt1): |
143 | hamming *= 2 // ok |
144 | case I(key, tPStr1): |
145 | hamming *= 3 // ok |
146 | } |
147 | }) |
148 | if hamming != 6 { |
149 | t.Errorf("Iterate: hamming: got %d, want %d", hamming, 6) |
150 | } |
151 | |
152 | if v := tmap.At(tChanInt2); v != "<-chan int" { |
153 | t.Errorf("At(): got %q, want \"<-chan int\"", v) |
154 | } |
155 | // Deletion with key equal to present one. |
156 | if !tmap.Delete(tChanInt2) { |
157 | t.Errorf("Delete() of existing key: got false, want true") |
158 | } |
159 | |
160 | // Now: {*string: "*string again"} |
161 | |
162 | if l := tmap.Len(); l != 1 { |
163 | t.Errorf("Len(): got %d, want 1", l) |
164 | } |
165 | // Deletion again. |
166 | if !tmap.Delete(tPStr2) { |
167 | t.Errorf("Delete() of existing key: got false, want true") |
168 | } |
169 | |
170 | // Now: {} |
171 | |
172 | if l := tmap.Len(); l != 0 { |
173 | t.Errorf("Len(): got %d, want %d", l, 0) |
174 | } |
175 | if s := tmap.String(); s != "{}" { |
176 | t.Errorf("Len(): got %q, want %q", s, "") |
177 | } |
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 | // Examples from issue #51314 |
238 | type Constraint[T any] interface{} |
239 | func Foo[T Constraint[T]]() {} |
240 | func Fn[T1 ~*T2, T2 ~*T1](t1 T1, t2 T2) {} |
241 | |
242 | // Bar and Baz are identical to Foo. |
243 | func Bar[P Constraint[P]]() {} |
244 | func Baz[Q any]() {} // The underlying type of Constraint[P] is any. |
245 | // But Quux is not. |
246 | func Quux[Q interface{ quux() }]() {} |
247 | |
248 | |
249 | type Issue56048_I interface{ m() interface { Issue56048_I } } |
250 | var Issue56048 = Issue56048_I.m |
251 | |
252 | type Issue56048_Ib interface{ m() chan []*interface { Issue56048_Ib } } |
253 | var Issue56048b = Issue56048_Ib.m |
254 | |
255 | ` |
256 | |
257 | fset := token.NewFileSet() |
258 | file, err := parser.ParseFile(fset, "p.go", src, 0) |
259 | if err != nil { |
260 | t.Fatal(err) |
261 | } |
262 | |
263 | var conf types.Config |
264 | pkg, err := conf.Check("", fset, []*ast.File{file}, nil) |
265 | if err != nil { |
266 | t.Fatal(err) |
267 | } |
268 | |
269 | // Collect types. |
270 | scope := pkg.Scope() |
271 | var ( |
272 | T1 = scope.Lookup("T1").Type().(*types.Named) |
273 | T2 = scope.Lookup("T2").Type().(*types.Named) |
274 | T1M = T1.Method(0).Type() |
275 | T2M = T2.Method(0).Type() |
276 | G = scope.Lookup("G").Type() |
277 | GInt1 = instantiate(t, G, types.Typ[types.Int]) |
278 | GInt2 = instantiate(t, G, types.Typ[types.Int]) |
279 | GStr = instantiate(t, G, types.Typ[types.String]) |
280 | C = scope.Lookup("C").Type() |
281 | CI = C.Underlying().(*types.Interface) |
282 | I = scope.Lookup("I").Type() |
283 | II = I.Underlying().(*types.Interface) |
284 | U = CI.EmbeddedType(0).(*typeparams.Union) |
285 | Fa1 = scope.Lookup("Fa1").Type().(*types.Signature) |
286 | Fa2 = scope.Lookup("Fa2").Type().(*types.Signature) |
287 | Fa1P = typeparams.ForSignature(Fa1).At(0) |
288 | Fa2Q = typeparams.ForSignature(Fa2).At(0) |
289 | Fb1 = scope.Lookup("Fb1").Type().(*types.Signature) |
290 | Fb1x = Fb1.Params().At(0).Type() |
291 | Fb1y = scope.Lookup("Fb1").(*types.Func).Scope().Lookup("y").Type() |
292 | Fb2 = scope.Lookup("Fb2").Type().(*types.Signature) |
293 | Fb2x = Fb2.Params().At(0).Type() |
294 | G1 = scope.Lookup("G1").Type().(*types.Named) |
295 | G1M = G1.Method(0).Type() |
296 | G1IntM1 = instantiate(t, G1, types.Typ[types.Int]).(*types.Named).Method(0).Type() |
297 | G1IntM2 = instantiate(t, G1, types.Typ[types.Int]).(*types.Named).Method(0).Type() |
298 | G1StrM = instantiate(t, G1, types.Typ[types.String]).(*types.Named).Method(0).Type() |
299 | G2 = scope.Lookup("G2").Type() |
300 | // See below. |
301 | // G2M = G2.Method(0).Type() |
302 | G2IntM = instantiate(t, G2, types.Typ[types.Int]).(*types.Named).Method(0).Type() |
303 | ME1 = scope.Lookup("ME1").Type() |
304 | ME1Type = scope.Lookup("ME1Type").Type() |
305 | ME2 = scope.Lookup("ME2").Type() |
306 | |
307 | Constraint = scope.Lookup("Constraint").Type() |
308 | Foo = scope.Lookup("Foo").Type() |
309 | Fn = scope.Lookup("Fn").Type() |
310 | Bar = scope.Lookup("Foo").Type() |
311 | Baz = scope.Lookup("Foo").Type() |
312 | Quux = scope.Lookup("Quux").Type() |
313 | Issue56048 = scope.Lookup("Issue56048").Type() |
314 | Issue56048b = scope.Lookup("Issue56048b").Type() |
315 | ) |
316 | |
317 | tmap := new(typeutil.Map) |
318 | |
319 | steps := []struct { |
320 | typ types.Type |
321 | name string |
322 | newEntry bool |
323 | }{ |
324 | {T1, "T1", true}, |
325 | {T2, "T2", true}, |
326 | {G, "G", true}, |
327 | {C, "C", true}, |
328 | {CI, "CI", true}, |
329 | {U, "U", true}, |
330 | {I, "I", true}, |
331 | {II, "II", true}, // should not be identical to CI |
332 | |
333 | // Methods can be identical, even with distinct receivers. |
334 | {T1M, "T1M", true}, |
335 | {T2M, "T2M", false}, |
336 | |
337 | // Identical instances should map to the same entry. |
338 | {GInt1, "GInt1", true}, |
339 | {GInt2, "GInt2", false}, |
340 | // ..but instantiating with different arguments should yield a new entry. |
341 | {GStr, "GStr", true}, |
342 | |
343 | // F1 and F2 should have identical signatures. |
344 | {Fa1, "F1", true}, |
345 | {Fa2, "F2", false}, |
346 | |
347 | // The identity of P and Q should not have been affected by type parameter |
348 | // masking during signature hashing. |
349 | {Fa1P, "F1P", true}, |
350 | {Fa2Q, "F2Q", true}, |
351 | |
352 | {Fb1y, "Fb1y", true}, |
353 | {Fb1x, "Fb1x", false}, |
354 | {Fb2x, "Fb2x", true}, |
355 | {Fb1, "Fb1", true}, |
356 | |
357 | // Mapping elements of the function scope should not affect the identity of |
358 | // Fb2 or Fb1. |
359 | {Fb2, "Fb1", false}, |
360 | |
361 | {G1, "G1", true}, |
362 | {G1M, "G1M", true}, |
363 | {G2, "G2", true}, |
364 | |
365 | // See golang/go#49912: receiver type parameter names should be ignored |
366 | // when comparing method identity. |
367 | // {G2M, "G2M", false}, |
368 | {G1IntM1, "G1IntM1", true}, |
369 | {G1IntM2, "G1IntM2", false}, |
370 | {G1StrM, "G1StrM", true}, |
371 | {G2IntM, "G2IntM", false}, // identical to G1IntM1 |
372 | |
373 | {ME1, "ME1", true}, |
374 | {ME1Type, "ME1Type", false}, |
375 | {ME2, "ME2", true}, |
376 | |
377 | // See golang/go#51314: avoid infinite recursion on cyclic type constraints. |
378 | {Constraint, "Constraint", true}, |
379 | {Foo, "Foo", true}, |
380 | {Fn, "Fn", true}, |
381 | {Bar, "Bar", false}, |
382 | {Baz, "Baz", false}, |
383 | {Quux, "Quux", true}, |
384 | |
385 | {Issue56048, "Issue56048", true}, // (not actually about generics) |
386 | {Issue56048b, "Issue56048b", true}, // (not actually about generics) |
387 | } |
388 | |
389 | for _, step := range steps { |
390 | existing := tmap.At(step.typ) |
391 | if (existing == nil) != step.newEntry { |
392 | t.Errorf("At(%s) = %v, want new entry: %t", step.name, existing, step.newEntry) |
393 | } |
394 | tmap.Set(step.typ, step.name) |
395 | } |
396 | } |
397 | |
398 | func instantiate(t *testing.T, origin types.Type, targs ...types.Type) types.Type { |
399 | inst, err := typeparams.Instantiate(nil, origin, targs, true) |
400 | if err != nil { |
401 | t.Fatal(err) |
402 | } |
403 | return inst |
404 | } |
405 |
Members