mirror of
https://github.com/golang/go
synced 2024-11-18 19:54:44 -07:00
go.tools/go/types/typeutil: new package for type utilities.
Contains the members formerly known as: - ssa.IntuitiveMethodSet - typemap.M (now: Map) LGTM=gri R=gri CC=golang-codereviews https://golang.org/cl/65670043
This commit is contained in:
parent
42022f89d4
commit
03ca00ddd4
@ -15,7 +15,7 @@ import (
|
||||
|
||||
"code.google.com/p/go.tools/go/ssa"
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
"code.google.com/p/go.tools/go/types/typemap"
|
||||
"code.google.com/p/go.tools/go/types/typeutil"
|
||||
)
|
||||
|
||||
// object.flags bitmask values.
|
||||
@ -208,15 +208,15 @@ type analysis struct {
|
||||
track track // pointerlike types whose aliasing we track
|
||||
|
||||
// Reflection & intrinsics:
|
||||
hasher typemap.Hasher // cache of type hashes
|
||||
reflectValueObj types.Object // type symbol for reflect.Value (if present)
|
||||
reflectValueCall *ssa.Function // (reflect.Value).Call
|
||||
reflectRtypeObj types.Object // *types.TypeName for reflect.rtype (if present)
|
||||
reflectRtypePtr *types.Pointer // *reflect.rtype
|
||||
reflectType *types.Named // reflect.Type
|
||||
rtypes typemap.M // nodeid of canonical *rtype-tagged object for type T
|
||||
reflectZeros typemap.M // nodeid of canonical T-tagged object for zero value
|
||||
runtimeSetFinalizer *ssa.Function // runtime.SetFinalizer
|
||||
hasher typeutil.Hasher // cache of type hashes
|
||||
reflectValueObj types.Object // type symbol for reflect.Value (if present)
|
||||
reflectValueCall *ssa.Function // (reflect.Value).Call
|
||||
reflectRtypeObj types.Object // *types.TypeName for reflect.rtype (if present)
|
||||
reflectRtypePtr *types.Pointer // *reflect.rtype
|
||||
reflectType *types.Named // reflect.Type
|
||||
rtypes typeutil.Map // nodeid of canonical *rtype-tagged object for type T
|
||||
reflectZeros typeutil.Map // nodeid of canonical T-tagged object for zero value
|
||||
runtimeSetFinalizer *ssa.Function // runtime.SetFinalizer
|
||||
}
|
||||
|
||||
// enclosingObj returns the object (addressible memory object) that encloses node id.
|
||||
@ -294,7 +294,7 @@ func Analyze(config *Config) *Result {
|
||||
globalobj: make(map[ssa.Value]nodeid),
|
||||
flattenMemo: make(map[types.Type][]*fieldInfo),
|
||||
trackTypes: make(map[types.Type]bool),
|
||||
hasher: typemap.MakeHasher(),
|
||||
hasher: typeutil.MakeHasher(),
|
||||
intrinsics: make(map[*ssa.Function]intrinsic),
|
||||
work: makeMapWorklist(),
|
||||
result: &Result{
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
|
||||
"code.google.com/p/go.tools/go/callgraph"
|
||||
"code.google.com/p/go.tools/go/ssa"
|
||||
"code.google.com/p/go.tools/go/types/typemap"
|
||||
"code.google.com/p/go.tools/go/types/typeutil"
|
||||
)
|
||||
|
||||
// A Config formulates a pointer analysis problem for Analyze().
|
||||
@ -200,8 +200,8 @@ func (s PointsToSet) Labels() []*Label {
|
||||
//
|
||||
// The result is empty unless CanHaveDynamicTypes(T).
|
||||
//
|
||||
func (s PointsToSet) DynamicTypes() *typemap.M {
|
||||
var tmap typemap.M
|
||||
func (s PointsToSet) DynamicTypes() *typeutil.Map {
|
||||
var tmap typeutil.Map
|
||||
tmap.SetHasher(s.a.hasher)
|
||||
for ifaceObjId := range s.pts {
|
||||
if !s.a.isTaggedObject(ifaceObjId) {
|
||||
@ -250,6 +250,6 @@ func (p Pointer) MayAlias(q Pointer) bool {
|
||||
}
|
||||
|
||||
// DynamicTypes returns p.PointsTo().DynamicTypes().
|
||||
func (p Pointer) DynamicTypes() *typemap.M {
|
||||
func (p Pointer) DynamicTypes() *typeutil.Map {
|
||||
return p.PointsTo().DynamicTypes()
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ import (
|
||||
"code.google.com/p/go.tools/go/ssa"
|
||||
"code.google.com/p/go.tools/go/ssa/ssautil"
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
"code.google.com/p/go.tools/go/types/typemap"
|
||||
"code.google.com/p/go.tools/go/types/typeutil"
|
||||
)
|
||||
|
||||
var inputs = []string{
|
||||
@ -432,8 +432,8 @@ func underlyingType(typ types.Type) types.Type {
|
||||
}
|
||||
|
||||
func checkTypesExpectation(e *expectation, pts pointer.PointsToSet, typ types.Type) bool {
|
||||
var expected typemap.M
|
||||
var surplus typemap.M
|
||||
var expected typeutil.Map
|
||||
var surplus typeutil.Map
|
||||
exact := true
|
||||
for _, g := range e.types {
|
||||
if g == types.Typ[types.Invalid] {
|
||||
|
@ -44,7 +44,7 @@ import (
|
||||
|
||||
"code.google.com/p/go.tools/go/ssa"
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
"code.google.com/p/go.tools/go/types/typemap"
|
||||
"code.google.com/p/go.tools/go/types/typeutil"
|
||||
)
|
||||
|
||||
type value interface{}
|
||||
@ -92,7 +92,7 @@ func hashString(s string) int {
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
hasher = typemap.MakeHasher()
|
||||
hasher = typeutil.MakeHasher()
|
||||
)
|
||||
|
||||
// hashType returns a hash for t such that
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"sort"
|
||||
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
"code.google.com/p/go.tools/go/types/typeutil"
|
||||
)
|
||||
|
||||
// relName returns the name of v relative to i.
|
||||
@ -407,7 +408,7 @@ func WritePackage(buf *bytes.Buffer, p *Package) {
|
||||
case *Type:
|
||||
fmt.Fprintf(buf, " type %-*s %s\n",
|
||||
maxname, name, types.TypeString(p.Object, mem.Type().Underlying()))
|
||||
for _, meth := range IntuitiveMethodSet(mem.Type(), &p.Prog.MethodSets) {
|
||||
for _, meth := range typeutil.IntuitiveMethodSet(mem.Type(), &p.Prog.MethodSets) {
|
||||
fmt.Fprintf(buf, " %s\n", types.SelectionString(p.Object, meth))
|
||||
}
|
||||
|
||||
@ -420,37 +421,6 @@ func WritePackage(buf *bytes.Buffer, p *Package) {
|
||||
fmt.Fprintf(buf, "\n")
|
||||
}
|
||||
|
||||
// IntuitiveMethodSet returns the intuitive method set of a type, T.
|
||||
//
|
||||
// The result contains MethodSet(T) and additionally, if T is a
|
||||
// concrete type, methods belonging to *T if there is no similarly
|
||||
// named method on T itself. This corresponds to user intuition about
|
||||
// method sets; this function is intended only for user interfaces.
|
||||
//
|
||||
// The order of the result is as for types.MethodSet(T).
|
||||
//
|
||||
// TODO(gri): move this to go/types?
|
||||
//
|
||||
func IntuitiveMethodSet(T types.Type, msets *types.MethodSetCache) []*types.Selection {
|
||||
var result []*types.Selection
|
||||
mset := msets.MethodSet(T)
|
||||
if _, ok := T.Underlying().(*types.Interface); ok {
|
||||
for i, n := 0, mset.Len(); i < n; i++ {
|
||||
result = append(result, mset.At(i))
|
||||
}
|
||||
} else {
|
||||
pmset := msets.MethodSet(types.NewPointer(T))
|
||||
for i, n := 0, pmset.Len(); i < n; i++ {
|
||||
meth := pmset.At(i)
|
||||
if m := mset.Lookup(meth.Obj().Pkg(), meth.Obj().Name()); m != nil {
|
||||
meth = m
|
||||
}
|
||||
result = append(result, meth)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func commaOk(x bool) string {
|
||||
if x {
|
||||
return ",ok"
|
||||
|
@ -16,7 +16,7 @@ import (
|
||||
"code.google.com/p/go.tools/go/exact"
|
||||
"code.google.com/p/go.tools/go/loader"
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
"code.google.com/p/go.tools/go/types/typemap"
|
||||
"code.google.com/p/go.tools/go/types/typeutil"
|
||||
)
|
||||
|
||||
// A Program is a partial or complete Go program converted to SSA form.
|
||||
@ -29,7 +29,7 @@ type Program struct {
|
||||
MethodSets types.MethodSetCache // cache of type-checker's method-sets
|
||||
|
||||
methodsMu sync.Mutex // guards the following maps:
|
||||
methodSets typemap.M // maps type to its concrete methodSet
|
||||
methodSets typeutil.Map // maps type to its concrete methodSet
|
||||
boundMethodWrappers map[*types.Func]*Function // wrappers for curried x.Method closures
|
||||
ifaceMethodWrappers map[*types.Func]*Function // wrappers for curried I.Method functions
|
||||
}
|
||||
@ -53,7 +53,7 @@ type Package struct {
|
||||
started int32 // atomically tested and set at start of build phase
|
||||
ninit int32 // number of init functions
|
||||
info *loader.PackageInfo // package ASTs and type information
|
||||
needRTTI typemap.M // types for which runtime type info is needed
|
||||
needRTTI typeutil.Map // types for which runtime type info is needed
|
||||
}
|
||||
|
||||
// A Member is a member of a Go package, implemented by *NamedConst,
|
||||
|
@ -1,14 +1,10 @@
|
||||
// Package typemap defines type M, a hash-table-based mapping from
|
||||
// types (go/types.Type) to arbitrary values, and a hash function on
|
||||
// types.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// Not thread-safe.
|
||||
//
|
||||
package typemap
|
||||
// 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 (
|
||||
"bytes"
|
||||
@ -18,12 +14,18 @@ import (
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
// typemap.M is a mapping from types.Type to interface{} values.
|
||||
// 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 *typemap.M is a valid empty map.
|
||||
// Just as with map[K]V, a nil *Map is a valid empty map.
|
||||
//
|
||||
type M struct {
|
||||
hasher Hasher // shared by many typemap.Ms
|
||||
// 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
|
||||
}
|
||||
@ -34,36 +36,35 @@ type entry struct {
|
||||
value interface{}
|
||||
}
|
||||
|
||||
// SetHasher sets the hasher used by typemap.M.
|
||||
// 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 typemap.M instances. This is recommended if the instances
|
||||
// have many keys in common, as it will amortize the cost of hash
|
||||
// computation.
|
||||
// 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
|
||||
// M.Lookup require updates to the hasher, so a full Mutex lock (not a
|
||||
// read-lock) is require around all typemap.M operations if a shared
|
||||
// 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 type-map will create a private
|
||||
// hasher at the first call to Insert.
|
||||
// If SetHasher is not called, the Map will create a private hasher at
|
||||
// the first call to Insert.
|
||||
//
|
||||
func (m *M) SetHasher(hasher Hasher) {
|
||||
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 *M) Delete(key types.Type) bool {
|
||||
func (m *Map) Delete(key types.Type) bool {
|
||||
if m != nil && m.table != nil {
|
||||
hash := m.hasher.Hash(key)
|
||||
bucket := m.table[hash]
|
||||
@ -83,7 +84,7 @@ func (m *M) Delete(key types.Type) bool {
|
||||
// At returns the map entry for the given key.
|
||||
// The result is nil if the entry is not present.
|
||||
//
|
||||
func (m *M) At(key types.Type) interface{} {
|
||||
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) {
|
||||
@ -96,7 +97,7 @@ func (m *M) At(key types.Type) interface{} {
|
||||
|
||||
// Set sets the map entry for key to val,
|
||||
// and returns the previous entry, if any.
|
||||
func (m *M) Set(key types.Type, value interface{}) (prev interface{}) {
|
||||
func (m *Map) Set(key types.Type, value interface{}) (prev interface{}) {
|
||||
if m.table != nil {
|
||||
hash := m.hasher.Hash(key)
|
||||
bucket := m.table[hash]
|
||||
@ -129,7 +130,7 @@ func (m *M) Set(key types.Type, value interface{}) (prev interface{}) {
|
||||
}
|
||||
|
||||
// Len returns the number of map entries.
|
||||
func (m *M) Len() int {
|
||||
func (m *Map) Len() int {
|
||||
if m != nil {
|
||||
return m.length
|
||||
}
|
||||
@ -144,7 +145,7 @@ func (m *M) Len() int {
|
||||
// Iterate has not yet reached, whether or not f will be invoked for
|
||||
// it is unspecified.
|
||||
//
|
||||
func (m *M) Iterate(f func(key types.Type, value interface{})) {
|
||||
func (m *Map) Iterate(f func(key types.Type, value interface{})) {
|
||||
if m != nil {
|
||||
for _, bucket := range m.table {
|
||||
for _, e := range bucket {
|
||||
@ -158,7 +159,7 @@ func (m *M) Iterate(f func(key types.Type, value interface{})) {
|
||||
|
||||
// Keys returns a new slice containing the set of map keys.
|
||||
// The order is unspecified.
|
||||
func (m *M) Keys() []types.Type {
|
||||
func (m *Map) Keys() []types.Type {
|
||||
keys := make([]types.Type, 0, m.Len())
|
||||
m.Iterate(func(key types.Type, _ interface{}) {
|
||||
keys = append(keys, key)
|
||||
@ -166,7 +167,7 @@ func (m *M) Keys() []types.Type {
|
||||
return keys
|
||||
}
|
||||
|
||||
func (m *M) toString(values bool) string {
|
||||
func (m *Map) toString(values bool) string {
|
||||
if m == nil {
|
||||
return "{}"
|
||||
}
|
||||
@ -189,14 +190,14 @@ func (m *M) toString(values bool) string {
|
||||
// Values are printed using fmt.Sprintf("%v", v).
|
||||
// Order is unspecified.
|
||||
//
|
||||
func (m *M) String() string {
|
||||
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 *M) KeysString() string {
|
||||
func (m *Map) KeysString() string {
|
||||
return m.toString(false)
|
||||
}
|
||||
|
@ -1,4 +1,8 @@
|
||||
package typemap_test
|
||||
// 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_test
|
||||
|
||||
// TODO(adonovan):
|
||||
// - test use of explicit hasher across two maps.
|
||||
@ -9,7 +13,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
"code.google.com/p/go.tools/go/types/typemap"
|
||||
"code.google.com/p/go.tools/go/types/typeutil"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -35,8 +39,8 @@ func TestAxioms(t *testing.T) {
|
||||
checkEqualButNotIdentical(t, tChanInt1, tChanInt2, "tChanInt{1,2}")
|
||||
}
|
||||
|
||||
func TestTypeMap(t *testing.T) {
|
||||
var tmap *typemap.M
|
||||
func TestMap(t *testing.T) {
|
||||
var tmap *typeutil.Map
|
||||
|
||||
// All methods but Set are safe on on (*T)(nil).
|
||||
tmap.Len()
|
||||
@ -45,23 +49,23 @@ func TestTypeMap(t *testing.T) {
|
||||
tmap.KeysString()
|
||||
tmap.String()
|
||||
|
||||
tmap = new(typemap.M)
|
||||
tmap = new(typeutil.Map)
|
||||
|
||||
// Length of empty map.
|
||||
if l := tmap.Len(); l != 0 {
|
||||
t.Errorf("Len() on empty typemap: got %d, want 0", l)
|
||||
t.Errorf("Len() on empty Map: got %d, want 0", l)
|
||||
}
|
||||
// At of missing key.
|
||||
if v := tmap.At(tPStr1); v != nil {
|
||||
t.Errorf("At() on empty typemap: got %v, want nil", v)
|
||||
t.Errorf("At() on empty Map: got %v, want nil", v)
|
||||
}
|
||||
// Deletion of missing key.
|
||||
if tmap.Delete(tPStr1) {
|
||||
t.Errorf("Delete() on empty typemap: got true, want false")
|
||||
t.Errorf("Delete() on empty Map: got true, want false")
|
||||
}
|
||||
// Set of new key.
|
||||
if prev := tmap.Set(tPStr1, "*string"); prev != nil {
|
||||
t.Errorf("Set() on empty map returned non-nil previous value %s", prev)
|
||||
t.Errorf("Set() on empty Map returned non-nil previous value %s", prev)
|
||||
}
|
||||
|
||||
// Now: {*string: "*string"}
|
38
go/types/typeutil/ui.go
Normal file
38
go/types/typeutil/ui.go
Normal file
@ -0,0 +1,38 @@
|
||||
// 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
|
||||
|
||||
// This file defines utilities for user interfaces that display types.
|
||||
|
||||
import "code.google.com/p/go.tools/go/types"
|
||||
|
||||
// IntuitiveMethodSet returns the intuitive method set of a type, T.
|
||||
//
|
||||
// The result contains MethodSet(T) and additionally, if T is a
|
||||
// concrete type, methods belonging to *T if there is no identically
|
||||
// named method on T itself. This corresponds to user intuition about
|
||||
// method sets; this function is intended only for user interfaces.
|
||||
//
|
||||
// The order of the result is as for types.MethodSet(T).
|
||||
//
|
||||
func IntuitiveMethodSet(T types.Type, msets *types.MethodSetCache) []*types.Selection {
|
||||
var result []*types.Selection
|
||||
mset := msets.MethodSet(T)
|
||||
if _, ok := T.Underlying().(*types.Interface); ok {
|
||||
for i, n := 0, mset.Len(); i < n; i++ {
|
||||
result = append(result, mset.At(i))
|
||||
}
|
||||
} else {
|
||||
pmset := msets.MethodSet(types.NewPointer(T))
|
||||
for i, n := 0, pmset.Len(); i < n; i++ {
|
||||
meth := pmset.At(i)
|
||||
if m := mset.Lookup(meth.Obj().Pkg(), meth.Obj().Name()); m != nil {
|
||||
meth = m
|
||||
}
|
||||
result = append(result, meth)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
@ -15,8 +15,8 @@ import (
|
||||
"code.google.com/p/go.tools/astutil"
|
||||
"code.google.com/p/go.tools/go/exact"
|
||||
"code.google.com/p/go.tools/go/loader"
|
||||
"code.google.com/p/go.tools/go/ssa"
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
"code.google.com/p/go.tools/go/types/typeutil"
|
||||
"code.google.com/p/go.tools/oracle/serial"
|
||||
)
|
||||
|
||||
@ -712,7 +712,7 @@ func pathToString(path []ast.Node) string {
|
||||
|
||||
func accessibleMethods(t types.Type, from *types.Package) []*types.Selection {
|
||||
var methods []*types.Selection
|
||||
for _, meth := range ssa.IntuitiveMethodSet(t, nil) {
|
||||
for _, meth := range typeutil.IntuitiveMethodSet(t, nil) {
|
||||
if isAccessibleFrom(meth.Obj(), from) {
|
||||
methods = append(methods, meth)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user