1
0
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:
Alan Donovan 2014-02-19 13:32:36 -05:00
parent 42022f89d4
commit 03ca00ddd4
10 changed files with 113 additions and 100 deletions

View File

@ -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{

View File

@ -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()
}

View File

@ -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] {

View File

@ -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

View File

@ -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"

View File

@ -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,

View File

@ -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)
}

View File

@ -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
View 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
}

View File

@ -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)
}