| // 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 hash table that maps [types.Type] to any value. |
| package typeutil |
| |
| import ( |
| "bytes" |
| "fmt" |
| "go/types" |
| "hash/maphash" |
| "unsafe" |
| |
| "golang.org/x/tools/internal/typeparams" |
| ) |
| |
| // Map is a hash-table-based mapping from types (types.Type) to |
| // arbitrary 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. |
| // |
| // Read-only map operations ([Map.At], [Map.Len], and so on) may |
| // safely be called concurrently. |
| // |
| // TODO(adonovan): deprecate in favor of https://go.dev/issues/69420 |
| // and 69559, if the latter proposals for a generic hash-map type and |
| // a types.Hash function are accepted. |
| type Map struct { |
| 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 any |
| } |
| |
| // SetHasher has no effect. |
| // |
| // It is a relic of an optimization that is no longer profitable. Do |
| // not use [Hasher], [MakeHasher], or [SetHasher] in new code. |
| func (m *Map) SetHasher(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 := 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) any { |
| if m != nil && m.table != nil { |
| for _, e := range m.table[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 any) (prev any) { |
| if m.table != nil { |
| hash := 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 { |
| hash := 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 any)) { |
| 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, _ any) { |
| 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 any) { |
| 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 -- |
| |
| // hash returns the hash of type t. |
| // TODO(adonovan): replace by types.Hash when Go proposal #69420 is accepted. |
| func hash(t types.Type) uint32 { |
| return theHasher.Hash(t) |
| } |
| |
| // A Hasher provides a [Hasher.Hash] method to map a type to its hash value. |
| // Hashers are stateless, and all are equivalent. |
| type Hasher struct{} |
| |
| var theHasher Hasher |
| |
| // MakeHasher returns Hasher{}. |
| // Hashers are stateless; all are equivalent. |
| func MakeHasher() Hasher { return theHasher } |
| |
| // 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 { |
| return hasher{inGenericSig: false}.hash(t) |
| } |
| |
| // hasher holds the state of a single Hash traversal: whether we are |
| // inside the signature of a generic function; this is used to |
| // optimize [hasher.hashTypeParam]. |
| type hasher struct{ inGenericSig bool } |
| |
| // 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 |
| } |
| |
| // hash computes the hash of t. |
| func (h hasher) hash(t types.Type) uint32 { |
| // See Identical for rationale. |
| switch t := t.(type) { |
| case *types.Basic: |
| return uint32(t.Kind()) |
| |
| case *types.Alias: |
| return h.hash(types.Unalias(t)) |
| |
| 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 |
| } |
| |
| tparams := t.TypeParams() |
| if n := tparams.Len(); n > 0 { |
| h.inGenericSig = true // affects constraints, params, and results |
| |
| for i := range n { |
| tparam := tparams.At(i) |
| hash += 7 * h.hash(tparam.Constraint()) |
| } |
| } |
| |
| return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results()) |
| |
| case *types.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.hashTypeName(t.Obj()) |
| targs := t.TypeArgs() |
| for i := 0; i < targs.Len(); i++ { |
| targ := targs.At(i) |
| hash += 2 * h.hash(targ) |
| } |
| return hash |
| |
| case *types.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 := range n { |
| hash += 3 * h.hash(tuple.At(i).Type()) |
| } |
| return hash |
| } |
| |
| func (h hasher) hashUnion(t *types.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 []*types.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 the hash of a type parameter. |
| func (h hasher) hashTypeParam(t *types.TypeParam) uint32 { |
| // 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 |
| // types.Info.Types in the forward closure of net/http. |
| if !h.inGenericSig { |
| // Optimization: outside a generic function signature, |
| // use a more discrimating hash consistent with object identity. |
| return h.hashTypeName(t.Obj()) |
| } |
| return 9173 + 3*uint32(t.Index()) |
| } |
| |
| var theSeed = maphash.MakeSeed() |
| |
| // hashTypeName hashes the pointer of tname. |
| func (hasher) hashTypeName(tname *types.TypeName) uint32 { |
| // Since types.Identical uses == to compare TypeNames, |
| // the Hash function uses maphash.Comparable. |
| // TODO(adonovan): or will, when it becomes available in go1.24. |
| // In the meantime we use the pointer's numeric value. |
| // |
| // hash := maphash.Comparable(theSeed, tname) |
| // |
| // (Another approach would be to hash the name and package |
| // path, and whether or not it is a package-level typename. It |
| // is rare for a package to define multiple local types with |
| // the same name.) |
| ptr := uintptr(unsafe.Pointer(tname)) |
| if unsafe.Sizeof(ptr) == 8 { |
| hash := uint64(ptr) |
| return uint32(hash ^ (hash >> 32)) |
| } else { |
| return uint32(ptr) |
| } |
| } |
| |
| // 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.Alias: |
| return h.shallowHash(types.Unalias(t)) |
| |
| 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 := range n { |
| 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 *types.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.hashTypeName(t.Obj()) |
| |
| case *types.TypeParam: |
| return h.hashTypeParam(t) |
| } |
| panic(fmt.Sprintf("shallowHash: %T: %v", t, t)) |
| } |