[dev.regabi] cmd/compile: generalize ir/mknode.go

This CL generalizes ir/mknode.go to get rid of most of almost all of
its special cases for node field types. The only remaining speciale
case now is Field, which doesn't implement Node any more, but perhaps
should.

To help with removing special cases, node fields can now be tagged
with `mknode:"-"` so that mknode ignores them when generating its
helper methods. Further, to simplify skipping all of the orig fields,
a new origNode helper type is added which declares an orig field
marked as `mknode:"-"` and also provides the Orig and SetOrig methods
needed to implement the OrigNode interface.

Passes toolstash -cmp.

Change-Id: Ic68d4f0a9d2ef6e57e9fe87cdc641e5c4859830b
Reviewed-on: https://go-review.googlesource.com/c/go/+/280674
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
This commit is contained in:
Matthew Dempsky 2020-12-29 15:36:48 -08:00
parent 82ab3d1448
commit 499851bac8
9 changed files with 1071 additions and 706 deletions

View file

@ -25,6 +25,14 @@ type OrigNode interface {
SetOrig(Node) SetOrig(Node)
} }
// origNode may be embedded into a Node to make it implement OrigNode.
type origNode struct {
orig Node `mknode:"-"`
}
func (n *origNode) Orig() Node { return n.orig }
func (n *origNode) SetOrig(o Node) { n.orig = o }
// Orig returns the “original” node for n. // Orig returns the “original” node for n.
// If n implements OrigNode, Orig returns n.Orig(). // If n implements OrigNode, Orig returns n.Orig().
// Otherwise Orig returns n itself. // Otherwise Orig returns n itself.

View file

@ -14,27 +14,6 @@ import (
"go/token" "go/token"
) )
func maybeDo(x Node, err error, do func(Node) error) error {
if x != nil && err == nil {
err = do(x)
}
return err
}
func maybeDoList(x Nodes, err error, do func(Node) error) error {
if err == nil {
err = DoList(x, do)
}
return err
}
func maybeEdit(x Node, edit func(Node) Node) Node {
if x == nil {
return x
}
return edit(x)
}
// An Expr is a Node that can appear as an expression. // An Expr is a Node that can appear as an expression.
type Expr interface { type Expr interface {
Node Node
@ -77,16 +56,6 @@ func (n *miniExpr) Init() Nodes { return n.init }
func (n *miniExpr) PtrInit() *Nodes { return &n.init } func (n *miniExpr) PtrInit() *Nodes { return &n.init }
func (n *miniExpr) SetInit(x Nodes) { n.init = x } func (n *miniExpr) SetInit(x Nodes) { n.init = x }
func toNtype(x Node) Ntype {
if x == nil {
return nil
}
if _, ok := x.(Ntype); !ok {
Dump("not Ntype", x)
}
return x.(Ntype)
}
// An AddStringExpr is a string concatenation Expr[0] + Exprs[1] + ... + Expr[len(Expr)-1]. // An AddStringExpr is a string concatenation Expr[0] + Exprs[1] + ... + Expr[len(Expr)-1].
type AddStringExpr struct { type AddStringExpr struct {
miniExpr miniExpr
@ -189,7 +158,7 @@ const (
// A CallExpr is a function call X(Args). // A CallExpr is a function call X(Args).
type CallExpr struct { type CallExpr struct {
miniExpr miniExpr
orig Node origNode
X Node X Node
Args Nodes Args Nodes
Rargs Nodes // TODO(rsc): Delete. Rargs Nodes // TODO(rsc): Delete.
@ -210,9 +179,6 @@ func NewCallExpr(pos src.XPos, op Op, fun Node, args []Node) *CallExpr {
func (*CallExpr) isStmt() {} func (*CallExpr) isStmt() {}
func (n *CallExpr) Orig() Node { return n.orig }
func (n *CallExpr) SetOrig(x Node) { n.orig = x }
func (n *CallExpr) SetOp(op Op) { func (n *CallExpr) SetOp(op Op) {
switch op { switch op {
default: default:
@ -226,7 +192,7 @@ func (n *CallExpr) SetOp(op Op) {
// A ClosureExpr is a function literal expression. // A ClosureExpr is a function literal expression.
type ClosureExpr struct { type ClosureExpr struct {
miniExpr miniExpr
Func *Func Func *Func `mknode:"-"`
Prealloc *Name Prealloc *Name
} }
@ -254,7 +220,7 @@ func NewClosureRead(typ *types.Type, offset int64) *ClosureReadExpr {
// Before type-checking, the type is Ntype. // Before type-checking, the type is Ntype.
type CompLitExpr struct { type CompLitExpr struct {
miniExpr miniExpr
orig Node origNode
Ntype Ntype Ntype Ntype
List Nodes // initialized values List Nodes // initialized values
Prealloc *Name Prealloc *Name
@ -270,8 +236,6 @@ func NewCompLitExpr(pos src.XPos, op Op, typ Ntype, list []Node) *CompLitExpr {
return n return n
} }
func (n *CompLitExpr) Orig() Node { return n.orig }
func (n *CompLitExpr) SetOrig(x Node) { n.orig = x }
func (n *CompLitExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 } func (n *CompLitExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 }
func (n *CompLitExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) } func (n *CompLitExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) }
@ -286,14 +250,15 @@ func (n *CompLitExpr) SetOp(op Op) {
type ConstExpr struct { type ConstExpr struct {
miniExpr miniExpr
val constant.Value origNode
orig Node val constant.Value
} }
func NewConstExpr(val constant.Value, orig Node) Node { func NewConstExpr(val constant.Value, orig Node) Node {
n := &ConstExpr{orig: orig, val: val} n := &ConstExpr{val: val}
n.op = OLITERAL n.op = OLITERAL
n.pos = orig.Pos() n.pos = orig.Pos()
n.orig = orig
n.SetType(orig.Type()) n.SetType(orig.Type())
n.SetTypecheck(orig.Typecheck()) n.SetTypecheck(orig.Typecheck())
n.SetDiag(orig.Diag()) n.SetDiag(orig.Diag())
@ -301,8 +266,6 @@ func NewConstExpr(val constant.Value, orig Node) Node {
} }
func (n *ConstExpr) Sym() *types.Sym { return n.orig.Sym() } func (n *ConstExpr) Sym() *types.Sym { return n.orig.Sym() }
func (n *ConstExpr) Orig() Node { return n.orig }
func (n *ConstExpr) SetOrig(orig Node) { panic(n.no("SetOrig")) }
func (n *ConstExpr) Val() constant.Value { return n.val } func (n *ConstExpr) Val() constant.Value { return n.val }
// A ConvExpr is a conversion Type(X). // A ConvExpr is a conversion Type(X).
@ -664,9 +627,9 @@ type TypeAssertExpr struct {
// Runtime type information provided by walkDotType. // Runtime type information provided by walkDotType.
// Caution: These aren't always populated; see walkDotType. // Caution: These aren't always populated; see walkDotType.
SrcType *AddrExpr // *runtime._type for X's type SrcType *AddrExpr `mknode:"-"` // *runtime._type for X's type
DstType *AddrExpr // *runtime._type for Type DstType *AddrExpr `mknode:"-"` // *runtime._type for Type
Itab *AddrExpr // *runtime.itab for Type implementing X's type Itab *AddrExpr `mknode:"-"` // *runtime.itab for Type implementing X's type
} }
func NewTypeAssertExpr(pos src.XPos, x Node, typ Ntype) *TypeAssertExpr { func NewTypeAssertExpr(pos src.XPos, x Node, typ Ntype) *TypeAssertExpr {

View file

@ -115,6 +115,10 @@ func NewFunc(pos src.XPos) *Func {
func (f *Func) isStmt() {} 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 (f *Func) Type() *types.Type { return f.Nname.Type() } func (f *Func) Type() *types.Type { return f.Nname.Type() }
func (f *Func) Sym() *types.Sym { return f.Nname.Sym() } func (f *Func) Sym() *types.Sym { return f.Nname.Sym() }
func (f *Func) Linksym() *obj.LSym { return f.Nname.Linksym() } func (f *Func) Linksym() *obj.LSym { return f.Nname.Linksym() }

View file

@ -13,11 +13,16 @@ import (
"go/types" "go/types"
"io/ioutil" "io/ioutil"
"log" "log"
"reflect"
"sort"
"strings" "strings"
"golang.org/x/tools/go/packages" "golang.org/x/tools/go/packages"
) )
var irPkg *types.Package
var buf bytes.Buffer
func main() { func main() {
cfg := &packages.Config{ cfg := &packages.Config{
Mode: packages.NeedSyntax | packages.NeedTypes, Mode: packages.NeedSyntax | packages.NeedTypes,
@ -26,44 +31,26 @@ func main() {
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
irPkg = pkgs[0].Types
pkg := pkgs[0].Types
scope := pkg.Scope()
lookup := func(name string) *types.Named {
return scope.Lookup(name).(*types.TypeName).Type().(*types.Named)
}
nodeType := lookup("Node")
ptrNameType := types.NewPointer(lookup("Name"))
ntypeType := lookup("Ntype")
nodesType := lookup("Nodes")
slicePtrCaseClauseType := types.NewSlice(types.NewPointer(lookup("CaseClause")))
slicePtrCommClauseType := types.NewSlice(types.NewPointer(lookup("CommClause")))
ptrFieldType := types.NewPointer(lookup("Field"))
slicePtrFieldType := types.NewSlice(ptrFieldType)
ptrIdentType := types.NewPointer(lookup("Ident"))
var buf bytes.Buffer
fmt.Fprintln(&buf, "// Code generated by mknode.go. DO NOT EDIT.") fmt.Fprintln(&buf, "// Code generated by mknode.go. DO NOT EDIT.")
fmt.Fprintln(&buf) fmt.Fprintln(&buf)
fmt.Fprintln(&buf, "package ir") fmt.Fprintln(&buf, "package ir")
fmt.Fprintln(&buf) fmt.Fprintln(&buf)
fmt.Fprintln(&buf, `import "fmt"`) fmt.Fprintln(&buf, `import "fmt"`)
scope := irPkg.Scope()
for _, name := range scope.Names() { for _, name := range scope.Names() {
if strings.HasPrefix(name, "mini") {
continue
}
obj, ok := scope.Lookup(name).(*types.TypeName) obj, ok := scope.Lookup(name).(*types.TypeName)
if !ok { if !ok {
continue continue
} }
typ := obj.Type().(*types.Named)
typName := obj.Name() if !implementsNode(types.NewPointer(typ)) {
typ, ok := obj.Type().(*types.Named).Underlying().(*types.Struct)
if !ok {
continue
}
if strings.HasPrefix(typName, "mini") || !hasMiniNode(typ) {
continue continue
} }
@ -71,77 +58,31 @@ func main() {
fmt.Fprintf(&buf, "func (n *%s) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }\n", name) fmt.Fprintf(&buf, "func (n *%s) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }\n", name)
switch name { switch name {
case "Name": case "Name", "Func":
fmt.Fprintf(&buf, "func (n *%s) copy() Node {panic(\"%s.copy\")}\n", name, name) // Too specialized to automate.
default: continue
fmt.Fprintf(&buf, "func (n *%s) copy() Node { c := *n\n", name)
forNodeFields(typName, typ, func(name string, is func(types.Type) bool) {
switch {
case is(nodesType):
fmt.Fprintf(&buf, "c.%s = c.%s.Copy()\n", name, name)
case is(slicePtrCaseClauseType):
fmt.Fprintf(&buf, "c.%s = copyCases(c.%s)\n", name, name)
case is(slicePtrCommClauseType):
fmt.Fprintf(&buf, "c.%s = copyComms(c.%s)\n", name, name)
case is(ptrFieldType):
fmt.Fprintf(&buf, "if c.%s != nil { c.%s = c.%s.copy() }\n", name, name, name)
case is(slicePtrFieldType):
fmt.Fprintf(&buf, "c.%s = copyFields(c.%s)\n", name, name)
}
})
fmt.Fprintf(&buf, "return &c }\n")
} }
fmt.Fprintf(&buf, "func (n *%s) doChildren(do func(Node) error) error { var err error\n", name) forNodeFields(typ,
forNodeFields(typName, typ, func(name string, is func(types.Type) bool) { "func (n *%[1]s) copy() Node { c := *n\n",
switch { "",
case is(ptrIdentType), is(ptrNameType): "c.%[1]s = copy%[2]s(c.%[1]s)",
fmt.Fprintf(&buf, "if n.%s != nil { err = maybeDo(n.%s, err, do) }\n", name, name) "return &c }\n")
case is(nodeType), is(ntypeType):
fmt.Fprintf(&buf, "err = maybeDo(n.%s, err, do)\n", name)
case is(nodesType):
fmt.Fprintf(&buf, "err = maybeDoList(n.%s, err, do)\n", name)
case is(slicePtrCaseClauseType):
fmt.Fprintf(&buf, "err = maybeDoCases(n.%s, err, do)\n", name)
case is(slicePtrCommClauseType):
fmt.Fprintf(&buf, "err = maybeDoComms(n.%s, err, do)\n", name)
case is(ptrFieldType):
fmt.Fprintf(&buf, "err = maybeDoField(n.%s, err, do)\n", name)
case is(slicePtrFieldType):
fmt.Fprintf(&buf, "err = maybeDoFields(n.%s, err, do)\n", name)
}
})
fmt.Fprintf(&buf, "return err }\n")
fmt.Fprintf(&buf, "func (n *%s) editChildren(edit func(Node) Node) {\n", name) forNodeFields(typ,
forNodeFields(typName, typ, func(name string, is func(types.Type) bool) { "func (n *%[1]s) doChildren(do func(Node) error) error {\n",
switch { "if n.%[1]s != nil { if err := do(n.%[1]s); err != nil { return err } }",
case is(ptrIdentType): "if err := do%[2]s(n.%[1]s, do); err != nil { return err }",
fmt.Fprintf(&buf, "if n.%s != nil { n.%s = edit(n.%s).(*Ident) }\n", name, name, name) "return nil }\n")
case is(ptrNameType):
fmt.Fprintf(&buf, "if n.%s != nil { n.%s = edit(n.%s).(*Name) }\n", name, name, name) forNodeFields(typ,
case is(nodeType): "func (n *%[1]s) editChildren(edit func(Node) Node) {\n",
fmt.Fprintf(&buf, "n.%s = maybeEdit(n.%s, edit)\n", name, name) "if n.%[1]s != nil { n.%[1]s = edit(n.%[1]s).(%[2]s) }",
case is(ntypeType): "edit%[2]s(n.%[1]s, edit)",
fmt.Fprintf(&buf, "n.%s = toNtype(maybeEdit(n.%s, edit))\n", name, name) "}\n")
case is(nodesType):
fmt.Fprintf(&buf, "editList(n.%s, edit)\n", name)
case is(slicePtrCaseClauseType):
fmt.Fprintf(&buf, "editCases(n.%s, edit)\n", name)
case is(slicePtrCommClauseType):
fmt.Fprintf(&buf, "editComms(n.%s, edit)\n", name)
case is(ptrFieldType):
fmt.Fprintf(&buf, "editField(n.%s, edit)\n", name)
case is(slicePtrFieldType):
fmt.Fprintf(&buf, "editFields(n.%s, edit)\n", name)
}
})
fmt.Fprintf(&buf, "}\n")
} }
for _, name := range []string{"CaseClause", "CommClause"} { makeHelpers()
sliceHelper(&buf, name)
}
out, err := format.Source(buf.Bytes()) out, err := format.Source(buf.Bytes())
if err != nil { if err != nil {
@ -155,20 +96,32 @@ func main() {
} }
} }
func sliceHelper(buf *bytes.Buffer, name string) { // needHelper maps needed slice helpers from their base name to their
tmpl := fmt.Sprintf(` // respective slice-element type.
func copy%[1]ss(list []*%[2]s) []*%[2]s { var needHelper = map[string]string{}
func makeHelpers() {
var names []string
for name := range needHelper {
names = append(names, name)
}
sort.Strings(names)
for _, name := range names {
fmt.Fprintf(&buf, sliceHelperTmpl, name, needHelper[name])
}
}
const sliceHelperTmpl = `
func copy%[1]s(list []%[2]s) []%[2]s {
if list == nil { if list == nil {
return nil return nil
} }
c := make([]*%[2]s, len(list)) c := make([]%[2]s, len(list))
copy(c, list) copy(c, list)
return c return c
} }
func maybeDo%[1]ss(list []*%[2]s, err error, do func(Node) error) error { func do%[1]s(list []%[2]s, do func(Node) error) error {
if err != nil {
return err
}
for _, x := range list { for _, x := range list {
if x != nil { if x != nil {
if err := do(x); err != nil { if err := do(x); err != nil {
@ -178,51 +131,98 @@ func maybeDo%[1]ss(list []*%[2]s, err error, do func(Node) error) error {
} }
return nil return nil
} }
func edit%[1]ss(list []*%[2]s, edit func(Node) Node) { func edit%[1]s(list []%[2]s, edit func(Node) Node) {
for i, x := range list { for i, x := range list {
if x != nil { if x != nil {
list[i] = edit(x).(*%[2]s) list[i] = edit(x).(%[2]s)
} }
} }
} }
`, strings.TrimSuffix(name, "Clause"), name) `
fmt.Fprintln(buf, tmpl)
func forNodeFields(named *types.Named, prologue, singleTmpl, sliceTmpl, epilogue string) {
fmt.Fprintf(&buf, prologue, named.Obj().Name())
anyField(named.Underlying().(*types.Struct), func(f *types.Var) bool {
if f.Embedded() {
return false
}
name, typ := f.Name(), f.Type()
slice, _ := typ.Underlying().(*types.Slice)
if slice != nil {
typ = slice.Elem()
}
tmpl, what := singleTmpl, types.TypeString(typ, types.RelativeTo(irPkg))
if implementsNode(typ) {
if slice != nil {
helper := strings.TrimPrefix(what, "*") + "s"
needHelper[helper] = what
tmpl, what = sliceTmpl, helper
}
} else if what == "*Field" {
// Special case for *Field.
tmpl = sliceTmpl
if slice != nil {
what = "Fields"
} else {
what = "Field"
}
} else {
return false
}
if tmpl == "" {
return false
}
// Allow template to not use all arguments without
// upsetting fmt.Printf.
s := fmt.Sprintf(tmpl+"\x00 %[1]s %[2]s", name, what)
fmt.Fprintln(&buf, s[:strings.LastIndex(s, "\x00")])
return false
})
fmt.Fprintf(&buf, epilogue)
} }
func forNodeFields(typName string, typ *types.Struct, f func(name string, is func(types.Type) bool)) { func implementsNode(typ types.Type) bool {
for i, n := 0, typ.NumFields(); i < n; i++ { if _, ok := typ.Underlying().(*types.Interface); ok {
v := typ.Field(i) // TODO(mdempsky): Check the interface implements Node.
if v.Embedded() { // Worst case, node_gen.go will fail to compile if we're wrong.
if typ, ok := v.Type().Underlying().(*types.Struct); ok { return true
forNodeFields(typName, typ, f)
continue
}
}
switch typName {
case "Func":
if strings.ToLower(strings.TrimSuffix(v.Name(), "_")) != "body" {
continue
}
case "Name":
continue
}
switch v.Name() {
case "orig":
continue
}
f(v.Name(), func(t types.Type) bool { return types.Identical(t, v.Type()) })
} }
if ptr, ok := typ.(*types.Pointer); ok {
if str, ok := ptr.Elem().Underlying().(*types.Struct); ok {
return anyField(str, func(f *types.Var) bool {
return f.Embedded() && f.Name() == "miniNode"
})
}
}
return false
} }
func hasMiniNode(typ *types.Struct) bool { func anyField(typ *types.Struct, pred func(f *types.Var) bool) bool {
for i, n := 0, typ.NumFields(); i < n; i++ { for i, n := 0, typ.NumFields(); i < n; i++ {
v := typ.Field(i) if value, ok := reflect.StructTag(typ.Tag(i)).Lookup("mknode"); ok {
if v.Name() == "miniNode" { if value != "-" {
panic(fmt.Sprintf("unexpected tag value: %q", value))
}
continue
}
f := typ.Field(i)
if pred(f) {
return true return true
} }
if v.Embedded() { if f.Embedded() {
if typ, ok := v.Type().Underlying().(*types.Struct); ok && hasMiniNode(typ) { if typ, ok := f.Type().Underlying().(*types.Struct); ok {
return true if anyField(typ, pred) {
return true
}
} }
} }
} }

View file

@ -143,6 +143,10 @@ type Name struct {
func (n *Name) isExpr() {} 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) {}
// CloneName makes a cloned copy of the name. // CloneName makes a cloned copy of the name.
// It's not ir.Copy(n) because in general that operation is a mistake on names, // It's not ir.Copy(n) because in general that operation is a mistake on names,
// which uniquely identify variables. // which uniquely identify variables.

File diff suppressed because it is too large Load diff

View file

@ -322,8 +322,8 @@ func NewRangeStmt(pos src.XPos, key, value, x Node, body []Node) *RangeStmt {
// A ReturnStmt is a return statement. // A ReturnStmt is a return statement.
type ReturnStmt struct { type ReturnStmt struct {
miniStmt miniStmt
orig Node // for typecheckargs rewrite origNode // for typecheckargs rewrite
Results Nodes // return list Results Nodes // return list
} }
func NewReturnStmt(pos src.XPos, results []Node) *ReturnStmt { func NewReturnStmt(pos src.XPos, results []Node) *ReturnStmt {
@ -335,9 +335,6 @@ func NewReturnStmt(pos src.XPos, results []Node) *ReturnStmt {
return n return n
} }
func (n *ReturnStmt) Orig() Node { return n.orig }
func (n *ReturnStmt) SetOrig(x Node) { n.orig = x }
// A SelectStmt is a block: { Cases }. // A SelectStmt is a block: { Cases }.
type SelectStmt struct { type SelectStmt struct {
miniStmt miniStmt

View file

@ -185,45 +185,32 @@ func (f *Field) String() string {
return typ return typ
} }
func (f *Field) copy() *Field { // TODO(mdempsky): Make Field a Node again so these can be generated?
// Fields are Nodes in go/ast and cmd/compile/internal/syntax.
func copyField(f *Field) *Field {
if f == nil {
return nil
}
c := *f c := *f
return &c return &c
} }
func doField(f *Field, do func(Node) error) error {
func copyFields(list []*Field) []*Field { if f == nil {
out := make([]*Field, len(list)) return nil
copy(out, list)
for i, f := range out {
out[i] = f.copy()
} }
return out if f.Decl != nil {
} if err := do(f.Decl); err != nil {
func maybeDoField(f *Field, err error, do func(Node) error) error {
if f != nil {
if err == nil && f.Decl != nil {
err = do(f.Decl)
}
if err == nil && f.Ntype != nil {
err = do(f.Ntype)
}
}
return err
}
func maybeDoFields(list []*Field, err error, do func(Node) error) error {
if err != nil {
return err
}
for _, f := range list {
err = maybeDoField(f, err, do)
if err != nil {
return err return err
} }
} }
return err if f.Ntype != nil {
if err := do(f.Ntype); err != nil {
return err
}
}
return nil
} }
func editField(f *Field, edit func(Node) Node) { func editField(f *Field, edit func(Node) Node) {
if f == nil { if f == nil {
return return
@ -232,10 +219,25 @@ func editField(f *Field, edit func(Node) Node) {
f.Decl = edit(f.Decl).(*Name) f.Decl = edit(f.Decl).(*Name)
} }
if f.Ntype != nil { if f.Ntype != nil {
f.Ntype = toNtype(edit(f.Ntype)) f.Ntype = edit(f.Ntype).(Ntype)
} }
} }
func copyFields(list []*Field) []*Field {
out := make([]*Field, len(list))
for i, f := range list {
out[i] = copyField(f)
}
return out
}
func doFields(list []*Field, do func(Node) error) error {
for _, x := range list {
if err := doField(x, do); err != nil {
return err
}
}
return nil
}
func editFields(list []*Field, edit func(Node) Node) { func editFields(list []*Field, edit func(Node) Node) {
for _, f := range list { for _, f := range list {
editField(f, edit) editField(f, edit)

View file

@ -106,14 +106,7 @@ func DoChildren(n Node, do func(Node) error) error {
// Note that DoList only calls do on the nodes in the list, not their children. // 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. // If x's children should be processed, do(x) must call DoChildren(x, do) itself.
func DoList(list Nodes, do func(Node) error) error { func DoList(list Nodes, do func(Node) error) error {
for _, x := range list { return doNodes(list, do)
if x != nil {
if err := do(x); err != nil {
return err
}
}
}
return nil
} }
// Visit visits each non-nil node x in the IR tree rooted at n // Visit visits each non-nil node x in the IR tree rooted at n
@ -210,16 +203,3 @@ func EditChildren(n Node, edit func(Node) Node) {
} }
n.editChildren(edit) n.editChildren(edit)
} }
// editList calls edit on each non-nil node x in the list,
// saving the result of edit back into the list.
//
// Note that editList only calls edit on the nodes in the list, not their children.
// If x's children should be processed, edit(x) must call EditChildren(x, edit) itself.
func editList(list Nodes, edit func(Node) Node) {
for i, x := range list {
if x != nil {
list[i] = edit(x)
}
}
}