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