1
0
mirror of https://github.com/golang/go synced 2024-09-23 17:10:13 -06:00

[dev.regabi] cmd/compile: change ir.DoChildren to use bool result type

After using the IR visitor code for a bit, it seems clear that a
simple boolean result type is adequate for tree traversals. This CL
updates ir.DoChildren to use the same calling convention as ir.Any,
and updates mknode.go to generate code accordingly.

There were only two places where the error-based DoChildren API was
used within the compiler:

1. Within typechecking, marking statements that contain "break". This
code never returns errors anyway, so it's trivially updated to return
false instead.

2. Within inlining, the "hairy visitor" actually does make use of
returning errors. However, it threads through a reference to the
hairyVisitor anyway, where it would be trivial to store any needed
information instead. For the purpose of this CL, we provide
"errChildren" and "errList" helper functions that provide the previous
error-based semantics on top of the new bool-based API.

Passes toolstash -cmp.

Change-Id: I4bac9a697b4dbfb5f66eeac37d4a2ced2073d7d0
Reviewed-on: https://go-review.googlesource.com/c/go/+/280675
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
This commit is contained in:
Matthew Dempsky 2020-12-29 19:46:31 -08:00
parent 499851bac8
commit f9b67f76a5
9 changed files with 481 additions and 613 deletions

View File

@ -265,7 +265,7 @@ var errBudget = errors.New("too expensive")
func (v *hairyVisitor) tooHairy(fn *ir.Func) bool {
v.do = v.doNode // cache closure
err := ir.DoChildren(fn, v.do)
err := errChildren(fn, v.do)
if err != nil {
v.reason = err.Error()
return true
@ -393,13 +393,13 @@ func (v *hairyVisitor) doNode(n ir.Node) error {
if ir.IsConst(n.Cond, constant.Bool) {
// This if and the condition cost nothing.
// TODO(rsc): It seems strange that we visit the dead branch.
if err := ir.DoList(n.Init(), v.do); err != nil {
if err := errList(n.Init(), v.do); err != nil {
return err
}
if err := ir.DoList(n.Body, v.do); err != nil {
if err := errList(n.Body, v.do); err != nil {
return err
}
if err := ir.DoList(n.Else, v.do); err != nil {
if err := errList(n.Else, v.do); err != nil {
return err
}
return nil
@ -431,7 +431,7 @@ func (v *hairyVisitor) doNode(n ir.Node) error {
return errBudget
}
return ir.DoChildren(n, v.do)
return errChildren(n, v.do)
}
func isBigFunc(fn *ir.Func) bool {
@ -1214,3 +1214,22 @@ func numNonClosures(list []*ir.Func) int {
}
return count
}
// TODO(mdempsky): Update inl.go to use ir.DoChildren directly.
func errChildren(n ir.Node, do func(ir.Node) error) (err error) {
ir.DoChildren(n, func(x ir.Node) bool {
err = do(x)
return err != nil
})
return
}
func errList(list []ir.Node, do func(ir.Node) error) error {
for _, x := range list {
if x != nil {
if err := do(x); err != nil {
return err
}
}
}
return nil
}

View File

@ -115,9 +115,9 @@ func NewFunc(pos src.XPos) *Func {
func (f *Func) isStmt() {}
func (n *Func) copy() Node { panic(n.no("copy")) }
func (n *Func) doChildren(do func(Node) error) error { return doNodes(n.Body, do) }
func (n *Func) editChildren(edit func(Node) Node) { editNodes(n.Body, edit) }
func (n *Func) copy() Node { panic(n.no("copy")) }
func (n *Func) doChildren(do func(Node) bool) bool { return doNodes(n.Body, do) }
func (n *Func) editChildren(edit func(Node) Node) { editNodes(n.Body, edit) }
func (f *Func) Type() *types.Type { return f.Nname.Type() }
func (f *Func) Sym() *types.Sym { return f.Nname.Sym() }

View File

@ -70,10 +70,10 @@ func main() {
"return &c }\n")
forNodeFields(typ,
"func (n *%[1]s) doChildren(do func(Node) error) error {\n",
"if n.%[1]s != nil { if err := do(n.%[1]s); err != nil { return err } }",
"if err := do%[2]s(n.%[1]s, do); err != nil { return err }",
"return nil }\n")
"func (n *%[1]s) doChildren(do func(Node) bool) bool {\n",
"if n.%[1]s != nil && do(n.%[1]s) { return true }",
"if do%[2]s(n.%[1]s, do) { return true }",
"return false }\n")
forNodeFields(typ,
"func (n *%[1]s) editChildren(edit func(Node) Node) {\n",
@ -121,15 +121,13 @@ func copy%[1]s(list []%[2]s) []%[2]s {
copy(c, list)
return c
}
func do%[1]s(list []%[2]s, do func(Node) error) error {
func do%[1]s(list []%[2]s, do func(Node) bool) bool {
for _, x := range list {
if x != nil {
if err := do(x); err != nil {
return err
}
if x != nil && do(x) {
return true
}
}
return nil
return false
}
func edit%[1]s(list []%[2]s, edit func(Node) Node) {
for i, x := range list {

View File

@ -143,9 +143,9 @@ type Name struct {
func (n *Name) isExpr() {}
func (n *Name) copy() Node { panic(n.no("copy")) }
func (n *Name) doChildren(do func(Node) error) error { return nil }
func (n *Name) editChildren(edit func(Node) Node) {}
func (n *Name) copy() Node { panic(n.no("copy")) }
func (n *Name) doChildren(do func(Node) bool) bool { return false }
func (n *Name) editChildren(edit func(Node) Node) {}
// CloneName makes a cloned copy of the name.
// It's not ir.Copy(n) because in general that operation is a mistake on names,

View File

@ -28,7 +28,7 @@ type Node interface {
// For making copies. For Copy and SepCopy.
copy() Node
doChildren(func(Node) error) error
doChildren(func(Node) bool) bool
editChildren(func(Node) Node)
// Abstract graph structure, for generic traversals.

File diff suppressed because it is too large Load Diff

View File

@ -195,21 +195,17 @@ func copyField(f *Field) *Field {
c := *f
return &c
}
func doField(f *Field, do func(Node) error) error {
func doField(f *Field, do func(Node) bool) bool {
if f == nil {
return nil
return false
}
if f.Decl != nil {
if err := do(f.Decl); err != nil {
return err
}
if f.Decl != nil && do(f.Decl) {
return true
}
if f.Ntype != nil {
if err := do(f.Ntype); err != nil {
return err
}
if f.Ntype != nil && do(f.Ntype) {
return true
}
return nil
return false
}
func editField(f *Field, edit func(Node) Node) {
if f == nil {
@ -230,13 +226,13 @@ func copyFields(list []*Field) []*Field {
}
return out
}
func doFields(list []*Field, do func(Node) error) error {
func doFields(list []*Field, do func(Node) bool) bool {
for _, x := range list {
if err := doField(x, do); err != nil {
return err
if doField(x, do) {
return true
}
}
return nil
return false
}
func editFields(list []*Field, edit func(Node) Node) {
for _, f := range list {

View File

@ -4,23 +4,18 @@
// IR visitors for walking the IR tree.
//
// The lowest level helpers are DoChildren and EditChildren,
// which nodes help implement (TODO(rsc): eventually) and
// provide control over whether and when recursion happens
// during the walk of the IR.
// The lowest level helpers are DoChildren and EditChildren, which
// nodes help implement and provide control over whether and when
// recursion happens during the walk of the IR.
//
// Although these are both useful directly, two simpler patterns
// are fairly common and also provided: Inspect and Scan.
// are fairly common and also provided: Visit and Any.
package ir
import (
"errors"
)
// DoChildren calls do(x) on each of n's non-nil child nodes x.
// If any call returns a non-nil error, DoChildren stops and returns that error.
// Otherwise, DoChildren returns nil.
// If any call returns true, DoChildren stops and returns true.
// Otherwise, DoChildren returns false.
//
// Note that DoChildren(n, do) only calls do(x) for n's immediate children.
// If x's children should be processed, then do(x) must call DoChildren(x, do).
@ -28,32 +23,32 @@ import (
// DoChildren allows constructing general traversals of the IR graph
// that can stop early if needed. The most general usage is:
//
// var do func(ir.Node) error
// do = func(x ir.Node) error {
// var do func(ir.Node) bool
// do = func(x ir.Node) bool {
// ... processing BEFORE visting children ...
// if ... should visit children ... {
// ir.DoChildren(x, do)
// ... processing AFTER visting children ...
// }
// if ... should stop parent DoChildren call from visiting siblings ... {
// return non-nil error
// return true
// }
// return nil
// return false
// }
// do(root)
//
// Since DoChildren does not generate any errors itself, if the do function
// never wants to stop the traversal, it can assume that DoChildren itself
// will always return nil, simplifying to:
// Since DoChildren does not return true itself, if the do function
// never wants to stop the traversal, it can assume that DoChildren
// itself will always return false, simplifying to:
//
// var do func(ir.Node) error
// do = func(x ir.Node) error {
// var do func(ir.Node) bool
// do = func(x ir.Node) bool {
// ... processing BEFORE visting children ...
// if ... should visit children ... {
// ir.DoChildren(x, do)
// }
// ... processing AFTER visting children ...
// return nil
// return false
// }
// do(root)
//
@ -61,14 +56,15 @@ import (
// only processing before visiting children and never stopping:
//
// func Visit(n ir.Node, visit func(ir.Node)) {
// var do func(ir.Node) error
// do = func(x ir.Node) error {
// if n == nil {
// return
// }
// var do func(ir.Node) bool
// do = func(x ir.Node) bool {
// visit(x)
// return ir.DoChildren(x, do)
// }
// if n != nil {
// visit(n)
// }
// do(n)
// }
//
// The Any function illustrates a different simplification of the pattern,
@ -76,50 +72,40 @@ import (
// a node x for which cond(x) returns true, at which point the entire
// traversal stops and returns true.
//
// func Any(n ir.Node, find cond(ir.Node)) bool {
// stop := errors.New("stop")
// var do func(ir.Node) error
// do = func(x ir.Node) error {
// if cond(x) {
// return stop
// }
// return ir.DoChildren(x, do)
// func Any(n ir.Node, cond(ir.Node) bool) bool {
// if n == nil {
// return false
// }
// return do(n) == stop
// var do func(ir.Node) bool
// do = func(x ir.Node) bool {
// return cond(x) || ir.DoChildren(x, do)
// }
// return do(n)
// }
//
// Visit and Any are presented above as examples of how to use
// DoChildren effectively, but of course, usage that fits within the
// simplifications captured by Visit or Any will be best served
// by directly calling the ones provided by this package.
func DoChildren(n Node, do func(Node) error) error {
func DoChildren(n Node, do func(Node) bool) bool {
if n == nil {
return nil
return false
}
return n.doChildren(do)
}
// DoList calls f on each non-nil node x in the list, in list order.
// If any call returns a non-nil error, DoList stops and returns that error.
// Otherwise DoList returns nil.
//
// Note that DoList only calls do on the nodes in the list, not their children.
// If x's children should be processed, do(x) must call DoChildren(x, do) itself.
func DoList(list Nodes, do func(Node) error) error {
return doNodes(list, do)
}
// Visit visits each non-nil node x in the IR tree rooted at n
// in a depth-first preorder traversal, calling visit on each node visited.
func Visit(n Node, visit func(Node)) {
var do func(Node) error
do = func(x Node) error {
if n == nil {
return
}
var do func(Node) bool
do = func(x Node) bool {
visit(x)
return DoChildren(x, do)
}
if n != nil {
do(n)
}
do(n)
}
// VisitList calls Visit(x, visit) for each node x in the list.
@ -129,8 +115,6 @@ func VisitList(list Nodes, visit func(Node)) {
}
}
var stop = errors.New("stop")
// Any looks for a non-nil node x in the IR tree rooted at n
// for which cond(x) returns true.
// Any considers nodes in a depth-first, preorder traversal.
@ -141,14 +125,11 @@ func Any(n Node, cond func(Node) bool) bool {
if n == nil {
return false
}
var do func(Node) error
do = func(x Node) error {
if cond(x) {
return stop
}
return DoChildren(x, do)
var do func(Node) bool
do = func(x Node) bool {
return cond(x) || DoChildren(x, do)
}
return do(n) == stop
return do(n)
}
// AnyList calls Any(x, cond) for each node x in the list, in order.

View File

@ -2053,8 +2053,8 @@ func markBreak(fn *ir.Func) {
var labels map[*types.Sym]ir.Node
var implicit ir.Node
var mark func(ir.Node) error
mark = func(n ir.Node) error {
var mark func(ir.Node) bool
mark = func(n ir.Node) bool {
switch n.Op() {
default:
ir.DoChildren(n, mark)
@ -2094,7 +2094,7 @@ func markBreak(fn *ir.Func) {
}
implicit = old
}
return nil
return false
}
mark(fn)