Source File
hash.go
Belonging Package
go/types
// Copyright 2026 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.package types// This file defines a hash function for Types.import ()type (// Hasher defines a hash function and equivalence relation// for [Types] that is consistent with [Identical].// Hashers are stateless.Hasher struct{}// HasherIgnoreTags is a variant of [Hasher] that is// consistent with [IdenticalIgnoreTags].HasherIgnoreTags struct{})var (_ maphash.Hasher[Type] = Hasher{}_ maphash.Hasher[Type] = HasherIgnoreTags{})func (Hasher) ( *maphash.Hash, Type) {// The two hashers use essentially the same hash function,// which ignores tags; only the Equal methods vary.// But for future-proofing we gratuitously force them// to differ by one byte..WriteByte(0)hasher{inGenericSig: false}.hash(, )}func (HasherIgnoreTags) ( *maphash.Hash, Type) {.WriteByte(1)hasher{inGenericSig: false}.hash(, )}func (Hasher) (, Type) bool { return Identical(, ) }func (HasherIgnoreTags) (, Type) bool { return IdenticalIgnoreTags(, ) }// hasher holds the state of a single hash traversal, namely,// whether we are inside the signature of a generic function.// This is used to optimize [hasher.hashTypeParam].type hasher struct{ inGenericSig bool }func ( hasher) ( *maphash.Hash, Type) {// See [Identical] for rationale.switch t := .(type) {case *Alias:.(, Unalias())case *Array:.WriteByte('A')maphash.WriteComparable(, .Len()).(, .Elem())case *Basic:.WriteByte('B').WriteByte(byte(.Kind()))case *Chan:.WriteByte('C').WriteByte(byte(.Dir())).(, .Elem())case *Interface:.WriteByte('I').WriteByte(byte(.NumMethods()))// Interfaces are identical if they have the same set of methods, with// identical names and types, and they have the same set of type// restrictions. See [Identical] for more details.// Hash the methods.//// Because [Identical] treats Methods as an unordered set,// we must either:// (a) sort the methods into some canonical order; or// (b) hash them each in parallel, combine them with a// commutative operation such as + or ^, and then// write this value into the primary hasher.// Since (a) requires allocation, we choose (b).var uint64for := range .Methods() {var maphash.Hash.SetSeed(.Seed())// Ignore m.Pkg().// Use shallow hash on method signature to// avoid anonymous interface cycles..WriteString(.Name()).shallowHash(&, .Type())^= .Sum64()}maphash.WriteComparable(, )// Hash type restrictions.// TODO(adonovan): call (fork of) InterfaceTermSet from// golang.org/x/tools/internal/typeparams/normalize.go.// hr.hashTermSet(h, terms)case *Map:.WriteByte('M').(, .Key()).(, .Elem())case *Named:.WriteByte('N').hashTypeName(, .Obj())for := range .TypeArgs().Types() {.(, )}case *Pointer:.WriteByte('P').(, .Elem())case *Signature:.WriteByte('F')maphash.WriteComparable(, .Variadic()):= .TypeParams()if := .Len(); > 0 {.inGenericSig = true // affects constraints, params, and resultsmaphash.WriteComparable(, )for := range .TypeParams() {.(, .Constraint())}}.hashTuple(, .Params()).hashTuple(, .Results())case *Slice:.WriteByte('S').(, .Elem())case *Struct:.WriteByte('R') // mnemonic: a struct is a record type:= .NumFields().WriteByte(byte())for := range {:= .Field()maphash.WriteComparable(, .Anonymous())// Ignore t.Tag(i), so that a single hash function// can be used with both [Identical] and [IdenticalIgnoreTags]..WriteString(.Name()) // (ignore f.Pkg).(, .Type())}case *Tuple:.hashTuple(, )case *TypeParam:.hashTypeParam(, )case *Union:.WriteByte('U')// TODO(adonovan): opt: call (fork of) UnionTermSet from// golang.org/x/tools/internal/typeparams/normalize.go.// hr.hashTermSet(h, terms)default:panic(fmt.Sprintf("%T: %v", , ))}}func ( hasher) ( *maphash.Hash, *Tuple) {.WriteByte('T').WriteByte(byte(.Len()))for := range .Variables() {.hash(, .Type())}}// func (hr hasher) hashTermSet(h *maphash.Hash, terms []*Term) {// h.WriteByte(byte(len(terms)))// for _, term := range terms {// // term order is not significant.// h.WriteByte(byte(btoi(term.Tilde())))// hr.hash(h, term.Type())// }// }// hashTypeParam encodes a type parameter into hasher h.func ( hasher) ( *maphash.Hash, *TypeParam) {.WriteByte('P')// Within the signature of a generic function, TypeParams are// identical if they have the same index and constraint, so we// hash them based on index.//// When we are outside a generic function, free TypeParams are// identical iff they are the same object, so we can use a// more discriminating hash consistent with object identity.// This optimization saves [Map] about 4% when hashing all the// Info.Types in the forward closure of net/http.if !.inGenericSig {// Optimization: outside a generic function signature,// use a more discrimating hash consistent with object identity..hashTypeName(, .Obj())} else {.WriteByte(byte(.Index()))}}// hashTypeName hashes the pointer of tname.func (hasher) ( *maphash.Hash, *TypeName) {.WriteByte('N')// Since Identical uses == to compare TypeNames,// the hash function uses maphash.Comparable.maphash.WriteComparable(, )}// shallowHash computes a hash of t without looking at any of its// element Types, to avoid potential anonymous cycles in the types of// interface methods.//// When an unnamed non-empty interface type appears anywhere among the// arguments or results of an interface method, there is a potential// for endless recursion. Consider://// type X interface { m() []*interface { X } }//// The problem is that the Methods of the interface in m's result type// include m itself; there is no mention of the named type X that// might help us break the cycle.// (See comment in [Identical], case *Interface, for more.)func ( hasher) ( *maphash.Hash, Type) {// t is the type of an interface method (Signature),// its params or results (Tuples), or their immediate// elements (mostly Slice, Pointer, Basic, Named),// so there's no need to optimize anything else.switch t := .(type) {case *Alias:.(, Unalias())case *Array:.WriteByte('A')maphash.WriteComparable(, .Len())// ignore t.Elem()case *Basic:.WriteByte('B').WriteByte(byte(.Kind()))case *Chan:.WriteByte('C')// ignore Dir(), Elem()case *Interface:.WriteByte('I')// no recursion herecase *Map:.WriteByte('M')// ignore Key(), Elem()case *Named:.hashTypeName(, .Obj())case *Pointer:.WriteByte('P')// ignore t.Elem()case *Signature:.WriteByte(byte(btoi(.Variadic())))// The Signature/Tuple recursion is always// finite and invariably shallow..(, .Params()).(, .Results())case *Slice:.WriteByte('S')// ignore t.Elem()case *Struct:.WriteByte('R') // mnemonic: a struct is a record type.WriteByte(byte(.NumFields()))// ignore t.Fields()case *Tuple:.WriteByte('T').WriteByte(byte(.Len()))for := range .Variables() {.(, .Type())}case *TypeParam:.hashTypeParam(, )case *Union:.WriteByte('U')// ignore term setdefault:panic(fmt.Sprintf("shallowHash: %T: %v", , ))}}func ( bool) int {if {return 1} else {return 0}}
The pages are generated with Golds v0.8.4. (GOOS=linux GOARCH=amd64)