mirror of
https://github.com/golang/go
synced 2024-11-18 04:24:47 -07: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:
parent
bf87b9f0f5
commit
8cdf1f1cb1
@ -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 {
|
||||
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)
|
||||
index := -1
|
||||
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.setType(fn.Pkg.TypeOf(e))
|
||||
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)
|
||||
|
||||
@ -836,7 +851,7 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value {
|
||||
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)
|
||||
|
||||
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
|
||||
// (Func, Method, Recv, Args[0]) based on the kind of invocation
|
||||
// occurring in e.
|
||||
@ -934,45 +984,20 @@ func (b *Builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) {
|
||||
return
|
||||
}
|
||||
|
||||
id := MakeId(sel.Sel.Name, fn.Pkg.Types)
|
||||
|
||||
// Let X be the type of x.
|
||||
typ := fn.Pkg.TypeOf(sel.X)
|
||||
|
||||
// Case 2: x.f(): a statically dispatched call to a method
|
||||
// from the method-set of X or perhaps *X (if x is addressable
|
||||
// but not a pointer).
|
||||
id := MakeId(sel.Sel.Name, fn.Pkg.Types)
|
||||
// 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))
|
||||
}
|
||||
if m, recv := b.findMethod(fn, sel.X, id); m != nil {
|
||||
c.Func = m
|
||||
c.Args = append(c.Args, recv)
|
||||
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 3: x.f() where x.f is a function value in a
|
||||
// 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.
|
||||
initguard := &Global{
|
||||
Pkg: p,
|
||||
Name_: "init·guard",
|
||||
Name_: "init$guard",
|
||||
Type_: pointer(tBool),
|
||||
}
|
||||
p.Members[initguard.Name()] = initguard
|
||||
@ -2671,13 +2696,13 @@ func (b *Builder) BuildPackage(p *Package) {
|
||||
return // nothing to do
|
||||
}
|
||||
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.startBody()
|
||||
|
||||
// Make init() skip if package is already initialized.
|
||||
initguard := p.Var("init·guard")
|
||||
initguard := p.Var("init$guard")
|
||||
doinit := init.newBasicBlock("init.start")
|
||||
done := init.newBasicBlock("init.done")
|
||||
emitIf(init, emitLoad(init, initguard), done, doinit)
|
||||
|
@ -280,16 +280,13 @@ func emitTypeTest(f *Function, x Value, t types.Type) Value {
|
||||
return f.emit(a)
|
||||
}
|
||||
|
||||
// emitTailCall emits to f a function call in tail position,
|
||||
// passing on all but the first formal parameter to f as actual
|
||||
// values in the call. Intended for delegating bridge methods.
|
||||
// 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.
|
||||
// Precondition: f does/will not use deferred procedure calls.
|
||||
// Postcondition: f.currentBlock is nil.
|
||||
//
|
||||
func emitTailCall(f *Function, call *Call) {
|
||||
for _, arg := range f.Params[1:] {
|
||||
call.Call.Args = append(call.Call.Args, arg)
|
||||
}
|
||||
tresults := f.Signature.Results()
|
||||
nr := tresults.Len()
|
||||
if nr == 1 {
|
||||
|
@ -71,18 +71,18 @@ func main() {
|
||||
// Output:
|
||||
//
|
||||
// Package main:
|
||||
// var init·guard *bool
|
||||
// var init$guard *bool
|
||||
// func main func()
|
||||
// const message message = "Hello, World!":untyped string
|
||||
//
|
||||
// # Name: main.init
|
||||
// # Declared at -
|
||||
// # Synthetic
|
||||
// func init():
|
||||
// .0.entry: P:0 S:2
|
||||
// t0 = *init·guard bool
|
||||
// t0 = *init$guard bool
|
||||
// if t0 goto 2.init.done else 1.init.start
|
||||
// .1.init.start: P:1 S:1
|
||||
// *init·guard = true:bool
|
||||
// *init$guard = true:bool
|
||||
// t1 = fmt.init() ()
|
||||
// jump 2.init.done
|
||||
// .2.init.done: P:2 S:0
|
||||
|
34
ssa/func.go
34
ssa/func.go
@ -8,6 +8,7 @@ import (
|
||||
"go/token"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"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 {
|
||||
if v, ok := f.objects[obj]; ok {
|
||||
if escaping {
|
||||
// Walk up the chain of Captures.
|
||||
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
|
||||
if alloc, ok := v.(*Alloc); ok && escaping {
|
||||
alloc.Heap = true
|
||||
}
|
||||
return v // function-local var (address)
|
||||
}
|
||||
@ -417,7 +407,12 @@ func (f *Function) lookup(obj types.Object, escaping bool) Value {
|
||||
if f.Enclosing == nil {
|
||||
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.FreeVars = append(f.FreeVars, v)
|
||||
return v
|
||||
@ -442,6 +437,7 @@ func (f *Function) emit(instr Instruction) Value {
|
||||
// "(*exp/ssa.Ret).Block" // a bridge method
|
||||
// "(ssa.Instruction).Block" // an interface method thunk
|
||||
// "func@5.32" // an anonymous function
|
||||
// "bound$(*T).f" // a bound method thunk
|
||||
//
|
||||
func (f *Function) FullName() string {
|
||||
return f.fullName(nil)
|
||||
@ -449,6 +445,8 @@ func (f *Function) FullName() string {
|
||||
|
||||
// Like FullName, but if from==f.Pkg, suppress package qualification.
|
||||
func (f *Function) fullName(from *Package) string {
|
||||
// TODO(adonovan): expose less fragile case discrimination.
|
||||
|
||||
// Anonymous?
|
||||
if f.Enclosing != nil {
|
||||
return f.Name_
|
||||
@ -461,6 +459,8 @@ func (f *Function) fullName(from *Package) string {
|
||||
var recvType types.Type
|
||||
if recv != nil {
|
||||
recvType = recv.Type() // bridge method
|
||||
} else if strings.HasPrefix(f.Name_, "bound$") {
|
||||
return f.Name_ // bound method thunk
|
||||
} else {
|
||||
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) {
|
||||
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 {
|
||||
fmt.Fprintf(w, "# Parent: %s\n", f.Enclosing.Name())
|
||||
|
@ -130,6 +130,7 @@ var gorootTests = []string{
|
||||
var testdataTests = []string{
|
||||
"coverage.go",
|
||||
"mrvchain.go",
|
||||
"boundmeth.go",
|
||||
}
|
||||
|
||||
func run(t *testing.T, dir, input string) bool {
|
||||
|
88
ssa/interp/testdata/boundmeth.go
vendored
Normal file
88
ssa/interp/testdata/boundmeth.go
vendored
Normal 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()
|
||||
}
|
@ -48,7 +48,7 @@ func relName(v Value, i Instruction) string {
|
||||
// It never appears in disassembly, which uses Value.Name().
|
||||
|
||||
func (v *Literal) String() string {
|
||||
return fmt.Sprintf("literal %s", v.Name())
|
||||
return v.Name()
|
||||
}
|
||||
|
||||
func (v *Parameter) String() string {
|
||||
@ -60,15 +60,15 @@ func (v *Capture) 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 {
|
||||
return fmt.Sprintf("builtin %s : %s", v.Name(), v.Type())
|
||||
return fmt.Sprintf("builtin %s", v.Name())
|
||||
}
|
||||
|
||||
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.
|
||||
|
@ -105,9 +105,7 @@ func (p *Program) MethodSet(typ types.Type) MethodSet {
|
||||
|
||||
// TODO(adonovan): Using Types as map keys doesn't properly
|
||||
// de-dup. e.g. *NamedType are canonical but *Struct and
|
||||
// others are not. Need to de-dup based on using a two-level
|
||||
// hash-table with hash function types.Type.String and
|
||||
// equivalence relation types.IsIdentical.
|
||||
// others are not. Need to de-dup using typemap.T.
|
||||
mset := p.methodSets[typ]
|
||||
if mset == nil {
|
||||
mset = buildMethodSet(p, typ)
|
||||
@ -308,13 +306,14 @@ func makeBridgeMethod(prog *Program, typ types.Type, cand *candidate) *Function
|
||||
var c Call
|
||||
if cand.concrete != nil {
|
||||
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)
|
||||
} else {
|
||||
c.Call.Recv = v
|
||||
c.Call.Method = 0
|
||||
}
|
||||
for _, arg := range fn.Params[1:] {
|
||||
c.Call.Args = append(c.Call.Args, arg)
|
||||
}
|
||||
emitTailCall(fn, &c)
|
||||
fn.finishBody()
|
||||
return fn
|
||||
@ -339,7 +338,7 @@ func createParams(fn *Function) {
|
||||
|
||||
// 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,
|
||||
// e.g.:
|
||||
//
|
||||
@ -354,11 +353,6 @@ func createParams(fn *Function) {
|
||||
// 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
|
||||
// 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,
|
||||
Prog: prog,
|
||||
}
|
||||
// TODO(adonovan): set fn.Pos to location of interface method ast.Field.
|
||||
fn.startBody()
|
||||
fn.addParam("recv", typ)
|
||||
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 ----------------------------------------
|
||||
|
||||
// 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)
|
||||
fn.finishBody()
|
||||
return fn
|
||||
|
40
ssa/ssa.go
40
ssa/ssa.go
@ -242,7 +242,7 @@ type Instruction interface {
|
||||
// local variables ("free variables") has Capture parameters. Such
|
||||
// functions cannot be called directly but require a value created by
|
||||
// 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
|
||||
// element of Params is the receiver parameter.
|
||||
@ -306,15 +306,26 @@ type BasicBlock struct {
|
||||
|
||||
// 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
|
||||
// always considered potentially escaping, so Captures are always
|
||||
// addresses in the heap, and have pointer types.
|
||||
// Captures are used to implement anonymous functions, whose free
|
||||
// variables are lexically captured in a closure formed by
|
||||
// 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 {
|
||||
Outer Value // the Value captured from the enclosing context.
|
||||
Name_ string
|
||||
Type_ types.Type
|
||||
referrers []Instruction
|
||||
|
||||
// Transiently needed during building.
|
||||
outer Value // the Value captured from the enclosing context.
|
||||
}
|
||||
|
||||
// A Parameter represents an input parameter of a function.
|
||||
@ -587,20 +598,17 @@ type MakeInterface struct {
|
||||
Methods MethodSet // method set of (non-interface) X
|
||||
}
|
||||
|
||||
// The MakeClosure instruction yields an anonymous function value
|
||||
// whose code is Fn and whose lexical capture slots are populated by
|
||||
// Bindings.
|
||||
//
|
||||
// By construction, all captured variables are addresses of variables
|
||||
// allocated with 'new', i.e. Alloc(Heap=true).
|
||||
// The MakeClosure instruction yields a closure value whose code is
|
||||
// Fn and whose free variables' values are supplied by Bindings.
|
||||
//
|
||||
// Type() returns a (possibly named) *types.Signature.
|
||||
//
|
||||
// Pos() returns the ast.FuncLit.Type.Func of the function literal
|
||||
// that created this closure.
|
||||
// Pos() returns the ast.FuncLit.Type.Func for a function literal
|
||||
// closure or the ast.SelectorExpr.Sel for a bound method closure.
|
||||
//
|
||||
// Example printed form:
|
||||
// t0 = make closure anon@1.2 [x y z]
|
||||
// t1 = make closure bound$(main.I).add [i]
|
||||
//
|
||||
type MakeClosure struct {
|
||||
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 (*Builtin) Referrers() *[]Instruction { return nil }
|
||||
|
||||
func (v *Capture) Type() types.Type { return v.Outer.Type() }
|
||||
func (v *Capture) Name() string { return v.Outer.Name() }
|
||||
func (v *Capture) Type() types.Type { return v.Type_ }
|
||||
func (v *Capture) Name() string { return v.Name_ }
|
||||
func (v *Capture) Referrers() *[]Instruction { return &v.referrers }
|
||||
|
||||
func (v *Global) Type() types.Type { return v.Type_ }
|
||||
|
@ -53,6 +53,7 @@ func isPointer(typ types.Type) bool {
|
||||
}
|
||||
|
||||
// pointer(typ) returns the type that is a pointer to typ.
|
||||
// TODO(adonovan): inline and eliminate.
|
||||
func pointer(typ types.Type) *types.Pointer {
|
||||
return types.NewPointer(typ)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user