Source file
src/go/types/hash_test.go
1
2
3
4
5 package types_test
6
7
8
9 import (
10 "go/ast"
11 "go/parser"
12 "go/token"
13 "go/types"
14 "hash/maphash"
15 "testing"
16 )
17
18 func TestHasher(t *testing.T) {
19 const src = `
20 package p
21
22 // Basic defined types.
23 type T1 int
24 type T2 int
25
26 // Identical methods.
27 func (T1) M(int) {}
28 func (T2) M(int) {}
29
30 // A constraint interface.
31 type C interface {
32 ~int | string
33 }
34
35 type I interface {
36 }
37
38 // A generic type.
39 type G[P C] int
40
41 // Generic functions with identical signature.
42 func Fa1[P C](p P) {}
43 func Fa2[Q C](q Q) {}
44
45 // Fb1 and Fb2 are identical and should be mapped to the same entry, even if we
46 // map their arguments first.
47 func Fb1[P any](x *P) {
48 var y *P // Map this first.
49 _ = y
50 }
51 func Fb2[Q any](x *Q) {
52 }
53
54 // G1 and G2 are mutally recursive, and have identical methods.
55 type G1[P any] struct{
56 Field *G2[P]
57 }
58 func (G1[P]) M(G1[P], G2[P]) {}
59 type G2[Q any] struct{
60 Field *G1[Q]
61 }
62 func (G2[P]) M(G1[P], G2[P]) {}
63
64 // Method type expressions on different generic types are different.
65 var ME1 = G1[int].M
66 var ME2 = G2[int].M
67
68 // ME1Type should have identical type as ME1.
69 var ME1Type func(G1[int], G1[int], G2[int])
70
71 // Examples from issue #51314
72 type Constraint[T any] any
73 func Foo[T Constraint[T]]() {}
74 func Fn[T1 ~*T2, T2 ~*T1](t1 T1, t2 T2) {}
75
76 // Bar and Baz are identical to Foo.
77 func Bar[P Constraint[P]]() {}
78 func Baz[Q any]() {} // The underlying type of Constraint[P] is any.
79 // But Quux is not.
80 func Quux[Q interface{ quux() }]() {}
81
82 type Issue56048_I interface{ m() interface { Issue56048_I } }
83 var Issue56048 = Issue56048_I.m
84
85 type Issue56048_Ib interface{ m() chan []*interface { Issue56048_Ib } }
86 var Issue56048b = Issue56048_Ib.m
87
88 // Non-generic alias
89 type NonAlias int
90 type Alias1 = NonAlias
91 type Alias2 = NonAlias
92
93 type Tagged1 struct { F int "tag1" }
94 type Tagged2 struct { F int "tag2" }
95 `
96
97 fset := token.NewFileSet()
98 file, err := parser.ParseFile(fset, "p.go", src, 0)
99 if err != nil {
100 t.Fatal(err)
101 }
102
103 var conf types.Config
104 pkg, err := conf.Check("", fset, []*ast.File{file}, nil)
105 if err != nil {
106 t.Fatal(err)
107 }
108
109 instantiate := func(origin types.Type, targs ...types.Type) types.Type {
110 inst, err := types.Instantiate(nil, origin, targs, true)
111 if err != nil {
112 t.Fatal(err)
113 }
114 return inst
115 }
116
117 scope := pkg.Scope()
118 var (
119 tInt = types.Typ[types.Int]
120 tString = types.Typ[types.String]
121
122 T1 = scope.Lookup("T1").Type().(*types.Named)
123 T2 = scope.Lookup("T2").Type().(*types.Named)
124 T1M = T1.Method(0).Type()
125 T2M = T2.Method(0).Type()
126 G = scope.Lookup("G").Type()
127 GInt1 = instantiate(G, tInt)
128 GInt2 = instantiate(G, tInt)
129 GStr = instantiate(G, tString)
130 C = scope.Lookup("C").Type()
131 CI = C.Underlying().(*types.Interface)
132 I = scope.Lookup("I").Type()
133 II = I.Underlying().(*types.Interface)
134 U = CI.EmbeddedType(0).(*types.Union)
135 Fa1 = scope.Lookup("Fa1").Type().(*types.Signature)
136 Fa2 = scope.Lookup("Fa2").Type().(*types.Signature)
137 Fa1P = Fa1.TypeParams().At(0)
138 Fa2Q = Fa2.TypeParams().At(0)
139 Fb1 = scope.Lookup("Fb1").Type().(*types.Signature)
140 Fb1x = Fb1.Params().At(0).Type()
141 Fb1y = scope.Lookup("Fb1").(*types.Func).Scope().Lookup("y").Type()
142 Fb2 = scope.Lookup("Fb2").Type().(*types.Signature)
143 Fb2x = Fb2.Params().At(0).Type()
144 G1 = scope.Lookup("G1").Type().(*types.Named)
145 G1M = G1.Method(0).Type()
146 G1IntM1 = instantiate(G1, tInt).(*types.Named).Method(0).Type()
147 G1IntM2 = instantiate(G1, tInt).(*types.Named).Method(0).Type()
148 G1StrM = instantiate(G1, tString).(*types.Named).Method(0).Type()
149 G2 = scope.Lookup("G2").Type()
150 G2IntM = instantiate(G2, tInt).(*types.Named).Method(0).Type()
151 ME1 = scope.Lookup("ME1").Type()
152 ME1Type = scope.Lookup("ME1Type").Type()
153 ME2 = scope.Lookup("ME2").Type()
154
155 Constraint = scope.Lookup("Constraint").Type()
156 Foo = scope.Lookup("Foo").Type()
157 Fn = scope.Lookup("Fn").Type()
158 Bar = scope.Lookup("Bar").Type()
159 Baz = scope.Lookup("Baz").Type()
160 Quux = scope.Lookup("Quux").Type()
161 Issue56048 = scope.Lookup("Issue56048").Type()
162 Issue56048b = scope.Lookup("Issue56048b").Type()
163
164 NonAlias = scope.Lookup("NonAlias").Type()
165 Alias1 = scope.Lookup("Alias1").Type()
166 Alias2 = scope.Lookup("Alias2").Type()
167
168 Tagged1 = scope.Lookup("Tagged1").Type().Underlying().(*types.Struct)
169 Tagged2 = scope.Lookup("Tagged2").Type().Underlying().(*types.Struct)
170 )
171
172
173 eqclasses := [][]types.Type{
174 {T1},
175 {T2},
176 {G},
177 {C},
178 {CI},
179 {U},
180 {I},
181 {II},
182 {T1M, T2M},
183 {GInt1, GInt2},
184 {GStr},
185 {Fa1, Fa2},
186 {Fa1P},
187 {Fa2Q},
188 {Fb1y, Fb1x},
189 {Fb2x},
190 {Fb1, Fb2},
191 {G1},
192 {G1M},
193 {G2},
194 {G1IntM1, G1IntM2, G2IntM},
195 {G1StrM},
196 {ME1, ME1Type},
197 {ME2},
198 {Constraint},
199 {Foo, Bar},
200 {Baz},
201 {Fn},
202 {Quux},
203 {Issue56048},
204 {Issue56048b},
205 {NonAlias, Alias1, Alias2},
206 }
207
208 run := func(t *testing.T, hasher maphash.Hasher[types.Type], eq func(x, y types.Type) bool, classes [][]types.Type) {
209 seed := maphash.MakeSeed()
210
211 hash := func(t types.Type) uint64 {
212 var h maphash.Hash
213 h.SetSeed(seed)
214 hasher.Hash(&h, t)
215 return h.Sum64()
216 }
217
218 for xi, class := range classes {
219 tx := class[0]
220
221 for yi := range classes {
222 if xi == yi {
223
224
225 for i, ty := range class {
226 hx, hy := hash(tx), hash(ty)
227 if !eq(tx, ty) || hx != hy {
228 t.Fatalf("class[%d][%d] (%v, hash %x) is not equivalent to class[%d][%d] (%v, hash %x)",
229 xi, 0, tx, hx,
230 yi, i, ty, hy)
231 }
232 }
233 } else {
234
235
236 for k, typ := range classes[yi] {
237 if eq(tx, typ) {
238 t.Fatalf("class[%d][%d] (%v) is equivalent to class[%d][%d] (%v)",
239 xi, 0, tx,
240 yi, k, typ)
241 }
242 }
243 }
244 }
245 }
246 }
247
248
249 t.Run("Hasher", func(t *testing.T) {
250 run(t, types.Hasher{}, types.Identical, append(
251 eqclasses,
252 []types.Type{Tagged1},
253 []types.Type{Tagged2},
254 ))
255 })
256
257
258 t.Run("HasherIgnoreTags", func(t *testing.T) {
259 run(t, types.HasherIgnoreTags{}, types.IdenticalIgnoreTags, append(
260 eqclasses,
261 []types.Type{Tagged1, Tagged2},
262 ))
263 })
264 }
265
View as plain text