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  }