// 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.Hasherstruct{}// HasherIgnoreTags is a variant of [Hasher] that is // consistent with [IdenticalIgnoreTags].HasherIgnoreTagsstruct{})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 { returnIdentical(, ) }func (HasherIgnoreTags) (, Type) bool { returnIdenticalIgnoreTags(, ) }// 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).varuint64for := range .Methods() {varmaphash.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 btoi( bool) int {if {return1 } else {return0 }}
The pages are generated with Goldsv0.8.7. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.