From b4d2a0445b0ca54a159e0895e1a8b31d47411894 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Mon, 11 Jan 2021 15:58:19 -0800 Subject: [PATCH] [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 TryBot-Result: Go Bot Reviewed-by: Dan Scales Trust: Dan Scales Trust: Matthew Dempsky --- src/cmd/compile/internal/ir/name.go | 76 +++++++++++++++++++++++++ src/cmd/compile/internal/noder/noder.go | 64 +-------------------- 2 files changed, 79 insertions(+), 61 deletions(-) diff --git a/src/cmd/compile/internal/ir/name.go b/src/cmd/compile/internal/ir/name.go index cfb481e31c..2375eddb99 100644 --- a/src/cmd/compile/internal/ir/name.go +++ b/src/cmd/compile/internal/ir/name.go @@ -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. // diff --git a/src/cmd/compile/internal/noder/noder.go b/src/cmd/compile/internal/noder/noder.go index 76913c62a6..ec0debdbbd 100644 --- a/src/cmd/compile/internal/noder/noder.go +++ b/src/cmd/compile/internal/noder/noder.go @@ -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