1
0
mirror of https://github.com/golang/go synced 2024-11-23 05:40:04 -07:00

[dev.regabi] cmd/compile: refactor closure var setup/teardown

Creating closure vars is subtle and is also needed in both CL 281932
and CL 283112, so refactor out a common implementation that can be
used in all 3 places.

Passes toolstash -cmp.

Change-Id: Ib993eb90c895b52759bfbfbaad88921e391b0b4d
Reviewed-on: https://go-review.googlesource.com/c/go/+/283194
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Dan Scales <danscales@google.com>
Trust: Dan Scales <danscales@google.com>
Trust: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
Matthew Dempsky 2021-01-11 15:58:19 -08:00
parent f57f484053
commit b4d2a0445b
2 changed files with 79 additions and 61 deletions

View File

@ -351,6 +351,82 @@ func (n *Name) Byval() bool {
return n.Canonical().flags&nameByval != 0
}
// CaptureName returns a Name suitable for referring to n from within function
// fn or from the package block if fn is nil. If n is a free variable declared
// within a function that encloses fn, then CaptureName returns a closure
// variable that refers to n and adds it to fn.ClosureVars. Otherwise, it simply
// returns n.
func CaptureName(pos src.XPos, fn *Func, n *Name) *Name {
if n.IsClosureVar() {
base.FatalfAt(pos, "misuse of CaptureName on closure variable: %v", n)
}
if n.Op() != ONAME || n.Curfn == nil || n.Curfn == fn {
return n // okay to use directly
}
if fn == nil {
base.FatalfAt(pos, "package-block reference to %v, declared in %v", n, n.Curfn)
}
c := n.Innermost
if c != nil && c.Curfn == fn {
return c
}
// Do not have a closure var for the active closure yet; make one.
c = NewNameAt(pos, n.Sym())
c.Curfn = fn
c.Class = PAUTOHEAP
c.SetIsClosureVar(true)
c.Defn = n
// Link into list of active closure variables.
// Popped from list in FinishCaptureNames.
c.Outer = n.Innermost
n.Innermost = c
fn.ClosureVars = append(fn.ClosureVars, c)
return c
}
// FinishCaptureNames handles any work leftover from calling CaptureName
// earlier. outerfn should be the function that immediately encloses fn.
func FinishCaptureNames(pos src.XPos, outerfn, fn *Func) {
// closure-specific variables are hanging off the
// ordinary ones; see CaptureName above.
// unhook them.
// make the list of pointers for the closure call.
for _, cv := range fn.ClosureVars {
// Unlink from n; see comment in syntax.go type Param for these fields.
n := cv.Defn.(*Name)
n.Innermost = cv.Outer
// If the closure usage of n is not dense, we need to make it
// dense by recapturing n within the enclosing function.
//
// That is, suppose we just finished parsing the innermost
// closure f4 in this code:
//
// func f() {
// n := 1
// func() { // f2
// use(n)
// func() { // f3
// func() { // f4
// use(n)
// }()
// }()
// }()
// }
//
// At this point cv.Outer is f2's n; there is no n for f3. To
// construct the closure f4 from within f3, we need to use f3's
// n and in this case we need to create f3's n with CaptureName.
//
// We'll decide later in walk whether to use v directly or &v.
cv.Outer = CaptureName(pos, outerfn, n)
}
}
// SameSource reports whether two nodes refer to the same source
// element.
//

View File

@ -1872,45 +1872,7 @@ func (p *noder) funcLit(expr *syntax.FuncLit) ir.Node {
p.funcBody(fn, expr.Body)
// closure-specific variables are hanging off the
// ordinary ones in the symbol table; see oldname.
// unhook them.
// make the list of pointers for the closure call.
for _, v := range fn.ClosureVars {
// Unlink from v1; see comment in syntax.go type Param for these fields.
v1 := v.Defn
v1.Name().Innermost = v.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.Outer = oldname(v.Sym()).(*ir.Name)
}
ir.FinishCaptureNames(base.Pos, ir.CurFunc, fn)
return clo
}
@ -1944,32 +1906,12 @@ func oldname(s *types.Sym) ir.Node {
return ir.NewIdent(base.Pos, s)
}
if ir.CurFunc != nil && n.Op() == ir.ONAME && n.Name().Curfn != nil && n.Name().Curfn != ir.CurFunc {
// Inner func is referring to var in outer func.
//
if n, ok := n.(*ir.Name); ok {
// 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.
n := n.(*ir.Name)
c := n.Innermost
if c == nil || c.Curfn != ir.CurFunc {
// Do not have a closure var for the active closure yet; make one.
c = typecheck.NewName(s)
c.Class = ir.PAUTOHEAP
c.SetIsClosureVar(true)
c.Defn = n
// Link into list of active closure variables.
// Popped from list in func funcLit.
c.Outer = n.Innermost
n.Innermost = c
ir.CurFunc.ClosureVars = append(ir.CurFunc.ClosureVars, c)
}
// return ref to closure var, not original
return c
return ir.CaptureName(base.Pos, ir.CurFunc, n)
}
return n