Requested during CL 23431.
Change-Id: I513ae42166b3a9fcfe51231ff55c163ab672e7d2
Reviewed-on: https://go-review.googlesource.com/23485
Reviewed-by: David Chase <drchase@google.com>
// unhook them.
// make the list of pointers for the closure call.
for _, v := range func_.Func.Cvars.Slice() {
- v.Name.Param.Closure.Name.Param.Closure = v.Name.Param.Outer
- v.Name.Param.Outerexpr = oldname(v.Sym)
+ // Unlink from v1; see comment in syntax.go type Param for these fields.
+ v1 := v.Name.Defn
+ v1.Name.Param.Innermost = v.Name.Param.Outer
+
+ // If the closure usage of v is not dense,
+ // we need to make it dense; now that we're out
+ // of the function in which v appeared,
+ // look up v.Sym in the enclosing function
+ // and keep it around for use in the compiled code.
+ //
+ // That is, suppose we just finished parsing the innermost
+ // closure f4 in this code:
+ //
+ // func f() {
+ // v := 1
+ // func() { // f2
+ // use(v)
+ // func() { // f3
+ // func() { // f4
+ // use(v)
+ // }()
+ // }()
+ // }()
+ // }
+ //
+ // At this point v.Outer is f2's v; there is no f3's v.
+ // To construct the closure f4 from within f3,
+ // we need to use f3's v and in this case we need to create f3's v.
+ // We are now in the context of f3, so calling oldname(v.Sym)
+ // obtains f3's v, creating it if necessary (as it is in the example).
+ //
+ // capturevars will decide whether to use v directly or &v.
+ v.Name.Param.Outer = oldname(v.Sym)
}
return func_
func typecheckclosure(func_ *Node, top int) {
for _, ln := range func_.Func.Cvars.Slice() {
- n := ln.Name.Param.Closure
+ n := ln.Name.Defn
if !n.Name.Captured {
n.Name.Captured = true
if n.Name.Decldepth == 0 {
// We use value capturing for values <= 128 bytes that are never reassigned
// after capturing (effectively constant).
func capturevars(xfunc *Node) {
- var outer *Node
-
lno := lineno
lineno = xfunc.Lineno
// so that the outer frame also grabs them and knows they escape.
dowidth(v.Type)
- outer = v.Name.Param.Outerexpr
- v.Name.Param.Outerexpr = nil
+ outer := v.Name.Param.Outer
+ outermost := v.Name.Defn
// out parameters will be assigned to implicitly upon return.
- if outer.Class != PPARAMOUT && !v.Name.Param.Closure.Addrtaken && !v.Name.Param.Closure.Assigned && v.Type.Width <= 128 {
+ if outer.Class != PPARAMOUT && !outermost.Addrtaken && !outermost.Assigned && v.Type.Width <= 128 {
v.Name.Byval = true
} else {
- v.Name.Param.Closure.Addrtaken = true
+ outermost.Addrtaken = true
outer = Nod(OADDR, outer, nil)
}
if v.Name.Byval {
how = "value"
}
- Warnl(v.Lineno, "%v capturing by %s: %v (addr=%v assign=%v width=%d)", name, how, v.Sym, v.Name.Param.Closure.Addrtaken, v.Name.Param.Closure.Assigned, int32(v.Type.Width))
+ Warnl(v.Lineno, "%v capturing by %s: %v (addr=%v assign=%v width=%d)", name, how, v.Sym, outermost.Addrtaken, outermost.Assigned, int32(v.Type.Width))
}
outer = typecheck(outer, Erv)
}
if Curfn != nil && n.Op == ONAME && n.Name.Funcdepth > 0 && n.Name.Funcdepth != Funcdepth {
- // inner func is referring to var in outer func.
+ // Inner func is referring to var in outer func.
//
// TODO(rsc): If there is an outer variable x and we
// are parsing x := 5 inside the closure, until we get to
// the := it looks like a reference to the outer x so we'll
// make x a closure variable unnecessarily.
- if n.Name.Param.Closure == nil || n.Name.Param.Closure.Name.Funcdepth != Funcdepth {
- // create new closure var.
- c := Nod(ONAME, nil, nil)
-
+ c := n.Name.Param.Innermost
+ if c == nil || c.Name.Funcdepth != Funcdepth {
+ // Do not have a closure var for the active closure yet; make one.
+ c = Nod(ONAME, nil, nil)
c.Sym = s
c.Class = PAUTOHEAP
- c.setIsClosureParam(true)
+ c.setIsClosureVar(true)
c.Isddd = n.Isddd
c.Name.Defn = n
c.Addable = false
c.Ullman = 2
c.Name.Funcdepth = Funcdepth
- c.Name.Param.Outer = n.Name.Param.Closure
- n.Name.Param.Closure = c
- c.Name.Param.Closure = n
+
+ // Link into list of active closure variables.
+ // Popped from list in func closurebody.
+ c.Name.Param.Outer = n.Name.Param.Innermost
+ n.Name.Param.Innermost = c
+
c.Xoffset = 0
Curfn.Func.Cvars.Append(c)
}
// return ref to closure var, not original
- return n.Name.Param.Closure
+ return c
}
return n
escassignSinkNilWhy(e, n, n7.Right, "map literal value")
}
- // Link addresses of captured variables to closure.
case OCLOSURE:
+ // Link addresses of captured variables to closure.
for _, v := range n.Func.Cvars.Slice() {
if v.Op == OXXX { // unnamed out argument; see dcl.go:/^funcargs
continue
}
- a := v.Name.Param.Closure
+ a := v.Name.Defn
if !v.Name.Byval {
a = Nod(OADDR, a, nil)
a.Lineno = v.Lineno
// Treat a captured closure variable as equivalent to the
// original variable.
- if src.isClosureParam() {
+ if src.isClosureVar() {
if leaks && Debug['m'] != 0 {
Warnl(src.Lineno, "leaking closure reference %v", Nconv(src, FmtShort))
step.describe(src)
}
- escwalk(e, level, dst, src.Name.Param.Closure, e.stepWalk(dst, src.Name.Param.Closure, "closure-var", step))
+ escwalk(e, level, dst, src.Name.Defn, e.stepWalk(dst, src.Name.Defn, "closure-var", step))
}
case OPTRLIT, OADDR:
if n.Nbody.Len() != 0 {
return fmt.Sprintf("%v { %v }", n.Type, n.Nbody)
}
- return fmt.Sprintf("%v { %v }", n.Type, n.Name.Param.Closure.Nbody)
+ return fmt.Sprintf("%v { %v }", n.Type, n.Func.Closure.Nbody)
case OCOMPLIT:
ptrlit := n.Right != nil && n.Right.Implicit && n.Right.Type != nil && n.Right.Type.IsPtr()
}
// If a closure reference escapes, mark the outer variable as escaping.
- if n.isClosureParam() {
+ if n.isClosureVar() {
addrescapes(n.Name.Defn)
break
}
const (
hasBreak = 1 << iota
notLiveAtEnd
- isClosureParam
+ isClosureVar
)
func (n *Node) HasBreak() bool {
n.flags &^= notLiveAtEnd
}
}
-func (n *Node) isClosureParam() bool {
- return n.flags&isClosureParam != 0
+func (n *Node) isClosureVar() bool {
+ return n.flags&isClosureVar != 0
}
-func (n *Node) setIsClosureParam(b bool) {
+func (n *Node) setIsClosureVar(b bool) {
if b {
- n.flags |= isClosureParam
+ n.flags |= isClosureVar
} else {
- n.flags &^= isClosureParam
+ n.flags &^= isClosureVar
}
}
type Name struct {
Pack *Node // real package for import . names
Pkg *Pkg // pkg for OPACK nodes
- Heapaddr *Node // temp holding heap address of param
- Inlvar *Node // ONAME substitute while inlining
+ Heapaddr *Node // temp holding heap address of param (could move to Param?)
+ Inlvar *Node // ONAME substitute while inlining (could move to Param?)
Defn *Node // initializing assignment
Curfn *Node // function for local variables
Param *Param // additional fields for ONAME, ODCLFIELD
Ntype *Node
// ONAME PAUTOHEAP
- Outerexpr *Node // expression copied into closure for variable
Stackcopy *Node // the PPARAM/PPARAMOUT on-stack slot (moved func params only)
// ONAME PPARAM
Field *Field // TFIELD in arg struct
// ONAME closure linkage
- Outer *Node
- Closure *Node
+ // Consider:
+ //
+ // func f() {
+ // x := 1 // x1
+ // func() {
+ // use(x) // x2
+ // func() {
+ // use(x) // x3
+ // --- parser is here ---
+ // }()
+ // }()
+ // }
+ //
+ // There is an original declaration of x and then a chain of mentions of x
+ // leading into the current function. Each time x is mentioned in a new closure,
+ // we create a variable representing x for use in that specific closure,
+ // since the way you get to x is different in each closure.
+ //
+ // Let's number the specific variables as shown in the code:
+ // x1 is the original x, x2 is when mentioned in the closure,
+ // and x3 is when mentioned in the closure in the closure.
+ //
+ // We keep these linked (assume N > 1):
+ //
+ // - x1.Defn = original declaration statement for x (like most variables)
+ // - x1.Innermost = current innermost closure x (in this case x3), or nil for none
+ // - x1.isClosureVar() = false
+ //
+ // - xN.Defn = x1, N > 1
+ // - xN.isClosureVar() = true, N > 1
+ // - x2.Outer = nil
+ // - xN.Outer = x(N-1), N > 2
+ //
+ //
+ // When we look up x in the symbol table, we always get x1.
+ // Then we can use x1.Innermost (if not nil) to get the x
+ // for the innermost known closure function,
+ // but the first reference in a closure will find either no x1.Innermost
+ // or an x1.Innermost with .Funcdepth < Funcdepth.
+ // In that case, a new xN must be created, linked in with:
+ //
+ // xN.Defn = x1
+ // xN.Outer = x1.Innermost
+ // x1.Innermost = xN
+ //
+ // When we finish the function, we'll process its closure variables
+ // and find xN and pop it off the list using:
+ //
+ // x1 := xN.Defn
+ // x1.Innermost = xN.Outer
+ //
+ // We leave xN.Innermost set so that we can still get to the original
+ // variable quickly. Not shown here, but once we're
+ // done parsing a function and no longer need xN.Outer for the
+ // lexical x reference links as described above, closurebody
+ // recomputes xN.Outer as the semantic x reference link tree,
+ // even filling in x in intermediate closures that might not
+ // have mentioned it along the way to inner closures that did.
+ // See closurebody for details.
+ //
+ // During the eventual compilation, then, for closure variables we have:
+ //
+ // xN.Defn = original variable
+ // xN.Outer = variable captured in next outward scope
+ // to make closure where xN appears
+ //
+ // Because of the sharding of pieces of the node, x.Defn means x.Name.Defn
+ // and x.Innermost/Outer means x.Name.Param.Innermost/Outer.
+ Innermost *Node
+ Outer *Node
}
// Func holds Node fields used only with function-like nodes.
var l *Node
for l = n.Left; l != r; l = l.Left {
l.Addrtaken = true
- if l.Name != nil && l.Name.Param != nil && l.Name.Param.Closure != nil {
- l.Name.Param.Closure.Addrtaken = true
+ if l.isClosureVar() {
+ l.Name.Defn.Addrtaken = true
}
}
Fatalf("found non-orig name node %v", l)
}
l.Addrtaken = true
- if l.Name != nil && l.Name.Param != nil && l.Name.Param.Closure != nil {
- l.Name.Param.Closure.Addrtaken = true
+ if l.isClosureVar() {
+ l.Name.Defn.Addrtaken = true
}
n.Left = defaultlit(n.Left, nil)
l = n.Left
var l *Node
for l = n; l != r; l = l.Left {
l.Assigned = true
- if l.Name != nil && l.Name.Param != nil && l.Name.Param.Closure != nil {
- l.Name.Param.Closure.Assigned = true
+ if l.isClosureVar() {
+ l.Name.Defn.Assigned = true
}
}
l.Assigned = true
- if l.Name != nil && l.Name.Param != nil && l.Name.Param.Closure != nil {
- l.Name.Param.Closure.Assigned = true
+ if l.isClosureVar() {
+ l.Name.Defn.Assigned = true
}
}