github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/go/types/typeutil/map_test.go (about) 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 }