1
0
mirror of https://github.com/golang/go synced 2024-11-19 03:44:40 -07:00
go/ssa/promote.go
Alan Donovan 2a3a12930b go.tools/ssa: add test of SSA construction on $GOROOT/src/pkg/...
stdlib_test runs the builder (in sanity-checking mode) over
the Go standard library.  It also prints some stats about
the time and memory usage.

Also:
- importer.LoadPackage too (not just doImport) must consult
  the cache to avoid creating duplicate Package instances for
  the same import path when called serially from a test.
- importer: skip empty directories without an error.
- importer: print all errors, not just the first.
- visit.go: added AllFunctions utility for enumerating all
  Functions in a Program.
- ssa.MethodSet is not safe to expose from the package since
  it must be accessed under an (inaccessible) lock.  (!!!)
  This CL makes it unexported and restricts its use to the
  single function Program.LookupMethod().
- Program.MethodSet() has gone.
  Clients should instead iterate over the types.MethodSet
  and call LookupMethod.
- Package.DumpTo(): improved efficiency of methodset printing
  (by not creating wrappers) and accuracy (by showing * on
  receiver type only when necessary).
- Program.CreatePackage: documented precondition and added
  assertion.

R=gri
CC=golang-dev
https://golang.org/cl/12058048
2013-07-30 14:28:14 -04:00

324 lines
8.9 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()
}
func methodSetOf(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 {
if meth == nil {
panic("LookupMethod(nil)")
}
typ := meth.Recv()
if prog.mode&LogSource != 0 {
defer logStack("LookupMethod %s %v", typ, meth)()
}
prog.methodsMu.Lock()
defer prog.methodsMu.Unlock()
type methodSet map[string]*Function
mset, _ := prog.methodSets.At(typ).(methodSet)
if mset == nil {
mset = make(methodSet)
prog.methodSets.Set(typ, mset)
}
id := meth.Obj().Id()
fn := mset[id]
if fn == nil {
fn = findMethod(prog, meth)
mset[id] = fn
}
return fn
}
// declaredFunc returns the concrete function/method denoted by obj.
// Panic ensues if there is none.
//
func (prog *Program) declaredFunc(obj *types.Func) *Function {
if v := prog.packageLevelValue(obj); v != nil {
return v.(*Function)
}
panic("no concrete method: " + obj.String())
}
// 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
obj := meth.Obj().(*types.Func)
needsIndirection := !isPointer(recvType(obj)) && 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(), obj)
}
return prog.declaredFunc(obj)
}
// 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 {
obj := meth.Obj().(*types.Func)
old := obj.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", obj)
if prog.mode&LogSource != 0 {
defer logStack("make %s to (%s)", description, typ)()
}
fn := &Function{
name: obj.Name(),
method: meth,
Signature: sig,
Synthetic: description,
Prog: prog,
pos: obj.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.declaredFunc(obj)
c.Call.Args = append(c.Call.Args, v)
} else {
c.Call.Method = obj
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.declaredFunc(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
}