// Copyright 2014 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 typeutil defines various utilities for types, such as Map, // a mapping from types.Type to interface{} values. package typeutil // import "golang.org/x/tools/go/types/typeutil" import ( "bytes" "fmt" "go/types" "reflect" "golang.org/x/tools/internal/typeparams" ) // Map is a hash-table-based mapping from types (types.Type) to // arbitrary interface{} values. The concrete types that implement // the Type interface are pointers. Since they are not canonicalized, // == cannot be used to check for equivalence, and thus we cannot // simply use a Go map. // // Just as with map[K]V, a nil *Map is a valid empty map. // // Not thread-safe. type Map struct { hasher Hasher // shared by many Maps table map[uint32][]entry // maps hash to bucket; entry.key==nil means unused length int // number of map entries } // entry is an entry (key/value association) in a hash bucket. type entry struct { key types.Type value interface{} } // SetHasher sets the hasher used by Map. // // All Hashers are functionally equivalent but contain internal state // used to cache the results of hashing previously seen types. // // A single Hasher created by MakeHasher() may be shared among many // Maps. This is recommended if the instances have many keys in // common, as it will amortize the cost of hash computation. // // A Hasher may grow without bound as new types are seen. Even when a // type is deleted from the map, the Hasher never shrinks, since other // types in the map may reference the deleted type indirectly. // // Hashers are not thread-safe, and read-only operations such as // Map.Lookup require updates to the hasher, so a full Mutex lock (not a // read-lock) is require around all Map operations if a shared // hasher is accessed from multiple threads. // // If SetHasher is not called, the Map will create a private hasher at // the first call to Insert. func (m *Map) SetHasher(hasher Hasher) { m.hasher = hasher } // Delete removes the entry with the given key, if any. // It returns true if the entry was found. func (m *Map) Delete(key types.Type) bool { if m != nil && m.table != nil { hash := m.hasher.Hash(key) bucket := m.table[hash] for i, e := range bucket { if e.key != nil && types.Identical(key, e.key) { // We can't compact the bucket as it // would disturb iterators. bucket[i] = entry{} m.length-- return true } } } return false } // At returns the map entry for the given key. // The result is nil if the entry is not present. func (m *Map) At(key types.Type) interface{} { if m != nil && m.table != nil { for _, e := range m.table[m.hasher.Hash(key)] { if e.key != nil && types.Identical(key, e.key) { return e.value } } } return nil } // Set sets the map entry for key to val, // and returns the previous entry, if any. func (m *Map) Set(key types.Type, value interface{}) (prev interface{}) { if m.table != nil { hash := m.hasher.Hash(key) bucket := m.table[hash] var hole *entry for i, e := range bucket { if e.key == nil { hole = &bucket[i] } else if types.Identical(key, e.key) { prev = e.value bucket[i].value = value return } } if hole != nil { *hole = entry{key, value} // overwrite deleted entry } else { m.table[hash] = append(bucket, entry{key, value}) } } else { if m.hasher.memo == nil { m.hasher = MakeHasher() } hash := m.hasher.Hash(key) m.table = map[uint32][]entry{hash: {entry{key, value}}} } m.length++ return } // Len returns the number of map entries. func (m *Map) Len() int { if m != nil { return m.length } return 0 } // Iterate calls function f on each entry in the map in unspecified order. // // If f should mutate the map, Iterate provides the same guarantees as // Go maps: if f deletes a map entry that Iterate has not yet reached, // f will not be invoked for it, but if f inserts a map entry that // Iterate has not yet reached, whether or not f will be invoked for // it is unspecified. func (m *Map) Iterate(f func(key types.Type, value interface{})) { if m != nil { for _, bucket := range m.table { for _, e := range bucket { if e.key != nil { f(e.key, e.value) } } } } } // Keys returns a new slice containing the set of map keys. // The order is unspecified. func (m *Map) Keys() []types.Type { keys := make([]types.Type, 0, m.Len()) m.Iterate(func(key types.Type, _ interface{}) { keys = append(keys, key) }) return keys } func (m *Map) toString(values bool) string { if m == nil { return "{}" } var buf bytes.Buffer fmt.Fprint(&buf, "{") sep := "" m.Iterate(func(key types.Type, value interface{}) { fmt.Fprint(&buf, sep) sep = ", " fmt.Fprint(&buf, key) if values { fmt.Fprintf(&buf, ": %q", value) } }) fmt.Fprint(&buf, "}") return buf.String() } // String returns a string representation of the map's entries. // Values are printed using fmt.Sprintf("%v", v). // Order is unspecified. func (m *Map) String() string { return m.toString(true) } // KeysString returns a string representation of the map's key set. // Order is unspecified. func (m *Map) KeysString() string { return m.toString(false) } //////////////////////////////////////////////////////////////////////// // Hasher // A Hasher maps each type to its hash value. // For efficiency, a hasher uses memoization; thus its memory // footprint grows monotonically over time. // Hashers are not thread-safe. // Hashers have reference semantics. // Call MakeHasher to create a Hasher. type Hasher struct { memo map[types.Type]uint32 // ptrMap records pointer identity. ptrMap map[interface{}]uint32 // sigTParams holds type parameters from the signature being hashed. // Signatures are considered identical modulo renaming of type parameters, so // within the scope of a signature type the identity of the signature's type // parameters is just their index. // // Since the language does not currently support referring to uninstantiated // generic types or functions, and instantiated signatures do not have type // parameter lists, we should never encounter a second non-empty type // parameter list when hashing a generic signature. sigTParams *typeparams.TypeParamList } // MakeHasher returns a new Hasher instance. func MakeHasher() Hasher { return Hasher{ memo: make(map[types.Type]uint32), ptrMap: make(map[interface{}]uint32), sigTParams: nil, } } // Hash computes a hash value for the given type t such that // Identical(t, t') => Hash(t) == Hash(t'). func (h Hasher) Hash(t types.Type) uint32 { hash, ok := h.memo[t] if !ok { hash = h.hashFor(t) h.memo[t] = hash } return hash } // hashString computes the Fowler–Noll–Vo hash of s. func hashString(s string) uint32 { var h uint32 for i := 0; i < len(s); i++ { h ^= uint32(s[i]) h *= 16777619 } return h } // hashFor computes the hash of t. func (h Hasher) hashFor(t types.Type) uint32 { // See Identical for rationale. switch t := t.(type) { case *types.Basic: return uint32(t.Kind()) case *types.Array: return 9043 + 2*uint32(t.Len()) + 3*h.Hash(t.Elem()) case *types.Slice: return 9049 + 2*h.Hash(t.Elem()) case *types.Struct: var hash uint32 = 9059 for i, n := 0, t.NumFields(); i < n; i++ { f := t.Field(i) if f.Anonymous() { hash += 8861 } hash += hashString(t.Tag(i)) hash += hashString(f.Name()) // (ignore f.Pkg) hash += h.Hash(f.Type()) } return hash case *types.Pointer: return 9067 + 2*h.Hash(t.Elem()) case *types.Signature: var hash uint32 = 9091 if t.Variadic() { hash *= 8863 } // Use a separate hasher for types inside of the signature, where type // parameter identity is modified to be (index, constraint). We must use a // new memo for this hasher as type identity may be affected by this // masking. For example, in func[T any](*T), the identity of *T depends on // whether we are mapping the argument in isolation, or recursively as part // of hashing the signature. // // We should never encounter a generic signature while hashing another // generic signature, but defensively set sigTParams only if h.mask is // unset. tparams := typeparams.ForSignature(t) if h.sigTParams == nil && tparams.Len() != 0 { h = Hasher{ // There may be something more efficient than discarding the existing // memo, but it would require detecting whether types are 'tainted' by // references to type parameters. memo: make(map[types.Type]uint32), // Re-using ptrMap ensures that pointer identity is preserved in this // hasher. ptrMap: h.ptrMap, sigTParams: tparams, } } for i := 0; i < tparams.Len(); i++ { tparam := tparams.At(i) hash += 7 * h.Hash(tparam.Constraint()) } return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results()) case *typeparams.Union: return h.hashUnion(t) case *types.Interface: // 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 go/types.identical for more details. var hash uint32 = 9103 // Hash methods. for i, n := 0, t.NumMethods(); i < n; i++ { // Method order is not significant. // Ignore m.Pkg(). m := t.Method(i) // Use shallow hash on method signature to // avoid anonymous interface cycles. hash += 3*hashString(m.Name()) + 5*h.shallowHash(m.Type()) } // Hash type restrictions. terms, err := typeparams.InterfaceTermSet(t) // if err != nil t has invalid type restrictions. if err == nil { hash += h.hashTermSet(terms) } return hash case *types.Map: return 9109 + 2*h.Hash(t.Key()) + 3*h.Hash(t.Elem()) case *types.Chan: return 9127 + 2*uint32(t.Dir()) + 3*h.Hash(t.Elem()) case *types.Named: hash := h.hashPtr(t.Obj()) targs := typeparams.NamedTypeArgs(t) for i := 0; i < targs.Len(); i++ { targ := targs.At(i) hash += 2 * h.Hash(targ) } return hash case *typeparams.TypeParam: return h.hashTypeParam(t) case *types.Tuple: return h.hashTuple(t) } panic(fmt.Sprintf("%T: %v", t, t)) } func (h Hasher) hashTuple(tuple *types.Tuple) uint32 { // See go/types.identicalTypes for rationale. n := tuple.Len() hash := 9137 + 2*uint32(n) for i := 0; i < n; i++ { hash += 3 * h.Hash(tuple.At(i).Type()) } return hash } func (h Hasher) hashUnion(t *typeparams.Union) uint32 { // Hash type restrictions. terms, err := typeparams.UnionTermSet(t) // if err != nil t has invalid type restrictions. Fall back on a non-zero // hash. if err != nil { return 9151 } return h.hashTermSet(terms) } func (h Hasher) hashTermSet(terms []*typeparams.Term) uint32 { hash := 9157 + 2*uint32(len(terms)) for _, term := range terms { // term order is not significant. termHash := h.Hash(term.Type()) if term.Tilde() { termHash *= 9161 } hash += 3 * termHash } return hash } // hashTypeParam returns a hash of the type parameter t, with a hash value // depending on whether t is contained in h.sigTParams. // // If h.sigTParams is set and contains t, then we are in the process of hashing // a signature, and the hash value of t must depend only on t's index and // constraint: signatures are considered identical modulo type parameter // renaming. To avoid infinite recursion, we only hash the type parameter // index, and rely on types.Identical to handle signatures where constraints // are not identical. // // Otherwise the hash of t depends only on t's pointer identity. func (h Hasher) hashTypeParam(t *typeparams.TypeParam) uint32 { if h.sigTParams != nil { i := t.Index() if i >= 0 && i < h.sigTParams.Len() && t == h.sigTParams.At(i) { return 9173 + 3*uint32(i) } } return h.hashPtr(t.Obj()) } // hashPtr hashes the pointer identity of ptr. It uses h.ptrMap to ensure that // pointers values are not dependent on the GC. func (h Hasher) hashPtr(ptr interface{}) uint32 { if hash, ok := h.ptrMap[ptr]; ok { return hash } hash := uint32(reflect.ValueOf(ptr).Pointer()) h.ptrMap[ptr] = hash return hash } // 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 go/types.identical, case *Interface, for more.) func (h Hasher) shallowHash(t types.Type) uint32 { // 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 := t.(type) { case *types.Signature: var hash uint32 = 604171 if t.Variadic() { hash *= 971767 } // The Signature/Tuple recursion is always finite // and invariably shallow. return hash + 1062599*h.shallowHash(t.Params()) + 1282529*h.shallowHash(t.Results()) case *types.Tuple: n := t.Len() hash := 9137 + 2*uint32(n) for i := 0; i < n; i++ { hash += 53471161 * h.shallowHash(t.At(i).Type()) } return hash case *types.Basic: return 45212177 * uint32(t.Kind()) case *types.Array: return 1524181 + 2*uint32(t.Len()) case *types.Slice: return 2690201 case *types.Struct: return 3326489 case *types.Pointer: return 4393139 case *typeparams.Union: return 562448657 case *types.Interface: return 2124679 // no recursion here case *types.Map: return 9109 case *types.Chan: return 9127 case *types.Named: return h.hashPtr(t.Obj()) case *typeparams.TypeParam: return h.hashPtr(t.Obj()) } panic(fmt.Sprintf("shallowHash: %T: %v", t, t)) }