1
0
mirror of https://github.com/golang/go synced 2024-10-01 12:38:31 -06:00
go/ssa/promote.go
Robert Griesemer 64ea46e0bc go.tools/go/types: replace Method w/ Selection
A Method corresponds to a MethodVal Selection;
so the explicit Method object is not needed anymore.

- moved Selection code into separate file
- implemented Selection.String()
- improved and more consistent documentation

R=adonovan
CC=golang-dev
https://golang.org/cl/11950043
2013-07-26 22:27:48 -07:00

376 lines
10 KiB
Go

package ssa
// This file defines utilities for method-set computation including
// synthesis of wrapper methods.
//
// Wrappers include:
// - indirection/promotion wrappers for methods of embedded fields.
// - interface method wrappers for closures of I.f.
// - bound method wrappers, for uncalled obj.Method closures.
// TODO(adonovan): rename to wrappers.go.
import (
"fmt"
"go/token"
"code.google.com/p/go.tools/go/types"
)
// recvType returns the receiver type of method obj.
func recvType(obj *types.Func) types.Type {
return obj.Type().(*types.Signature).Recv().Type()
}
// 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.
//
// This function should only be called when you need to construct the
// entire method set, synthesizing all wrappers, for example during
// the processing of a MakeInterface instruction or when visiting all
// reachable functions.
//
// If you only need to look up a single method (obj), avoid this
// function and use LookupMethod instead:
//
// meth := types.MethodSet(typ).Lookup(pkg, name)
// m := prog.MethodSet(typ)[meth.Id()] // don't do this
// m := prog.LookupMethod(meth) // use this instead
//
// If you only need to enumerate the keys, use types.MethodSet
// instead.
//
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
//
// Thread-safe.
//
func (prog *Program) MethodSet(typ types.Type) MethodSet {
return prog.populateMethodSet(typ, nil)
}
// populateMethodSet returns the method set for typ, ensuring that it
// contains at least the function for meth, if that is a key.
// If meth is nil, the entire method set is populated.
//
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
//
func (prog *Program) populateMethodSet(typ types.Type, meth *types.Selection) MethodSet {
tmset := methodSet(typ)
n := tmset.Len()
if n == 0 {
return nil
}
if prog.mode&LogSource != 0 {
defer logStack("populateMethodSet %s meth=%v", typ, meth)()
}
prog.methodsMu.Lock()
defer prog.methodsMu.Unlock()
mset, _ := prog.methodSets.At(typ).(MethodSet)
if mset == nil {
mset = make(MethodSet)
prog.methodSets.Set(typ, mset)
}
if len(mset) < n {
if meth != nil { // single method
id := meth.Obj().Id()
if mset[id] == nil {
mset[id] = findMethod(prog, meth)
}
} else {
// complete set
for i := 0; i < n; i++ {
meth := tmset.At(i)
if id := meth.Obj().Id(); mset[id] == nil {
mset[id] = findMethod(prog, meth)
}
}
}
}
return mset
}
func methodSet(typ types.Type) *types.MethodSet {
// TODO(adonovan): temporary workaround. Inline it away when fixed.
if _, ok := deref(typ).Underlying().(*types.Interface); ok && isPointer(typ) {
// TODO(gri): fix: go/types bug: pointer-to-interface
// has no methods---yet go/types says it has!
return new(types.MethodSet)
}
return typ.MethodSet()
}
// LookupMethod returns the Function for the specified method object,
// building wrapper methods on demand. It returns nil if the typ has
// no such method.
//
// Thread-safe.
//
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
//
func (prog *Program) LookupMethod(meth *types.Selection) *Function {
return prog.populateMethodSet(meth.Recv(), meth)[meth.Obj().Id()]
}
// concreteMethod returns the concrete method denoted by obj.
// Panic ensues if there is no such method (e.g. it's a standalone
// function).
//
func (prog *Program) concreteMethod(obj *types.Func) *Function {
fn := prog.concreteMethods[obj]
if fn == nil {
panic("no concrete method: " + obj.String())
}
return fn
}
// findMethod returns the concrete Function for the method meth,
// synthesizing wrappers as needed.
//
// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
//
func findMethod(prog *Program, meth *types.Selection) *Function {
needsPromotion := len(meth.Index()) > 1
mfunc := meth.Obj().(*types.Func)
needsIndirection := !isPointer(recvType(mfunc)) && isPointer(meth.Recv())
if needsPromotion || needsIndirection {
return makeWrapper(prog, meth.Recv(), meth)
}
if _, ok := meth.Recv().Underlying().(*types.Interface); ok {
return interfaceMethodWrapper(prog, meth.Recv(), mfunc)
}
return prog.concreteMethod(mfunc)
}
// makeWrapper returns a synthetic wrapper Function that optionally
// performs receiver indirection, implicit field selections and then a
// tailcall of a "promoted" method. For example, given these decls:
//
// type A struct {B}
// type B struct {*C}
// type C ...
// func (*C) f()
//
// then makeWrapper(typ=A, obj={Func:(*C).f, Indices=[B,C,f]})
// 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 wrapper method. obj is the
// type-checker's object for the promoted method; its Func may be a
// concrete or an interface method.
//
// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
//
func makeWrapper(prog *Program, typ types.Type, meth *types.Selection) *Function {
mfunc := meth.Obj().(*types.Func)
old := mfunc.Type().(*types.Signature)
sig := types.NewSignature(nil, types.NewVar(token.NoPos, nil, "recv", typ), old.Params(), old.Results(), old.IsVariadic())
description := fmt.Sprintf("wrapper for %s", mfunc)
if prog.mode&LogSource != 0 {
defer logStack("make %s to (%s)", description, typ)()
}
fn := &Function{
name: mfunc.Name(),
method: meth,
Signature: sig,
Synthetic: description,
Prog: prog,
pos: mfunc.Pos(),
}
fn.startBody()
fn.addSpilledParam(sig.Recv())
createParams(fn)
var v Value = fn.Locals[0] // spilled receiver
if isPointer(typ) {
// TODO(adonovan): consider emitting a nil-pointer check here
// with a nice error message, like gc does.
v = emitLoad(fn, v)
}
// Invariant: v is a pointer, either
// value of *A receiver param, or
// address of A spilled receiver.
// We use pointer arithmetic (FieldAddr possibly followed by
// Load) in preference to value extraction (Field possibly
// preceded by Load).
indices := meth.Index()
v = emitImplicitSelections(fn, v, indices[:len(indices)-1])
// Invariant: v is a pointer, either
// value of implicit *C field, or
// address of implicit C field.
var c Call
if _, ok := old.Recv().Type().Underlying().(*types.Interface); !ok { // concrete method
if !isPointer(old.Recv().Type()) {
v = emitLoad(fn, v)
}
c.Call.Value = prog.concreteMethod(mfunc)
c.Call.Args = append(c.Call.Args, v)
} else {
c.Call.Method = mfunc
c.Call.Value = emitLoad(fn, v)
}
for _, arg := range fn.Params[1:] {
c.Call.Args = append(c.Call.Args, arg)
}
emitTailCall(fn, &c)
fn.finishBody()
return fn
}
// createParams creates parameters for wrapper method fn based on its
// Signature.Params, which do not include the receiver.
//
func createParams(fn *Function) {
var last *Parameter
tparams := fn.Signature.Params()
for i, n := 0, tparams.Len(); i < n; i++ {
last = fn.addParamObj(tparams.At(i))
}
if fn.Signature.IsVariadic() {
last.typ = types.NewSlice(last.typ)
}
}
// Wrappers for standalone interface methods ----------------------------------
// interfaceMethodWrapper returns a synthetic wrapper function
// permitting an abstract method obj to be called like a standalone
// function, e.g.:
//
// type I interface { f(x int) R }
// m := I.f // wrapper
// var i I
// m(i, 0)
//
// The wrapper is defined as if by:
//
// func I.f(i I, x int, ...) R {
// return i.f(x, ...)
// }
//
// typ is the type of the receiver (I here). It isn't necessarily
// equal to the recvType(obj) because one interface may embed another.
// TODO(adonovan): more tests.
//
// TODO(adonovan): opt: currently the stub is created even when used
// in call position: I.f(i, 0). Clearly this is suboptimal.
//
// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
//
func interfaceMethodWrapper(prog *Program, typ types.Type, obj *types.Func) *Function {
// 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[obj]
if !ok {
description := fmt.Sprintf("interface method wrapper for %s.%s", typ, obj)
if prog.mode&LogSource != 0 {
defer logStack("%s", description)()
}
fn = &Function{
name: obj.Name(),
object: obj,
Signature: obj.Type().(*types.Signature),
Synthetic: description,
pos: obj.Pos(),
Prog: prog,
}
fn.startBody()
fn.addParam("recv", typ, token.NoPos)
createParams(fn)
var c Call
c.Call.Method = obj
c.Call.Value = fn.Params[0]
for _, arg := range fn.Params[1:] {
c.Call.Args = append(c.Call.Args, arg)
}
emitTailCall(fn, &c)
fn.finishBody()
prog.ifaceMethodWrappers[obj] = fn
}
return fn
}
// Wrappers for bound methods -------------------------------------------------
// boundMethodWrapper returns a synthetic wrapper function that
// delegates to a concrete or interface 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.:
//
// type T int or: type T interface { meth() }
// func (t T) meth()
// var t T
// f := t.meth
// f() // calls t.meth()
//
// f is a closure of a synthetic wrapper defined as if by:
//
// f := func() { return t.meth() }
//
// EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu)
//
func boundMethodWrapper(prog *Program, obj *types.Func) *Function {
prog.methodsMu.Lock()
defer prog.methodsMu.Unlock()
fn, ok := prog.boundMethodWrappers[obj]
if !ok {
description := fmt.Sprintf("bound method wrapper for %s", obj)
if prog.mode&LogSource != 0 {
defer logStack("%s", description)()
}
s := obj.Type().(*types.Signature)
fn = &Function{
name: "bound$" + obj.FullName(),
Signature: types.NewSignature(nil, nil, s.Params(), s.Results(), s.IsVariadic()),
Synthetic: description,
Prog: prog,
pos: obj.Pos(),
}
cap := &Capture{name: "recv", typ: recvType(obj), parent: fn}
fn.FreeVars = []*Capture{cap}
fn.startBody()
createParams(fn)
var c Call
if _, ok := recvType(obj).Underlying().(*types.Interface); !ok { // concrete
c.Call.Value = prog.concreteMethod(obj)
c.Call.Args = []Value{cap}
} else {
c.Call.Value = cap
c.Call.Method = obj
}
for _, arg := range fn.Params {
c.Call.Args = append(c.Call.Args, arg)
}
emitTailCall(fn, &c)
fn.finishBody()
prog.boundMethodWrappers[obj] = fn
}
return fn
}