Source file src/go/types/hash_test.go

     1  // Copyright 2026 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 types_test
     6  
     7  // This file defines a test of using [types.Hasher].
     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  	// eqclasses groups the above types into types.Identical equivalence classes.
   169  	eqclasses := [][]types.Type{
   170  		{T1},
   171  		{T2},
   172  		{G},
   173  		{C},
   174  		{CI},
   175  		{U},
   176  		{I},
   177  		{II}, // should not be identical to CI
   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] // arbitrary representative of class
   216  
   217  			for yi := range classes {
   218  				if xi == yi {
   219  					// Within a class, each element is equivalent to first
   220  					// and has the same hash.
   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  					// Across classes, no element is equivalent to first.
   231  					// (We can't say for sure that the hashes are unequal.)
   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  	// Hasher considers the two Tagged{1,2} types distinct.
   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  	// HasherIgnoreTags considers the two Tagged{1,2} types equal.
   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