]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.regabi] cmd/compile: refactor closure var setup/teardown
authorMatthew Dempsky <mdempsky@google.com>
Mon, 11 Jan 2021 23:58:19 +0000 (15:58 -0800)
committerMatthew Dempsky <mdempsky@google.com>
Tue, 12 Jan 2021 02:46:27 +0000 (02:46 +0000)
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>

src/cmd/compile/internal/ir/name.go
src/cmd/compile/internal/noder/noder.go

index cfb481e31cd1f5dafab5a53e7f4a16c1cd963aba..2375eddb99a0d8c2a5311c5f009c0df6716fbedc 100644 (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.
 //
index 76913c62a6dfd94545d21b5eb045fb7b93a218ea..ec0debdbbd6e932e59220b22449b1a40ab604cfb 100644 (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