зеркало из https://github.com/golang/tools.git
go.tools/ssa: memoize synthesis of all wrapper methods.
methodIndex() utility was split and specialized to its two cases, *Interface vs *Named, which are logically quite different. We can't memoize promotion wrappers yet; we need typemap. Terminology: - "thunks" are now "wrappers" - "bridge methods" are now "promotion wrappers" Where the diff is messy it's just because of indentation. R=gri CC=golang-dev https://golang.org/cl/10282043
This commit is contained in:
Родитель
0f26bbae8f
Коммит
f1d4d01fed
|
@ -365,7 +365,7 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
|
|||
// TypeName returns the type name for the named type t.
|
||||
func (t *Named) Obj() *TypeName { return t.obj }
|
||||
|
||||
// NumMethods returns the number of methods directly associated with named type t.
|
||||
// NumMethods returns the number of explicit methods whose receiver is named type t.
|
||||
func (t *Named) NumMethods() int { return len(t.methods) }
|
||||
|
||||
// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
|
||||
|
|
|
@ -5,7 +5,7 @@ package ssa
|
|||
// SSA construction has two phases, CREATE and BUILD. In the CREATE phase
|
||||
// (create.go), all packages are constructed and type-checked and
|
||||
// definitions of all package members are created, method-sets are
|
||||
// computed, and bridge methods are synthesized. The create phase
|
||||
// computed, and wrapper methods are synthesized. The create phase
|
||||
// proceeds in topological order over the import dependency graph,
|
||||
// initiated by client calls to CreatePackages.
|
||||
//
|
||||
|
@ -345,7 +345,7 @@ func (b *builder) selector(fn *Function, e *ast.SelectorExpr, wantAddr, escaping
|
|||
if !wantAddr {
|
||||
if m, recv := b.findMethod(fn, e.X, id); m != nil {
|
||||
c := &MakeClosure{
|
||||
Fn: makeBoundMethodThunk(fn.Prog, m, recv.Type()),
|
||||
Fn: boundMethodWrapper(m),
|
||||
Bindings: []Value{recv},
|
||||
}
|
||||
c.setPos(e.Sel.Pos())
|
||||
|
@ -743,8 +743,8 @@ func (b *builder) expr(fn *Function, e ast.Expr) Value {
|
|||
return m
|
||||
}
|
||||
|
||||
// T must be an interface; return method thunk.
|
||||
return makeImethodThunk(fn.Prog, typ, id)
|
||||
// T must be an interface; return wrapper.
|
||||
return interfaceMethodWrapper(fn.Prog, typ, id)
|
||||
}
|
||||
|
||||
// e.f where e is an expression. f may be a method.
|
||||
|
@ -905,7 +905,7 @@ func (b *builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) {
|
|||
// Case 4: x.f() where a dynamically dispatched call
|
||||
// to an interface method f. f is a 'func' object in
|
||||
// the Methods of types.Interface X
|
||||
c.Method, _ = methodIndex(t, id)
|
||||
c.Method, _ = interfaceMethodIndex(t, id)
|
||||
c.Recv = b.expr(fn, sel.X)
|
||||
|
||||
default:
|
||||
|
|
|
@ -42,6 +42,8 @@ func NewProgram(fset *token.FileSet, mode BuilderMode) *Program {
|
|||
methodSets: make(map[types.Type]MethodSet),
|
||||
concreteMethods: make(map[*types.Func]*Function),
|
||||
indirectionWrappers: make(map[*Function]*Function),
|
||||
boundMethodWrappers: make(map[*Function]*Function),
|
||||
ifaceMethodWrappers: make(map[*types.Func]*Function),
|
||||
mode: mode,
|
||||
}
|
||||
|
||||
|
@ -130,8 +132,9 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) {
|
|||
pkg.Members[name] = fn
|
||||
} else {
|
||||
// Method declaration.
|
||||
nt := recv.Type().Deref().(*types.Named)
|
||||
_, method := methodIndex(nt, MakeId(name, pkg.Types))
|
||||
_, method := namedTypeMethodIndex(
|
||||
recv.Type().Deref().(*types.Named),
|
||||
MakeId(name, pkg.Types))
|
||||
pkg.Prog.concreteMethods[method] = fn
|
||||
}
|
||||
|
||||
|
|
|
@ -286,7 +286,7 @@ func emitTypeTest(f *Function, x Value, t types.Type, pos token.Pos) Value {
|
|||
|
||||
// emitTailCall emits to f a function call in tail position. The
|
||||
// caller is responsible for all fields of 'call' except its type.
|
||||
// Intended for delegating bridge methods.
|
||||
// Intended for wrapper methods.
|
||||
// Precondition: f does/will not use deferred procedure calls.
|
||||
// Postcondition: f.currentBlock is nil.
|
||||
//
|
||||
|
|
12
ssa/func.go
12
ssa/func.go
|
@ -443,10 +443,10 @@ func (f *Function) emit(instr Instruction) Value {
|
|||
// "math.IsNaN" // a package-level function
|
||||
// "IsNaN" // intra-package reference to same
|
||||
// "(*sync.WaitGroup).Add" // a declared method
|
||||
// "(*exp/ssa.Ret).Block" // a bridge method
|
||||
// "(ssa.Instruction).Block" // an interface method thunk
|
||||
// "(*exp/ssa.Ret).Block" // a promotion wrapper method
|
||||
// "(ssa.Instruction).Block" // an interface method wrapper
|
||||
// "func@5.32" // an anonymous function
|
||||
// "bound$(*T).f" // a bound method thunk
|
||||
// "bound$(*T).f" // a bound method wrapper
|
||||
//
|
||||
func (f *Function) FullName() string {
|
||||
return f.fullName(nil)
|
||||
|
@ -467,11 +467,11 @@ func (f *Function) fullName(from *Package) string {
|
|||
if f.Pkg == nil {
|
||||
var recvType types.Type
|
||||
if recv != nil {
|
||||
recvType = recv.Type() // bridge method
|
||||
recvType = recv.Type() // promotion wrapper
|
||||
} else if strings.HasPrefix(f.name, "bound$") {
|
||||
return f.name // bound method thunk
|
||||
return f.name // bound method wrapper
|
||||
} else {
|
||||
recvType = f.Params[0].Type() // interface method thunk
|
||||
recvType = f.Params[0].Type() // interface method wrapper
|
||||
}
|
||||
return fmt.Sprintf("(%s).%s", recvType, f.name)
|
||||
}
|
||||
|
|
|
@ -385,7 +385,7 @@ func (p *Package) DumpTo(w io.Writer) {
|
|||
// We display only mset(*T) since its keys
|
||||
// are a superset of mset(T)'s keys, though the
|
||||
// methods themselves may differ,
|
||||
// e.g. different bridge methods.
|
||||
// e.g. promotion wrappers.
|
||||
// NB: if mem.Type() is a pointer, mset is empty.
|
||||
mset := p.Prog.MethodSet(pointer(mem.Type()))
|
||||
var keys ids
|
||||
|
|
184
ssa/promote.go
184
ssa/promote.go
|
@ -1,9 +1,15 @@
|
|||
package ssa
|
||||
|
||||
// This file defines algorithms related to "promotion" of field and
|
||||
// method selector expressions e.x, such as desugaring implicit field
|
||||
// and method selections, method-set computation, and construction of
|
||||
// synthetic "bridge" methods.
|
||||
// This file defines utilities for method-set computation, synthesis
|
||||
// of wrapper methods, and desugaring of implicit field selections.
|
||||
//
|
||||
// Wrappers include:
|
||||
// - promotion wrappers for methods of embedded fields.
|
||||
// - interface method wrappers for closures of I.f.
|
||||
// - bound method wrappers, for uncalled obj.Method closures.
|
||||
// - indirection wrappers, for calls to T-methods on a *T receiver.
|
||||
|
||||
// TODO(adonovan): rename to methods.go.
|
||||
|
||||
import (
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
|
@ -90,9 +96,9 @@ func (c candidate) ptrRecv() bool {
|
|||
return c.concrete != nil && isPointer(c.concrete.Signature.Recv().Type())
|
||||
}
|
||||
|
||||
// MethodSet returns the method set for type typ,
|
||||
// building bridge methods as needed for promoted methods
|
||||
// and indirection wrappers for *T receiver types.
|
||||
// MethodSet returns the method set for type typ, building wrapper
|
||||
// methods as needed for embedded field promotion, and indirection for
|
||||
// *T receiver types, etc.
|
||||
// A nil result indicates an empty set.
|
||||
//
|
||||
// Thread-safe.
|
||||
|
@ -101,8 +107,8 @@ func (p *Program) MethodSet(typ types.Type) MethodSet {
|
|||
return nil
|
||||
}
|
||||
|
||||
p.methodSetsMu.Lock()
|
||||
defer p.methodSetsMu.Unlock()
|
||||
p.methodsMu.Lock()
|
||||
defer p.methodsMu.Unlock()
|
||||
|
||||
// TODO(adonovan): Using Types as map keys doesn't properly
|
||||
// de-dup. e.g. *Named are canonical but *Struct and
|
||||
|
@ -200,7 +206,7 @@ func buildMethodSet(prog *Program, typ types.Type) MethodSet {
|
|||
list, next = next, list[:0] // reuse array
|
||||
}
|
||||
|
||||
// Build method sets and bridge methods.
|
||||
// Build method sets and wrapper methods.
|
||||
mset := make(MethodSet)
|
||||
for id, cand := range cands {
|
||||
if cand == nil {
|
||||
|
@ -223,7 +229,7 @@ func buildMethodSet(prog *Program, typ types.Type) MethodSet {
|
|||
method = indirectionWrapper(method)
|
||||
}
|
||||
} else {
|
||||
method = makeBridgeMethod(prog, typ, cand)
|
||||
method = promotionWrapper(prog, typ, cand)
|
||||
}
|
||||
if method == nil {
|
||||
panic("unexpected nil method in method set")
|
||||
|
@ -251,7 +257,7 @@ func addCandidate(m map[Id]*candidate, id Id, method *types.Func, concrete *Func
|
|||
}
|
||||
}
|
||||
|
||||
// makeBridgeMethod creates a synthetic Function that delegates to a
|
||||
// promotionWrapper returns a synthetic Function that delegates to a
|
||||
// "promoted" method. For example, given these decls:
|
||||
//
|
||||
// type A struct {B}
|
||||
|
@ -259,24 +265,25 @@ func addCandidate(m map[Id]*candidate, id Id, method *types.Func, concrete *Func
|
|||
// type C ...
|
||||
// func (*C) f()
|
||||
//
|
||||
// then makeBridgeMethod(typ=A, cand={method:(*C).f, path:[B,*C]}) will
|
||||
// synthesize this bridge method:
|
||||
// then promotionWrapper(typ=A, cand={method:(*C).f, path:[B,*C]}) will
|
||||
// synthesize this wrapper method:
|
||||
//
|
||||
// func (a A) f() { return a.B.C->f() }
|
||||
//
|
||||
// prog is the program to which the synthesized method will belong.
|
||||
// typ is the receiver type of the bridge method. cand is the
|
||||
// typ is the receiver type of the wrapper method. cand is the
|
||||
// candidate method to be promoted; it may be concrete or an interface
|
||||
// method.
|
||||
//
|
||||
func makeBridgeMethod(prog *Program, typ types.Type, cand *candidate) *Function {
|
||||
func promotionWrapper(prog *Program, typ types.Type, cand *candidate) *Function {
|
||||
old := cand.method.Type().(*types.Signature)
|
||||
sig := types.NewSignature(types.NewVar(token.NoPos, nil, "recv", typ), old.Params(), old.Results(), old.IsVariadic())
|
||||
|
||||
// TODO(adonovan): consult memoization cache keyed by (typ, cand).
|
||||
// Needs typemap. Also needs hash/eq functions for 'candidate'.
|
||||
if prog.mode&LogSource != 0 {
|
||||
defer logStack("makeBridgeMethod %s, %s, type %s", typ, cand, sig)()
|
||||
defer logStack("promotionWrapper %s, %s, type %s", typ, cand, sig)()
|
||||
}
|
||||
|
||||
fn := &Function{
|
||||
name: cand.method.Name(),
|
||||
Signature: sig,
|
||||
|
@ -286,7 +293,7 @@ func makeBridgeMethod(prog *Program, typ types.Type, cand *candidate) *Function
|
|||
fn.addSpilledParam(sig.Recv())
|
||||
createParams(fn)
|
||||
|
||||
// Each bridge method performs a sequence of selections,
|
||||
// Each promotion wrapper performs a sequence of selections,
|
||||
// then tailcalls the promoted method.
|
||||
// We use pointer arithmetic (FieldAddr possibly followed by
|
||||
// Load) in preference to value extraction (Field possibly
|
||||
|
@ -320,14 +327,9 @@ func makeBridgeMethod(prog *Program, typ types.Type, cand *candidate) *Function
|
|||
c.Call.Func = cand.concrete
|
||||
c.Call.Args = append(c.Call.Args, v)
|
||||
} else {
|
||||
c.Call.Method = -1
|
||||
iface := v.Type().Underlying().(*types.Interface)
|
||||
for i, n := 0, iface.NumMethods(); i < n; i++ {
|
||||
if iface.Method(i) == cand.method {
|
||||
c.Call.Method = i
|
||||
break
|
||||
}
|
||||
}
|
||||
id := MakeId(cand.method.Name(), cand.method.Pkg())
|
||||
c.Call.Method, _ = interfaceMethodIndex(iface, id)
|
||||
c.Call.Recv = v
|
||||
}
|
||||
for _, arg := range fn.Params[1:] {
|
||||
|
@ -338,7 +340,7 @@ func makeBridgeMethod(prog *Program, typ types.Type, cand *candidate) *Function
|
|||
return fn
|
||||
}
|
||||
|
||||
// createParams creates parameters for bridge method fn based on its
|
||||
// createParams creates parameters for wrapper method fn based on its
|
||||
// Signature.Params, which do not include the receiver.
|
||||
//
|
||||
func createParams(fn *Function) {
|
||||
|
@ -352,18 +354,18 @@ func createParams(fn *Function) {
|
|||
}
|
||||
}
|
||||
|
||||
// Thunks for standalone interface methods ----------------------------------------
|
||||
// Wrappers for standalone interface methods ----------------------------------
|
||||
|
||||
// makeImethodThunk returns a synthetic thunk function permitting a
|
||||
// interfaceMethodWrapper returns a synthetic wrapper function permitting a
|
||||
// method id of interface typ to be called like a standalone function,
|
||||
// e.g.:
|
||||
//
|
||||
// type I interface { f(x int) R }
|
||||
// m := I.f // thunk
|
||||
// m := I.f // wrapper
|
||||
// var i I
|
||||
// m(i, 0)
|
||||
//
|
||||
// The thunk is defined as if by:
|
||||
// The wrapper is defined as if by:
|
||||
//
|
||||
// func I.f(i I, x int, ...) R {
|
||||
// return i.f(x, ...)
|
||||
|
@ -372,39 +374,49 @@ func createParams(fn *Function) {
|
|||
// TODO(adonovan): opt: currently the stub is created even when used
|
||||
// in call position: I.f(i, 0). Clearly this is suboptimal.
|
||||
//
|
||||
// TODO(adonovan): memoize creation of these functions in the Program.
|
||||
// EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu)
|
||||
//
|
||||
func makeImethodThunk(prog *Program, typ types.Type, id Id) *Function {
|
||||
if prog.mode&LogSource != 0 {
|
||||
defer logStack("makeImethodThunk %s.%s", typ, id)()
|
||||
func interfaceMethodWrapper(prog *Program, typ types.Type, id Id) *Function {
|
||||
index, meth := interfaceMethodIndex(typ.Underlying().(*types.Interface), id)
|
||||
prog.methodsMu.Lock()
|
||||
defer prog.methodsMu.Unlock()
|
||||
// If one interface embeds another they'll share the same
|
||||
// wrappers for common methods. This is safe, but it might
|
||||
// confuse some tools because of the implicit interface
|
||||
// conversion applied to the first argument. If this becomes
|
||||
// a problem, we should include 'typ' in the memoization key.
|
||||
fn, ok := prog.ifaceMethodWrappers[meth]
|
||||
if !ok {
|
||||
if prog.mode&LogSource != 0 {
|
||||
defer logStack("interfaceMethodWrapper %s.%s", typ, id)()
|
||||
}
|
||||
fn = &Function{
|
||||
name: meth.Name(),
|
||||
Signature: meth.Type().(*types.Signature),
|
||||
Prog: prog,
|
||||
}
|
||||
fn.startBody()
|
||||
fn.addParam("recv", typ, token.NoPos)
|
||||
createParams(fn)
|
||||
var c Call
|
||||
c.Call.Method = index
|
||||
c.Call.Recv = fn.Params[0]
|
||||
for _, arg := range fn.Params[1:] {
|
||||
c.Call.Args = append(c.Call.Args, arg)
|
||||
}
|
||||
emitTailCall(fn, &c)
|
||||
fn.finishBody()
|
||||
|
||||
prog.ifaceMethodWrappers[meth] = fn
|
||||
}
|
||||
itf := typ.Underlying().(*types.Interface)
|
||||
index, meth := methodIndex(itf, id)
|
||||
sig := *meth.Type().(*types.Signature) // copy; shared Values
|
||||
fn := &Function{
|
||||
name: meth.Name(),
|
||||
Signature: &sig,
|
||||
Prog: prog,
|
||||
}
|
||||
fn.startBody()
|
||||
fn.addParam("recv", typ, token.NoPos)
|
||||
createParams(fn)
|
||||
var c Call
|
||||
c.Call.Method = index
|
||||
c.Call.Recv = fn.Params[0]
|
||||
for _, arg := range fn.Params[1:] {
|
||||
c.Call.Args = append(c.Call.Args, arg)
|
||||
}
|
||||
emitTailCall(fn, &c)
|
||||
fn.finishBody()
|
||||
return fn
|
||||
}
|
||||
|
||||
// Thunks for bound methods ----------------------------------------
|
||||
// Wrappers for bound methods -------------------------------------------------
|
||||
|
||||
// makeBoundMethodThunk returns a synthetic thunk function that
|
||||
// delegates to a concrete method. The thunk has one free variable,
|
||||
// the method's receiver. Use MakeClosure with such a thunk to
|
||||
// boundMethodWrapper returns a synthetic wrapper function that
|
||||
// delegates to a concrete method. The wrapper has one free variable,
|
||||
// the method's receiver. Use MakeClosure with such a wrapper to
|
||||
// construct a bound-method closure.
|
||||
// e.g.:
|
||||
//
|
||||
|
@ -414,35 +426,43 @@ func makeImethodThunk(prog *Program, typ types.Type, id Id) *Function {
|
|||
// f := t.meth
|
||||
// f() // calls t.meth()
|
||||
//
|
||||
// f is a closure of a synthetic thunk defined as if by:
|
||||
// f is a closure of a synthetic wrapper defined as if by:
|
||||
//
|
||||
// f := func() { return t.meth() }
|
||||
//
|
||||
// TODO(adonovan): memoize creation of these functions in the Program.
|
||||
// EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu)
|
||||
//
|
||||
func makeBoundMethodThunk(prog *Program, meth *Function, recvType types.Type) *Function {
|
||||
if prog.mode&LogSource != 0 {
|
||||
defer logStack("makeBoundMethodThunk %s", meth)()
|
||||
}
|
||||
s := meth.Signature
|
||||
fn := &Function{
|
||||
name: "bound$" + meth.FullName(),
|
||||
Signature: types.NewSignature(nil, s.Params(), s.Results(), s.IsVariadic()), // drop recv
|
||||
Prog: prog,
|
||||
}
|
||||
func boundMethodWrapper(meth *Function) *Function {
|
||||
prog := meth.Prog
|
||||
prog.methodsMu.Lock()
|
||||
defer prog.methodsMu.Unlock()
|
||||
fn, ok := prog.boundMethodWrappers[meth]
|
||||
if !ok {
|
||||
if prog.mode&LogSource != 0 {
|
||||
defer logStack("boundMethodWrapper %s", meth)()
|
||||
}
|
||||
s := meth.Signature
|
||||
fn = &Function{
|
||||
name: "bound$" + meth.FullName(),
|
||||
Signature: types.NewSignature(nil, s.Params(), s.Results(), s.IsVariadic()), // drop recv
|
||||
Prog: prog,
|
||||
}
|
||||
|
||||
cap := &Capture{name: "recv", typ: recvType, parent: fn}
|
||||
fn.FreeVars = []*Capture{cap}
|
||||
fn.startBody()
|
||||
createParams(fn)
|
||||
var c Call
|
||||
c.Call.Func = meth
|
||||
c.Call.Args = []Value{cap}
|
||||
for _, arg := range fn.Params {
|
||||
c.Call.Args = append(c.Call.Args, arg)
|
||||
cap := &Capture{name: "recv", typ: s.Recv().Type(), parent: fn}
|
||||
fn.FreeVars = []*Capture{cap}
|
||||
fn.startBody()
|
||||
createParams(fn)
|
||||
var c Call
|
||||
c.Call.Func = meth
|
||||
c.Call.Args = []Value{cap}
|
||||
for _, arg := range fn.Params {
|
||||
c.Call.Args = append(c.Call.Args, arg)
|
||||
}
|
||||
emitTailCall(fn, &c)
|
||||
fn.finishBody()
|
||||
|
||||
prog.boundMethodWrappers[meth] = fn
|
||||
}
|
||||
emitTailCall(fn, &c)
|
||||
fn.finishBody()
|
||||
return fn
|
||||
}
|
||||
|
||||
|
@ -455,7 +475,7 @@ func makeBoundMethodThunk(prog *Program, meth *Function, recvType types.Type) *F
|
|||
// return (*recv).f(...)
|
||||
// }
|
||||
//
|
||||
// EXCLUSIVE_LOCKS_REQUIRED(meth.Prog.methodSetsMu)
|
||||
// EXCLUSIVE_LOCKS_REQUIRED(meth.Prog.methodsMu)
|
||||
//
|
||||
func indirectionWrapper(meth *Function) *Function {
|
||||
prog := meth.Prog
|
||||
|
|
|
@ -24,9 +24,11 @@ type Program struct {
|
|||
concreteMethods map[*types.Func]*Function // maps named concrete methods to their code
|
||||
mode BuilderMode // set of mode bits for SSA construction
|
||||
|
||||
methodSetsMu sync.Mutex // guards methodSets, indirectionWrappers
|
||||
methodSets map[types.Type]MethodSet // concrete method sets for all needed types [TODO(adonovan): de-dup]
|
||||
indirectionWrappers map[*Function]*Function // func(*T) wrappers for T-methods
|
||||
methodsMu sync.Mutex // guards the following maps:
|
||||
methodSets map[types.Type]MethodSet // concrete method set each type [TODO(adonovan): de-dup]
|
||||
indirectionWrappers map[*Function]*Function // func(*T) wrappers for T-methods
|
||||
boundMethodWrappers map[*Function]*Function // wrappers for curried x.Method closures
|
||||
ifaceMethodWrappers map[*types.Func]*Function // wrappers for curried I.Method functions
|
||||
}
|
||||
|
||||
// A Package is a single analyzed Go package containing Members for
|
||||
|
|
34
ssa/util.go
34
ssa/util.go
|
@ -55,17 +55,31 @@ func pointer(typ types.Type) *types.Pointer {
|
|||
return types.NewPointer(typ)
|
||||
}
|
||||
|
||||
// methodIndex returns the method (and its index) named id within the
|
||||
// method table of named or interface type typ. If not found,
|
||||
// panic ensues.
|
||||
// namedTypeMethodIndex returns the method (and its index) named id
|
||||
// within the set of explicitly declared concrete methods of named
|
||||
// type typ. If not found, panic ensues.
|
||||
//
|
||||
func methodIndex(typ types.Type, id Id) (int, *types.Func) {
|
||||
t := typ.(interface {
|
||||
NumMethods() int
|
||||
Method(i int) *types.Func
|
||||
})
|
||||
for i, n := 0, t.NumMethods(); i < n; i++ {
|
||||
m := t.Method(i)
|
||||
// TODO(gri): move this functionality into the go/types API?
|
||||
//
|
||||
func namedTypeMethodIndex(typ *types.Named, id Id) (int, *types.Func) {
|
||||
for i, n := 0, typ.NumMethods(); i < n; i++ {
|
||||
m := typ.Method(i)
|
||||
if MakeId(m.Name(), m.Pkg()) == id {
|
||||
return i, m
|
||||
}
|
||||
}
|
||||
panic(fmt.Sprint("method not found: ", id, " in named type ", typ))
|
||||
}
|
||||
|
||||
// interfaceMethodIndex returns the method (and its index) named id
|
||||
// within the method-set of interface type typ. If not found, panic
|
||||
// ensues.
|
||||
//
|
||||
// TODO(gri): move this functionality into the go/types API.
|
||||
//
|
||||
func interfaceMethodIndex(typ *types.Interface, id Id) (int, *types.Func) {
|
||||
for i, n := 0, typ.NumMethods(); i < n; i++ {
|
||||
m := typ.Method(i)
|
||||
if MakeId(m.Name(), m.Pkg()) == id {
|
||||
return i, m
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче