[dev.typeparams] cmd/compile: switch CaptureVars to use syntax.Walk

This CL refactors CaptureVars to use a visitor type so it's easier to
break out helper functions to review.

It also simplifies the quirks-mode handling of function literals:
instead of trying to maintain information about whether we're inside a
function literal or not, it now just rewrites the recorded position
information for any newly added free variables after walking the
function literal.

(Quirks mode is only for "toolstash -cmp"-style binary output testing
of normal code and will eventually be removed, so I don't think it's
important that this is an O(N^2) algorithm for deeply nested function
literals with lots of free variables.)

Change-Id: I0689984f6d88cf9937d4706d2d8de96415eaeee3
Reviewed-on: https://go-review.googlesource.com/c/go/+/330789
Trust: Matthew Dempsky <mdempsky@google.com>
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Matthew Dempsky 2021-06-23 14:45:34 -07:00
parent 2493c72742
commit badb98364b

View file

@ -1264,90 +1264,114 @@ type posObj struct {
} }
// captureVars returns the free variables used by the given function // captureVars returns the free variables used by the given function
// literal. // literal. The closureVars result is the list of free variables
// captured by expr, and localsIdx is a map from free variable to
// index. See varCaptor's identically named fields for more details.
func (w *writer) captureVars(expr *syntax.FuncLit) (closureVars []posObj, localsIdx map[types2.Object]int) { func (w *writer) captureVars(expr *syntax.FuncLit) (closureVars []posObj, localsIdx map[types2.Object]int) {
scope, ok := w.p.info.Scopes[expr.Type] scope, ok := w.p.info.Scopes[expr.Type]
assert(ok) assert(ok)
localsIdx = make(map[types2.Object]int)
// TODO(mdempsky): This code needs to be cleaned up (e.g., to avoid // TODO(mdempsky): This code needs to be cleaned up (e.g., to avoid
// traversing nested function literals multiple times). This will be // traversing nested function literals multiple times). This will be
// easier after we drop quirks mode. // easier after we drop quirks mode.
var rbracePos syntax.Pos v := varCaptor{
w: w,
scope: scope,
localsIdx: make(map[types2.Object]int),
}
var visitor func(n syntax.Node) bool syntax.Walk(expr, &v)
visitor = func(n syntax.Node) bool {
// Constant expressions don't count towards capturing. return v.closureVars, v.localsIdx
if n, ok := n.(syntax.Expr); ok { }
if tv, ok := w.p.info.Types[n]; ok && tv.Value != nil {
return true // varCaptor implements syntax.Visitor for enumerating free variables
} // used by a function literal.
type varCaptor struct {
w *writer
scope *types2.Scope
// closureVars lists free variables along with the position where
// they first appeared, in order of appearance.
closureVars []posObj
// localsIdx is a map from free variables to their index within
// closureVars.
localsIdx map[types2.Object]int
}
func (v *varCaptor) capture(n *syntax.Name) {
obj, ok := v.w.p.info.Uses[n].(*types2.Var)
if !ok || obj.IsField() {
return // not a variable
}
if obj.Parent() == obj.Pkg().Scope() {
return // global variable
}
if _, ok := v.localsIdx[obj]; ok {
return // already captured
}
for parent := obj.Parent(); parent != obj.Pkg().Scope(); parent = parent.Parent() {
if parent == v.scope {
return // object declared within our scope
} }
}
idx := len(v.closureVars)
v.closureVars = append(v.closureVars, posObj{n.Pos(), obj})
v.localsIdx[obj] = idx
}
func (v *varCaptor) Visit(n syntax.Node) syntax.Visitor {
// Constant expressions don't count towards capturing.
if n, ok := n.(syntax.Expr); ok {
if tv, ok := v.w.p.info.Types[n]; ok && tv.Value != nil {
return nil
}
}
if n, ok := n.(*syntax.Name); ok {
v.capture(n)
}
if quirksMode() {
switch n := n.(type) { switch n := n.(type) {
case *syntax.Name:
if obj, ok := w.p.info.Uses[n].(*types2.Var); ok && !obj.IsField() && obj.Pkg() == w.p.curpkg && obj.Parent() != obj.Pkg().Scope() {
// Found a local variable. See if it chains up to scope.
parent := obj.Parent()
for {
if parent == scope {
break
}
if parent == obj.Pkg().Scope() {
if _, present := localsIdx[obj]; !present {
pos := rbracePos
if pos == (syntax.Pos{}) {
pos = n.Pos()
}
idx := len(closureVars)
closureVars = append(closureVars, posObj{pos, obj})
localsIdx[obj] = idx
}
break
}
parent = parent.Parent()
}
}
case *syntax.FuncLit: case *syntax.FuncLit:
// Quirk: typecheck uses the rbrace position position of the // Quirk: typecheck uses the rbrace position position of the
// function literal as the position of the intermediary capture. // function literal as the position of the intermediary capture.
if quirksMode() && rbracePos == (syntax.Pos{}) { end := len(v.closureVars)
rbracePos = n.Body.Rbrace syntax.Walk(n.Type, v) // unnecessary to walk, but consistent with non-quirks mode
syntax.Crawl(n.Body, visitor) syntax.Walk(n.Body, v)
rbracePos = syntax.Pos{} for i := end; i < len(v.closureVars); i++ {
return true v.closureVars[i].pos = n.Body.Rbrace
} }
return nil
case *syntax.AssignStmt: case *syntax.AssignStmt:
// Quirk: typecheck visits (and thus captures) the RHS of // Quirk: typecheck visits (and thus captures) the RHS of
// assignment statements before the LHS. // assignment statements (but not op= statements) before the LHS.
if quirksMode() && (n.Op == 0 || n.Op == syntax.Def) { if n.Op == 0 || n.Op == syntax.Def {
syntax.Crawl(n.Rhs, visitor) syntax.Walk(n.Rhs, v)
syntax.Crawl(n.Lhs, visitor) syntax.Walk(n.Lhs, v)
return true return nil
} }
case *syntax.RangeClause: case *syntax.RangeClause:
// Quirk: Similarly, it visits the expression to be iterated // Quirk: Similarly, typecheck visits the expression to be
// over before the iteration variables. // iterated over before the iteration variables.
if quirksMode() { syntax.Walk(n.X, v)
syntax.Crawl(n.X, visitor) if n.Lhs != nil {
if n.Lhs != nil { syntax.Walk(n.Lhs, v)
syntax.Crawl(n.Lhs, visitor)
}
return true
} }
return nil
} }
return false
} }
syntax.Crawl(expr.Body, visitor)
return return v
} }
func (w *writer) exprList(expr syntax.Expr) { func (w *writer) exprList(expr syntax.Expr) {