1
0
mirror of https://github.com/golang/go synced 2024-09-30 12:08:32 -06:00

go.tools/ssa: add support for bound-method closures.

Extracted Builder.findMethod function to handle
methodset/receiver logic common to
function calls (Builder.setCall) and
bound method closure creation (Builder.selector).

Capture: added explicit Name, Type fields to Capture instead
of relying on Outer field, which is now un-exported since its
only purpose is to let Builder.expr(case *ast.FuncLit) know
which values to put in the closure; it is nilled immediately
after.

Simplified Function.lookup() logic: there's no need to walk
the Outer chain each time to set Alloc.Heap=true, as it's
already set during creation of the outermost
Capture{outer:*Alloc}.

Added interp/testdata/boundmeth.go test.

Cosmetic changes:
- add support for bound method thunks to Function.FullName().
- Simplified {Literal,Global,Builtin,Function}.String()
- doc: Captures are no longer necessarily addresses.
- added yet another missing pair of "()" (go/types accessors).
- print "Synthetic" not "Declared at -" for synthetic functions.
- use '$' not center-dot in synthetic identifiers (easier to type).

R=gri
CC=golang-dev
https://golang.org/cl/9654043
This commit is contained in:
Alan Donovan 2013-05-22 17:56:18 -04:00
parent bf87b9f0f5
commit 8cdf1f1cb1
10 changed files with 259 additions and 93 deletions

View File

@ -449,6 +449,20 @@ func (b *Builder) builtin(fn *Function, name string, args []ast.Expr, typ types.
// //
func (b *Builder) selector(fn *Function, e *ast.SelectorExpr, wantAddr, escaping bool) Value { func (b *Builder) selector(fn *Function, e *ast.SelectorExpr, wantAddr, escaping bool) Value {
id := MakeId(e.Sel.Name, fn.Pkg.Types) id := MakeId(e.Sel.Name, fn.Pkg.Types)
// Bound method closure? (e.m where m is a method)
if !wantAddr {
if m, recv := b.findMethod(fn, e.X, id); m != nil {
c := &MakeClosure{
Fn: makeBoundMethodThunk(b.Prog, m, recv),
Bindings: []Value{recv},
}
c.setPos(e.Sel.Pos())
c.setType(fn.Pkg.TypeOf(e))
return fn.emit(c)
}
}
st := fn.Pkg.TypeOf(e.X).Deref().Underlying().(*types.Struct) st := fn.Pkg.TypeOf(e.X).Deref().Underlying().(*types.Struct)
index := -1 index := -1
for i, n := 0, st.NumFields(); i < n; i++ { for i, n := 0, st.NumFields(); i < n; i++ {
@ -704,7 +718,8 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value {
v := &MakeClosure{Fn: fn2} v := &MakeClosure{Fn: fn2}
v.setType(fn.Pkg.TypeOf(e)) v.setType(fn.Pkg.TypeOf(e))
for _, fv := range fn2.FreeVars { for _, fv := range fn2.FreeVars {
v.Bindings = append(v.Bindings, fv.Outer) v.Bindings = append(v.Bindings, fv.outer)
fv.outer = nil
} }
return fn.emit(v) return fn.emit(v)
@ -836,7 +851,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value {
return makeImethodThunk(b.Prog, typ, id) return makeImethodThunk(b.Prog, typ, id)
} }
// e.f where e is an expression. // e.f where e is an expression. f may be a method.
return b.selector(fn, e, false, false) return b.selector(fn, e, false, false)
case *ast.IndexExpr: case *ast.IndexExpr:
@ -894,6 +909,41 @@ func (b *Builder) stmtList(fn *Function, list []ast.Stmt) {
} }
} }
// findMethod returns the method and receiver for a call base.id().
// It locates the method using the method-set for base's type,
// and emits code for the receiver, handling the cases where
// the formal and actual parameter's pointerness are unequal.
//
// findMethod returns (nil, nil) if no such method was found.
//
func (b *Builder) findMethod(fn *Function, base ast.Expr, id Id) (*Function, Value) {
typ := fn.Pkg.TypeOf(base)
// Consult method-set of X.
if m := b.Prog.MethodSet(typ)[id]; m != nil {
aptr := isPointer(typ)
fptr := isPointer(m.Signature.Recv().Type())
if aptr == fptr {
// Actual's and formal's "pointerness" match.
return m, b.expr(fn, base)
}
// Actual is a pointer, formal is not.
// Load a copy.
return m, emitLoad(fn, b.expr(fn, base))
}
if !isPointer(typ) {
// Consult method-set of *X.
if m := b.Prog.MethodSet(pointer(typ))[id]; m != nil {
// A method found only in MS(*X) must have a
// pointer formal receiver; but the actual
// value is not a pointer.
// Implicit & -- possibly escaping.
return m, b.addr(fn, base, true).(address).addr
}
}
return nil, nil
}
// setCallFunc populates the function parts of a CallCommon structure // setCallFunc populates the function parts of a CallCommon structure
// (Func, Method, Recv, Args[0]) based on the kind of invocation // (Func, Method, Recv, Args[0]) based on the kind of invocation
// occurring in e. // occurring in e.
@ -934,45 +984,20 @@ func (b *Builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) {
return return
} }
id := MakeId(sel.Sel.Name, fn.Pkg.Types)
// Let X be the type of x. // Let X be the type of x.
typ := fn.Pkg.TypeOf(sel.X)
// Case 2: x.f(): a statically dispatched call to a method // Case 2: x.f(): a statically dispatched call to a method
// from the method-set of X or perhaps *X (if x is addressable // from the method-set of X or perhaps *X (if x is addressable
// but not a pointer). // but not a pointer).
id := MakeId(sel.Sel.Name, fn.Pkg.Types) if m, recv := b.findMethod(fn, sel.X, id); m != nil {
// Consult method-set of X.
if m := b.Prog.MethodSet(typ)[id]; m != nil {
var recv Value
aptr := isPointer(typ)
fptr := isPointer(m.Signature.Recv().Type())
if aptr == fptr {
// Actual's and formal's "pointerness" match.
recv = b.expr(fn, sel.X)
} else {
// Actual is a pointer, formal is not.
// Load a copy.
recv = emitLoad(fn, b.expr(fn, sel.X))
}
c.Func = m c.Func = m
c.Args = append(c.Args, recv) c.Args = append(c.Args, recv)
return return
} }
if !isPointer(typ) {
// Consult method-set of *X.
if m := b.Prog.MethodSet(pointer(typ))[id]; m != nil {
// A method found only in MS(*X) must have a
// pointer formal receiver; but the actual
// value is not a pointer.
// Implicit & -- possibly escaping.
recv := b.addr(fn, sel.X, true).(address).addr
c.Func = m
c.Args = append(c.Args, recv)
return
}
}
switch t := typ.Underlying().(type) { switch t := fn.Pkg.TypeOf(sel.X).Underlying().(type) {
case *types.Struct, *types.Pointer: case *types.Struct, *types.Pointer:
// Case 3: x.f() where x.f is a function value in a // Case 3: x.f() where x.f is a function value in a
// struct field f; not a method call. f is a 'var' // struct field f; not a method call. f is a 'var'
@ -2562,7 +2587,7 @@ func (b *Builder) createPackageImpl(typkg *types.Package, importPath string, fil
// Add initializer guard variable. // Add initializer guard variable.
initguard := &Global{ initguard := &Global{
Pkg: p, Pkg: p,
Name_: "init·guard", Name_: "init$guard",
Type_: pointer(tBool), Type_: pointer(tBool),
} }
p.Members[initguard.Name()] = initguard p.Members[initguard.Name()] = initguard
@ -2671,13 +2696,13 @@ func (b *Builder) BuildPackage(p *Package) {
return // nothing to do return // nothing to do
} }
if b.Context.Mode&LogSource != 0 { if b.Context.Mode&LogSource != 0 {
defer logStack("build package %s", p.Types.Path)() defer logStack("build package %s", p.Types.Path())()
} }
init := p.Init init := p.Init
init.startBody() init.startBody()
// Make init() skip if package is already initialized. // Make init() skip if package is already initialized.
initguard := p.Var("init·guard") initguard := p.Var("init$guard")
doinit := init.newBasicBlock("init.start") doinit := init.newBasicBlock("init.start")
done := init.newBasicBlock("init.done") done := init.newBasicBlock("init.done")
emitIf(init, emitLoad(init, initguard), done, doinit) emitIf(init, emitLoad(init, initguard), done, doinit)

View File

@ -280,16 +280,13 @@ func emitTypeTest(f *Function, x Value, t types.Type) Value {
return f.emit(a) return f.emit(a)
} }
// emitTailCall emits to f a function call in tail position, // emitTailCall emits to f a function call in tail position. The
// passing on all but the first formal parameter to f as actual // caller is responsible for all fields of 'call' except its type.
// values in the call. Intended for delegating bridge methods. // Intended for delegating bridge methods.
// Precondition: f does/will not use deferred procedure calls. // Precondition: f does/will not use deferred procedure calls.
// Postcondition: f.currentBlock is nil. // Postcondition: f.currentBlock is nil.
// //
func emitTailCall(f *Function, call *Call) { func emitTailCall(f *Function, call *Call) {
for _, arg := range f.Params[1:] {
call.Call.Args = append(call.Call.Args, arg)
}
tresults := f.Signature.Results() tresults := f.Signature.Results()
nr := tresults.Len() nr := tresults.Len()
if nr == 1 { if nr == 1 {

View File

@ -71,18 +71,18 @@ func main() {
// Output: // Output:
// //
// Package main: // Package main:
// var init·guard *bool // var init$guard *bool
// func main func() // func main func()
// const message message = "Hello, World!":untyped string // const message message = "Hello, World!":untyped string
// //
// # Name: main.init // # Name: main.init
// # Declared at - // # Synthetic
// func init(): // func init():
// .0.entry: P:0 S:2 // .0.entry: P:0 S:2
// t0 = *init·guard bool // t0 = *init$guard bool
// if t0 goto 2.init.done else 1.init.start // if t0 goto 2.init.done else 1.init.start
// .1.init.start: P:1 S:1 // .1.init.start: P:1 S:1
// *init·guard = true:bool // *init$guard = true:bool
// t1 = fmt.init() () // t1 = fmt.init() ()
// jump 2.init.done // jump 2.init.done
// .2.init.done: P:2 S:0 // .2.init.done: P:2 S:0

View File

@ -8,6 +8,7 @@ import (
"go/token" "go/token"
"io" "io"
"os" "os"
"strings"
"code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/go/types"
) )
@ -395,19 +396,8 @@ func (f *Function) addLocal(typ types.Type, pos token.Pos) *Alloc {
// //
func (f *Function) lookup(obj types.Object, escaping bool) Value { func (f *Function) lookup(obj types.Object, escaping bool) Value {
if v, ok := f.objects[obj]; ok { if v, ok := f.objects[obj]; ok {
if escaping { if alloc, ok := v.(*Alloc); ok && escaping {
// Walk up the chain of Captures. alloc.Heap = true
x := v
for {
if c, ok := x.(*Capture); ok {
x = c.Outer
} else {
break
}
}
// By construction, all captures are ultimately Allocs in the
// naive SSA form. Parameters are pre-spilled to the stack.
x.(*Alloc).Heap = true
} }
return v // function-local var (address) return v // function-local var (address)
} }
@ -417,7 +407,12 @@ func (f *Function) lookup(obj types.Object, escaping bool) Value {
if f.Enclosing == nil { if f.Enclosing == nil {
panic("no Value for type.Object " + obj.Name()) panic("no Value for type.Object " + obj.Name())
} }
v := &Capture{Outer: f.Enclosing.lookup(obj, true)} // escaping outer := f.Enclosing.lookup(obj, true) // escaping
v := &Capture{
Name_: outer.Name(),
Type_: outer.Type(),
outer: outer,
}
f.objects[obj] = v f.objects[obj] = v
f.FreeVars = append(f.FreeVars, v) f.FreeVars = append(f.FreeVars, v)
return v return v
@ -442,6 +437,7 @@ func (f *Function) emit(instr Instruction) Value {
// "(*exp/ssa.Ret).Block" // a bridge method // "(*exp/ssa.Ret).Block" // a bridge method
// "(ssa.Instruction).Block" // an interface method thunk // "(ssa.Instruction).Block" // an interface method thunk
// "func@5.32" // an anonymous function // "func@5.32" // an anonymous function
// "bound$(*T).f" // a bound method thunk
// //
func (f *Function) FullName() string { func (f *Function) FullName() string {
return f.fullName(nil) return f.fullName(nil)
@ -449,6 +445,8 @@ func (f *Function) FullName() string {
// Like FullName, but if from==f.Pkg, suppress package qualification. // Like FullName, but if from==f.Pkg, suppress package qualification.
func (f *Function) fullName(from *Package) string { func (f *Function) fullName(from *Package) string {
// TODO(adonovan): expose less fragile case discrimination.
// Anonymous? // Anonymous?
if f.Enclosing != nil { if f.Enclosing != nil {
return f.Name_ return f.Name_
@ -461,6 +459,8 @@ func (f *Function) fullName(from *Package) string {
var recvType types.Type var recvType types.Type
if recv != nil { if recv != nil {
recvType = recv.Type() // bridge method recvType = recv.Type() // bridge method
} else if strings.HasPrefix(f.Name_, "bound$") {
return f.Name_ // bound method thunk
} else { } else {
recvType = f.Params[0].Type() // interface method thunk recvType = f.Params[0].Type() // interface method thunk
} }
@ -527,7 +527,11 @@ func writeSignature(w io.Writer, name string, sig *types.Signature, params []*Pa
// //
func (f *Function) DumpTo(w io.Writer) { func (f *Function) DumpTo(w io.Writer) {
fmt.Fprintf(w, "# Name: %s\n", f.FullName()) fmt.Fprintf(w, "# Name: %s\n", f.FullName())
fmt.Fprintf(w, "# Declared at %s\n", f.Prog.Files.Position(f.Pos())) if pos := f.Pos(); pos.IsValid() {
fmt.Fprintf(w, "# Declared at %s\n", f.Prog.Files.Position(pos))
} else {
fmt.Fprintln(w, "# Synthetic")
}
if f.Enclosing != nil { if f.Enclosing != nil {
fmt.Fprintf(w, "# Parent: %s\n", f.Enclosing.Name()) fmt.Fprintf(w, "# Parent: %s\n", f.Enclosing.Name())

View File

@ -130,6 +130,7 @@ var gorootTests = []string{
var testdataTests = []string{ var testdataTests = []string{
"coverage.go", "coverage.go",
"mrvchain.go", "mrvchain.go",
"boundmeth.go",
} }
func run(t *testing.T, dir, input string) bool { func run(t *testing.T, dir, input string) bool {

88
ssa/interp/testdata/boundmeth.go vendored Normal file
View File

@ -0,0 +1,88 @@
// Tests of bound method closures.
package main
func assert(b bool) {
if !b {
panic("oops")
}
}
type I int
func (i I) add(x int) int {
return int(i) + x
}
func valueReceiver() {
var three I = 3
assert(three.add(5) == 8)
var add3 func(int) int = three.add
assert(add3(5) == 8)
}
type S struct{ x int }
func (s *S) incr() {
s.x++
}
func (s *S) get() int {
return s.x
}
func pointerReceiver() {
ps := new(S)
incr := ps.incr
get := ps.get
assert(get() == 0)
incr()
incr()
incr()
assert(get() == 3)
}
func addressibleValuePointerReceiver() {
var s S
incr := s.incr
get := s.get
assert(get() == 0)
incr()
incr()
incr()
assert(get() == 3)
}
type S2 struct {
S
}
func promotedReceiver() {
var s2 S2
incr := s2.incr
get := s2.get
assert(get() == 0)
incr()
incr()
incr()
assert(get() == 3)
}
func anonStruct() {
var s struct{ S }
incr := s.incr
get := s.get
assert(get() == 0)
incr()
incr()
incr()
assert(get() == 3)
}
func main() {
valueReceiver()
pointerReceiver()
addressibleValuePointerReceiver()
promotedReceiver()
anonStruct()
}

View File

@ -48,7 +48,7 @@ func relName(v Value, i Instruction) string {
// It never appears in disassembly, which uses Value.Name(). // It never appears in disassembly, which uses Value.Name().
func (v *Literal) String() string { func (v *Literal) String() string {
return fmt.Sprintf("literal %s", v.Name()) return v.Name()
} }
func (v *Parameter) String() string { func (v *Parameter) String() string {
@ -60,15 +60,15 @@ func (v *Capture) String() string {
} }
func (v *Global) String() string { func (v *Global) String() string {
return fmt.Sprintf("global %s : %s", v.Name(), v.Type()) return v.FullName()
} }
func (v *Builtin) String() string { func (v *Builtin) String() string {
return fmt.Sprintf("builtin %s : %s", v.Name(), v.Type()) return fmt.Sprintf("builtin %s", v.Name())
} }
func (v *Function) String() string { func (v *Function) String() string {
return fmt.Sprintf("function %s : %s", v.Name(), v.Type()) return v.FullName()
} }
// FullName returns g's package-qualified name. // FullName returns g's package-qualified name.

View File

@ -105,9 +105,7 @@ func (p *Program) MethodSet(typ types.Type) MethodSet {
// TODO(adonovan): Using Types as map keys doesn't properly // TODO(adonovan): Using Types as map keys doesn't properly
// de-dup. e.g. *NamedType are canonical but *Struct and // de-dup. e.g. *NamedType are canonical but *Struct and
// others are not. Need to de-dup based on using a two-level // others are not. Need to de-dup using typemap.T.
// hash-table with hash function types.Type.String and
// equivalence relation types.IsIdentical.
mset := p.methodSets[typ] mset := p.methodSets[typ]
if mset == nil { if mset == nil {
mset = buildMethodSet(p, typ) mset = buildMethodSet(p, typ)
@ -308,13 +306,14 @@ func makeBridgeMethod(prog *Program, typ types.Type, cand *candidate) *Function
var c Call var c Call
if cand.concrete != nil { if cand.concrete != nil {
c.Call.Func = cand.concrete c.Call.Func = cand.concrete
fn.pos = c.Call.Func.(*Function).pos // TODO(adonovan): fix: wrong.
c.Call.pos = fn.pos // TODO(adonovan): fix: wrong.
c.Call.Args = append(c.Call.Args, v) c.Call.Args = append(c.Call.Args, v)
} else { } else {
c.Call.Recv = v c.Call.Recv = v
c.Call.Method = 0 c.Call.Method = 0
} }
for _, arg := range fn.Params[1:] {
c.Call.Args = append(c.Call.Args, arg)
}
emitTailCall(fn, &c) emitTailCall(fn, &c)
fn.finishBody() fn.finishBody()
return fn return fn
@ -339,7 +338,7 @@ func createParams(fn *Function) {
// Thunks for standalone interface methods ---------------------------------------- // Thunks for standalone interface methods ----------------------------------------
// makeImethodThunk returns a synthetic thunk function permitting an // makeImethodThunk returns a synthetic thunk function permitting a
// method id of interface typ to be called like a standalone function, // method id of interface typ to be called like a standalone function,
// e.g.: // e.g.:
// //
@ -354,11 +353,6 @@ func createParams(fn *Function) {
// return i.f(x, ...) // return i.f(x, ...)
// } // }
// //
// The generated thunks do not belong to any package. (Arguably they
// belong in the package that defines the interface, but we have no
// way to determine that on demand; we'd have to create all possible
// thunks a priori.)
//
// TODO(adonovan): opt: currently the stub is created even when used // TODO(adonovan): opt: currently the stub is created even when used
// in call position: I.f(i, 0). Clearly this is suboptimal. // in call position: I.f(i, 0). Clearly this is suboptimal.
// //
@ -376,13 +370,61 @@ func makeImethodThunk(prog *Program, typ types.Type, id Id) *Function {
Signature: &sig, Signature: &sig,
Prog: prog, Prog: prog,
} }
// TODO(adonovan): set fn.Pos to location of interface method ast.Field.
fn.startBody() fn.startBody()
fn.addParam("recv", typ) fn.addParam("recv", typ)
createParams(fn) createParams(fn)
var c Call var c Call
c.Call.Method = index c.Call.Method = index
c.Call.Recv = fn.Params[0] 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 ----------------------------------------
// 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
// construct a bound-method closure.
// e.g.:
//
// type T int
// func (t T) meth()
// var t T
// f := t.meth
// f() // calls t.meth()
//
// f is a closure of a synthetic thunk defined as if by:
//
// f := func() { return t.meth() }
//
// TODO(adonovan): memoize creation of these functions in the Program.
//
func makeBoundMethodThunk(prog *Program, meth *Function, recv Value) *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,
}
cap := &Capture{Name_: "recv", Type_: recv.Type()}
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) emitTailCall(fn, &c)
fn.finishBody() fn.finishBody()
return fn return fn

View File

@ -242,7 +242,7 @@ type Instruction interface {
// local variables ("free variables") has Capture parameters. Such // local variables ("free variables") has Capture parameters. Such
// functions cannot be called directly but require a value created by // functions cannot be called directly but require a value created by
// MakeClosure which, via its Bindings, supplies values for these // MakeClosure which, via its Bindings, supplies values for these
// parameters. Captures are always addresses. // parameters.
// //
// If the function is a method (Signature.Recv() != nil) then the first // If the function is a method (Signature.Recv() != nil) then the first
// element of Params is the receiver parameter. // element of Params is the receiver parameter.
@ -306,15 +306,26 @@ type BasicBlock struct {
// Pure values ---------------------------------------- // Pure values ----------------------------------------
// A Capture is a pointer to a lexically enclosing local variable. // A Capture represents a free variable of the function to which it
// belongs.
// //
// The referent of a capture is an Alloc or another Capture and is // Captures are used to implement anonymous functions, whose free
// always considered potentially escaping, so Captures are always // variables are lexically captured in a closure formed by
// addresses in the heap, and have pointer types. // MakeClosure. The referent of such a capture is an Alloc or another
// Capture and is considered a potentially escaping heap address, with
// pointer type.
//
// Captures are also used to implement bound method closures. Such a
// capture represents the receiver value and may be of any type that
// has concrete methods.
// //
type Capture struct { type Capture struct {
Outer Value // the Value captured from the enclosing context. Name_ string
Type_ types.Type
referrers []Instruction referrers []Instruction
// Transiently needed during building.
outer Value // the Value captured from the enclosing context.
} }
// A Parameter represents an input parameter of a function. // A Parameter represents an input parameter of a function.
@ -587,20 +598,17 @@ type MakeInterface struct {
Methods MethodSet // method set of (non-interface) X Methods MethodSet // method set of (non-interface) X
} }
// The MakeClosure instruction yields an anonymous function value // The MakeClosure instruction yields a closure value whose code is
// whose code is Fn and whose lexical capture slots are populated by // Fn and whose free variables' values are supplied by Bindings.
// Bindings.
//
// By construction, all captured variables are addresses of variables
// allocated with 'new', i.e. Alloc(Heap=true).
// //
// Type() returns a (possibly named) *types.Signature. // Type() returns a (possibly named) *types.Signature.
// //
// Pos() returns the ast.FuncLit.Type.Func of the function literal // Pos() returns the ast.FuncLit.Type.Func for a function literal
// that created this closure. // closure or the ast.SelectorExpr.Sel for a bound method closure.
// //
// Example printed form: // Example printed form:
// t0 = make closure anon@1.2 [x y z] // t0 = make closure anon@1.2 [x y z]
// t1 = make closure bound$(main.I).add [i]
// //
type MakeClosure struct { type MakeClosure struct {
Register Register
@ -1217,8 +1225,8 @@ func (v *Builtin) Type() types.Type { return v.Object.Type() }
func (v *Builtin) Name() string { return v.Object.Name() } func (v *Builtin) Name() string { return v.Object.Name() }
func (*Builtin) Referrers() *[]Instruction { return nil } func (*Builtin) Referrers() *[]Instruction { return nil }
func (v *Capture) Type() types.Type { return v.Outer.Type() } func (v *Capture) Type() types.Type { return v.Type_ }
func (v *Capture) Name() string { return v.Outer.Name() } func (v *Capture) Name() string { return v.Name_ }
func (v *Capture) Referrers() *[]Instruction { return &v.referrers } func (v *Capture) Referrers() *[]Instruction { return &v.referrers }
func (v *Global) Type() types.Type { return v.Type_ } func (v *Global) Type() types.Type { return v.Type_ }

View File

@ -53,6 +53,7 @@ func isPointer(typ types.Type) bool {
} }
// pointer(typ) returns the type that is a pointer to typ. // pointer(typ) returns the type that is a pointer to typ.
// TODO(adonovan): inline and eliminate.
func pointer(typ types.Type) *types.Pointer { func pointer(typ types.Type) *types.Pointer {
return types.NewPointer(typ) return types.NewPointer(typ)
} }