diff --git a/go/pointer/analysis.go b/go/pointer/analysis.go index 3b96930e..4741caf8 100644 --- a/go/pointer/analysis.go +++ b/go/pointer/analysis.go @@ -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{ diff --git a/go/pointer/api.go b/go/pointer/api.go index 6cc32ec6..3925b630 100644 --- a/go/pointer/api.go +++ b/go/pointer/api.go @@ -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() } diff --git a/go/pointer/pointer_test.go b/go/pointer/pointer_test.go index 863a2236..9f76a220 100644 --- a/go/pointer/pointer_test.go +++ b/go/pointer/pointer_test.go @@ -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] { diff --git a/go/ssa/interp/value.go b/go/ssa/interp/value.go index eb87b008..f3257b96 100644 --- a/go/ssa/interp/value.go +++ b/go/ssa/interp/value.go @@ -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 diff --git a/go/ssa/print.go b/go/ssa/print.go index a902382b..2c9a3b3a 100644 --- a/go/ssa/print.go +++ b/go/ssa/print.go @@ -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" diff --git a/go/ssa/ssa.go b/go/ssa/ssa.go index cfa06e5a..a9ace79b 100644 --- a/go/ssa/ssa.go +++ b/go/ssa/ssa.go @@ -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, diff --git a/go/types/typemap/typemap.go b/go/types/typeutil/map.go similarity index 79% rename from go/types/typemap/typemap.go rename to go/types/typeutil/map.go index 65bb63c7..010df20c 100644 --- a/go/types/typemap/typemap.go +++ b/go/types/typeutil/map.go @@ -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) } diff --git a/go/types/typemap/typemap_test.go b/go/types/typeutil/map_test.go similarity index 88% rename from go/types/typemap/typemap_test.go rename to go/types/typeutil/map_test.go index a2b5b7a4..9d2a788e 100644 --- a/go/types/typemap/typemap_test.go +++ b/go/types/typeutil/map_test.go @@ -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"} diff --git a/go/types/typeutil/ui.go b/go/types/typeutil/ui.go new file mode 100644 index 00000000..a17c1a4b --- /dev/null +++ b/go/types/typeutil/ui.go @@ -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 +} diff --git a/oracle/describe.go b/oracle/describe.go index fb99ff61..8f379def 100644 --- a/oracle/describe.go +++ b/oracle/describe.go @@ -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) }