go.tools/go/types: remove Type.MethodSet() method.
Method-set caching is now performed externally using a MethodSetCache (if desired), not by the Types themselves.
This a minor deoptimization due to the extra maps, but avoids a situation in which method-sets are computed and frozen prematurely. (See b/7114)
LGTM=gri
R=gri
CC=golang-codereviews
https://golang.org/cl/61430045
diff --git a/go/gcimporter/gcimporter_test.go b/go/gcimporter/gcimporter_test.go
index 37b439a..877489b 100644
--- a/go/gcimporter/gcimporter_test.go
+++ b/go/gcimporter/gcimporter_test.go
@@ -207,7 +207,7 @@
}
mutex := imports["sync"].Scope().Lookup("Mutex").(*types.TypeName).Type()
- mset := types.NewPointer(mutex).MethodSet() // methods of *sync.Mutex
+ mset := types.NewMethodSet(types.NewPointer(mutex)) // methods of *sync.Mutex
sel := mset.Lookup(nil, "Lock")
lock := sel.Obj().(*types.Func)
if got, want := lock.Pkg().Path(), "sync"; got != want {
diff --git a/go/pointer/analysis.go b/go/pointer/analysis.go
index 1995c8d..4cc310b 100644
--- a/go/pointer/analysis.go
+++ b/go/pointer/analysis.go
@@ -278,7 +278,7 @@
if reflect := a.prog.ImportedPackage("reflect"); reflect != nil {
rV := reflect.Object.Scope().Lookup("Value")
a.reflectValueObj = rV
- a.reflectValueCall = a.prog.Method(rV.Type().MethodSet().Lookup(nil, "Call"))
+ a.reflectValueCall = a.prog.Method(a.prog.MethodSets.MethodSet(rV.Type()).Lookup(nil, "Call"))
a.reflectType = reflect.Object.Scope().Lookup("Type").Type().(*types.Named)
a.reflectRtypeObj = reflect.Object.Scope().Lookup("rtype")
a.reflectRtypePtr = types.NewPointer(a.reflectRtypeObj.Type())
diff --git a/go/pointer/gen.go b/go/pointer/gen.go
index a9e1842..ce190d1 100644
--- a/go/pointer/gen.go
+++ b/go/pointer/gen.go
@@ -720,7 +720,7 @@
a.typeAssert(a.reflectRtypePtr, rtype, recv, true)
// Look up the concrete method.
- meth := a.reflectRtypePtr.MethodSet().Lookup(call.Method.Pkg(), call.Method.Name())
+ meth := a.prog.MethodSets.MethodSet(a.reflectRtypePtr).Lookup(call.Method.Pkg(), call.Method.Name())
fn := a.prog.Method(meth)
obj := a.makeFunctionObject(fn, site) // new contour for this call
@@ -1209,7 +1209,7 @@
// genMethodsOf generates nodes and constraints for all methods of type T.
func (a *analysis) genMethodsOf(T types.Type) {
- mset := T.MethodSet()
+ mset := a.prog.MethodSets.MethodSet(T)
for i, n := 0, mset.Len(); i < n; i++ {
a.valueNode(a.prog.Method(mset.At(i)))
}
diff --git a/go/pointer/reflect.go b/go/pointer/reflect.go
index 110cf06..ec71d9e 100644
--- a/go/pointer/reflect.go
+++ b/go/pointer/reflect.go
@@ -1568,7 +1568,7 @@
// We don't use Lookup(c.name) when c.name != "" to avoid
// ambiguity: >1 unexported methods could match.
- mset := T.MethodSet()
+ mset := a.prog.MethodSets.MethodSet(T)
for i, n := 0, mset.Len(); i < n; i++ {
sel := mset.At(i)
if c.name == "" || c.name == sel.Obj().Name() {
diff --git a/go/pointer/solve.go b/go/pointer/solve.go
index bfd4f99..8ed4aee 100644
--- a/go/pointer/solve.go
+++ b/go/pointer/solve.go
@@ -312,7 +312,7 @@
}
// Look up the concrete method.
- meth := tDyn.MethodSet().Lookup(c.method.Pkg(), c.method.Name())
+ meth := a.prog.MethodSets.MethodSet(tDyn).Lookup(c.method.Pkg(), c.method.Name())
if meth == nil {
panic(fmt.Sprintf("n%d: type %s has no method %s (iface=n%d)",
c.iface, tDyn, c.method, ifaceObj))
diff --git a/go/ssa/builder_test.go b/go/ssa/builder_test.go
index 03d77a0..8233224 100644
--- a/go/ssa/builder_test.go
+++ b/go/ssa/builder_test.go
@@ -104,7 +104,7 @@
if !isExt {
t.Fatalf("unexpected name type in main package: %s", mem)
}
- mset := types.NewPointer(mem.Type()).MethodSet()
+ mset := prog.MethodSets.MethodSet(types.NewPointer(mem.Type()))
for i, n := 0, mset.Len(); i < n; i++ {
m := prog.Method(mset.At(i))
// For external types, only synthetic wrappers have code.
diff --git a/go/ssa/interp/interp.go b/go/ssa/interp/interp.go
index 58194ef..19ece82 100644
--- a/go/ssa/interp/interp.go
+++ b/go/ssa/interp/interp.go
@@ -177,7 +177,7 @@
case errorType:
return i.errorMethods[meth.Id()]
}
- return i.prog.Method(typ.MethodSet().Lookup(meth.Pkg(), meth.Name()))
+ return i.prog.Method(i.prog.MethodSets.MethodSet(typ).Lookup(meth.Pkg(), meth.Name()))
}
// visitInstr interprets a single ssa.Instruction within the activation
diff --git a/go/ssa/interp/reflect.go b/go/ssa/interp/reflect.go
index a297eff..6d42fd9 100644
--- a/go/ssa/interp/reflect.go
+++ b/go/ssa/interp/reflect.go
@@ -119,7 +119,7 @@
func ext۰reflect۰rtype۰NumMethod(fr *frame, args []value) value {
// Signature: func (t reflect.rtype) int
- return args[0].(rtype).t.MethodSet().Len()
+ return fr.i.prog.MethodSets.MethodSet(args[0].(rtype).t).Len()
}
func ext۰reflect۰rtype۰NumOut(fr *frame, args []value) value {
@@ -330,7 +330,7 @@
func ext۰reflect۰Value۰NumMethod(fr *frame, args []value) value {
// Signature: func (reflect.Value) int
- return rV2T(args[0]).t.MethodSet().Len()
+ return fr.i.prog.MethodSets.MethodSet(rV2T(args[0]).t).Len()
}
func ext۰reflect۰Value۰Pointer(fr *frame, args []value) value {
diff --git a/go/ssa/print.go b/go/ssa/print.go
index 9634fdb..a902382 100644
--- a/go/ssa/print.go
+++ b/go/ssa/print.go
@@ -407,7 +407,7 @@
case *Type:
fmt.Fprintf(buf, " type %-*s %s\n",
maxname, name, types.TypeString(p.Object, mem.Type().Underlying()))
- for _, meth := range IntuitiveMethodSet(mem.Type()) {
+ for _, meth := range IntuitiveMethodSet(mem.Type(), &p.Prog.MethodSets) {
fmt.Fprintf(buf, " %s\n", types.SelectionString(p.Object, meth))
}
@@ -431,15 +431,15 @@
//
// TODO(gri): move this to go/types?
//
-func IntuitiveMethodSet(T types.Type) []*types.Selection {
+func IntuitiveMethodSet(T types.Type, msets *types.MethodSetCache) []*types.Selection {
var result []*types.Selection
- mset := T.MethodSet()
+ 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 := types.NewPointer(T).MethodSet()
+ 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 {
diff --git a/go/ssa/promote.go b/go/ssa/promote.go
index 40ba77e..5b17e66 100644
--- a/go/ssa/promote.go
+++ b/go/ssa/promote.go
@@ -54,7 +54,7 @@
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
//
func (prog *Program) makeMethods(T types.Type) bool {
- tmset := T.MethodSet()
+ tmset := prog.MethodSets.MethodSet(T)
n := tmset.Len()
if n == 0 {
return false // empty (common case)
diff --git a/go/ssa/source.go b/go/ssa/source.go
index 6dd667e..c98dae8 100644
--- a/go/ssa/source.go
+++ b/go/ssa/source.go
@@ -119,7 +119,7 @@
return mem
}
case *Type:
- mset := types.NewPointer(mem.Type()).MethodSet()
+ mset := pkg.Prog.MethodSets.MethodSet(types.NewPointer(mem.Type()))
for i, n := 0, mset.Len(); i < n; i++ {
// Don't call Program.Method: avoid creating wrappers.
obj := mset.At(i).Obj().(*types.Func)
@@ -202,7 +202,7 @@
return v.(*Function)
}
// Interface method wrapper?
- meth := recvType(obj).MethodSet().Lookup(obj.Pkg(), obj.Name())
+ meth := prog.MethodSets.MethodSet(recvType(obj)).Lookup(obj.Pkg(), obj.Name())
return prog.Method(meth)
}
diff --git a/go/ssa/ssa.go b/go/ssa/ssa.go
index 1b153c9..cfa06e5 100644
--- a/go/ssa/ssa.go
+++ b/go/ssa/ssa.go
@@ -22,10 +22,11 @@
// A Program is a partial or complete Go program converted to SSA form.
//
type Program struct {
- Fset *token.FileSet // position information for the files of this Program
- imported map[string]*Package // all importable Packages, keyed by import path
- packages map[*types.Package]*Package // all loaded Packages, keyed by object
- mode BuilderMode // set of mode bits for SSA construction
+ Fset *token.FileSet // position information for the files of this Program
+ imported map[string]*Package // all importable Packages, keyed by import path
+ packages map[*types.Package]*Package // all loaded Packages, keyed by object
+ mode BuilderMode // set of mode bits for SSA construction
+ 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
@@ -612,8 +613,8 @@
// MakeInterface constructs an instance of an interface type from a
// value of a concrete type.
//
-// Use X.Type().MethodSet() to find the method-set of X, and
-// Program.Method(m) to find the implementation of a method.
+// Use Program.MethodSets.MethodSet(X.Type()) to find the method-set
+// of X, and Program.Method(m) to find the implementation of a method.
//
// To construct the zero value of an interface type T, use:
// NewConst(exact.MakeNil(), T, pos)
diff --git a/go/ssa/ssautil/visit.go b/go/ssa/ssautil/visit.go
index f202148..6b8c5d2 100644
--- a/go/ssa/ssautil/visit.go
+++ b/go/ssa/ssautil/visit.go
@@ -42,7 +42,7 @@
}
}
for _, T := range visit.prog.TypesWithMethodSets() {
- mset := T.MethodSet()
+ mset := visit.prog.MethodSets.MethodSet(T)
for i, n := 0, mset.Len(); i < n; i++ {
visit.function(visit.prog.Method(mset.At(i)))
}
diff --git a/go/types/call.go b/go/types/call.go
index 823693d..2773e90 100644
--- a/go/types/call.go
+++ b/go/types/call.go
@@ -392,7 +392,7 @@
// TODO(gri) Consider also using a method set cache for the lifetime
// of checker once we rely on MethodSet lookup instead of individual
// lookup.
- mset := typ.MethodSet()
+ mset := NewMethodSet(typ)
if m := mset.Lookup(check.pkg, sel); m == nil || m.obj != obj {
check.dump("%s: (%s).%v -> %s", e.Pos(), typ, obj.name, m)
check.dump("%s\n", mset)
diff --git a/go/types/methodset.go b/go/types/methodset.go
index 4ecd6f5..9ded496 100644
--- a/go/types/methodset.go
+++ b/go/types/methodset.go
@@ -10,7 +10,6 @@
"bytes"
"fmt"
"sort"
- "sync"
)
// A MethodSet is an ordered set of concrete or abstract (interface) methods;
@@ -63,31 +62,11 @@
// Shared empty method set.
var emptyMethodSet MethodSet
-// A cachedMethodSet provides access to a method set
-// for a given type by computing it once on demand,
-// and then caching it for future use. Threadsafe.
-type cachedMethodSet struct {
- mset *MethodSet
- mu sync.RWMutex // protects mset
-}
-
-// Of returns the (possibly cached) method set for typ.
-// Threadsafe.
-func (c *cachedMethodSet) of(typ Type) *MethodSet {
- c.mu.RLock()
- mset := c.mset
- c.mu.RUnlock()
- if mset == nil {
- mset = NewMethodSet(typ)
- c.mu.Lock()
- c.mset = mset
- c.mu.Unlock()
- }
- return mset
-}
-
-// NewMethodSet computes the method set for the given type T.
-// It always returns a non-nil method set, even if it is empty.
+// NewMethodSet returns the method set for the given type T. It
+// always returns a non-nil method set, even if it is empty.
+//
+// A MethodSetCache handles repeat queries more efficiently.
+//
func NewMethodSet(T Type) *MethodSet {
// WARNING: The code in this function is extremely subtle - do not modify casually!
// This function and lookupFieldOrMethod should be kept in sync.
diff --git a/go/types/methodsetcache.go b/go/types/methodsetcache.go
new file mode 100644
index 0000000..5a482e9
--- /dev/null
+++ b/go/types/methodsetcache.go
@@ -0,0 +1,69 @@
+// 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.
+
+// This file implements a cache of method sets.
+
+package types
+
+import "sync"
+
+// A MethodSetCache records the method set of each type T for which
+// MethodSet(T) is called so that repeat queries are fast.
+// The zero value is a ready-to-use cache instance.
+type MethodSetCache struct {
+ mu sync.Mutex
+ named map[*Named]struct{ value, pointer *MethodSet } // method sets for named N and *N
+ others map[Type]*MethodSet // all other types
+}
+
+// MethodSet returns the method set of type T. It is thread-safe.
+//
+// If cache is nil, this function is equivalent to NewMethodSet(T).
+// Utility functions can thus expose an optional *MethodSetCache
+// parameter to clients that care about performance.
+//
+func (cache *MethodSetCache) MethodSet(T Type) *MethodSet {
+ if cache == nil {
+ return NewMethodSet(T)
+ }
+ cache.mu.Lock()
+ defer cache.mu.Unlock()
+
+ switch T := T.(type) {
+ case *Named:
+ return cache.lookupNamed(T).value
+
+ case *Pointer:
+ if N, ok := T.Elem().(*Named); ok {
+ return cache.lookupNamed(N).pointer
+ }
+ }
+
+ // all other types
+ // (The map uses pointer equivalence, not type identity.)
+ mset := cache.others[T]
+ if mset == nil {
+ mset = NewMethodSet(T)
+ if cache.others == nil {
+ cache.others = make(map[Type]*MethodSet)
+ }
+ cache.others[T] = mset
+ }
+ return mset
+}
+
+func (cache *MethodSetCache) lookupNamed(named *Named) struct{ value, pointer *MethodSet } {
+ if cache.named == nil {
+ cache.named = make(map[*Named]struct{ value, pointer *MethodSet })
+ }
+ // Avoid recomputing mset(*T) for each distinct Pointer
+ // instance whose underlying type is a named type.
+ msets, ok := cache.named[named]
+ if !ok {
+ msets.value = NewMethodSet(named)
+ msets.pointer = NewMethodSet(NewPointer(named))
+ cache.named[named] = msets
+ }
+ return msets
+}
diff --git a/go/types/type.go b/go/types/type.go
index 0a0b22e..5aeb416 100644
--- a/go/types/type.go
+++ b/go/types/type.go
@@ -14,9 +14,6 @@
// Underlying returns the underlying type of a type.
Underlying() Type
- // MethodSet returns the method set of a type.
- MethodSet() *MethodSet
-
// String returns a string representation of a type.
String() string
}
@@ -126,8 +123,7 @@
fields []*Var
tags []string // field tags; nil if there are no tags
// TODO(gri) access to offsets is not threadsafe - fix this
- offsets []int64 // field offsets in bytes, lazily initialized
- mset cachedMethodSet // method set, lazily initialized
+ offsets []int64 // field offsets in bytes, lazily initialized
}
// NewStruct returns a new struct with the given fields and corresponding field tags.
@@ -163,8 +159,7 @@
// A Pointer represents a pointer type.
type Pointer struct {
- base Type // element type
- mset cachedMethodSet // method set, lazily initialized
+ base Type // element type
}
// NewPointer returns a new pointer type for the given element (base) type.
@@ -249,8 +244,7 @@
methods []*Func // ordered list of explicitly declared methods
embeddeds []*Named // ordered list of explicitly embedded types
- allMethods []*Func // ordered list of methods declared with or embedded in this interface (TODO(gri): replace with mset)
- mset cachedMethodSet // method set for interface, lazily initialized
+ allMethods []*Func // ordered list of methods declared with or embedded in this interface (TODO(gri): replace with mset)
}
// NewInterface returns a new interface for the given methods and embedded types.
@@ -364,10 +358,9 @@
// A Named represents a named type.
type Named struct {
- obj *TypeName // corresponding declared object
- underlying Type // possibly a *Named during setup; never a *Named once set up completely
- methods []*Func // methods declared for this type (not the method set of this type)
- mset, pmset cachedMethodSet // method set for T, *T, lazily initialized
+ obj *TypeName // corresponding declared object
+ underlying Type // possibly a *Named during setup; never a *Named once set up completely
+ methods []*Func // methods declared for this type (not the method set of this type)
}
// NewNamed returns a new named type for the given type name, underlying type, and associated methods.
@@ -426,25 +419,6 @@
func (t *Chan) Underlying() Type { return t }
func (t *Named) Underlying() Type { return t.underlying }
-func (t *Basic) MethodSet() *MethodSet { return &emptyMethodSet }
-func (t *Array) MethodSet() *MethodSet { return &emptyMethodSet }
-func (t *Slice) MethodSet() *MethodSet { return &emptyMethodSet }
-func (t *Struct) MethodSet() *MethodSet { return t.mset.of(t) }
-func (t *Pointer) MethodSet() *MethodSet {
- if named, _ := t.base.(*Named); named != nil {
- // Avoid recomputing mset(*T) for each distinct Pointer
- // instance whose underlying type is a named type.
- return named.pmset.of(t)
- }
- return t.mset.of(t)
-}
-func (t *Tuple) MethodSet() *MethodSet { return &emptyMethodSet }
-func (t *Signature) MethodSet() *MethodSet { return &emptyMethodSet }
-func (t *Interface) MethodSet() *MethodSet { return t.mset.of(t) }
-func (t *Map) MethodSet() *MethodSet { return &emptyMethodSet }
-func (t *Chan) MethodSet() *MethodSet { return &emptyMethodSet }
-func (t *Named) MethodSet() *MethodSet { return t.mset.of(t) }
-
func (t *Basic) String() string { return TypeString(nil, t) }
func (t *Array) String() string { return TypeString(nil, t) }
func (t *Slice) String() string { return TypeString(nil, t) }