]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.regabi] cmd/compile: generalize ir/mknode.go
authorMatthew Dempsky <mdempsky@google.com>
Tue, 29 Dec 2020 23:36:48 +0000 (15:36 -0800)
committerMatthew Dempsky <mdempsky@google.com>
Wed, 30 Dec 2020 03:38:02 +0000 (03:38 +0000)
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>
src/cmd/compile/internal/ir/copy.go
src/cmd/compile/internal/ir/expr.go
src/cmd/compile/internal/ir/func.go
src/cmd/compile/internal/ir/mknode.go
src/cmd/compile/internal/ir/name.go
src/cmd/compile/internal/ir/node_gen.go
src/cmd/compile/internal/ir/stmt.go
src/cmd/compile/internal/ir/type.go
src/cmd/compile/internal/ir/visit.go

index 0ab355f76749c857981711340489806a0153752e..7da9b24940ff9d215d2c51e90b92a5595121da00 100644 (file)
@@ -25,6 +25,14 @@ type OrigNode interface {
        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.
 // If n implements OrigNode, Orig returns n.Orig().
 // Otherwise Orig returns n itself.
index a989ce5e01a5e4f71d4227cedb0d9462a212d68a..55e4b61baf04e57bfb7fe3537f32e0a5a3857451 100644 (file)
@@ -14,27 +14,6 @@ import (
        "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.
 type Expr interface {
        Node
@@ -77,16 +56,6 @@ func (n *miniExpr) Init() Nodes           { return n.init }
 func (n *miniExpr) PtrInit() *Nodes       { return &n.init }
 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].
 type AddStringExpr struct {
        miniExpr
@@ -189,7 +158,7 @@ const (
 // A CallExpr is a function call X(Args).
 type CallExpr struct {
        miniExpr
-       orig     Node
+       origNode
        X        Node
        Args     Nodes
        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 (n *CallExpr) Orig() Node     { return n.orig }
-func (n *CallExpr) SetOrig(x Node) { n.orig = x }
-
 func (n *CallExpr) SetOp(op Op) {
        switch op {
        default:
@@ -226,7 +192,7 @@ func (n *CallExpr) SetOp(op Op) {
 // A ClosureExpr is a function literal expression.
 type ClosureExpr struct {
        miniExpr
-       Func     *Func
+       Func     *Func `mknode:"-"`
        Prealloc *Name
 }
 
@@ -254,7 +220,7 @@ func NewClosureRead(typ *types.Type, offset int64) *ClosureReadExpr {
 // Before type-checking, the type is Ntype.
 type CompLitExpr struct {
        miniExpr
-       orig     Node
+       origNode
        Ntype    Ntype
        List     Nodes // initialized values
        Prealloc *Name
@@ -270,8 +236,6 @@ func NewCompLitExpr(pos src.XPos, op Op, typ Ntype, list []Node) *CompLitExpr {
        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) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) }
 
@@ -286,14 +250,15 @@ func (n *CompLitExpr) SetOp(op Op) {
 
 type ConstExpr struct {
        miniExpr
-       val  constant.Value
-       orig Node
+       origNode
+       val constant.Value
 }
 
 func NewConstExpr(val constant.Value, orig Node) Node {
-       n := &ConstExpr{orig: orig, val: val}
+       n := &ConstExpr{val: val}
        n.op = OLITERAL
        n.pos = orig.Pos()
+       n.orig = orig
        n.SetType(orig.Type())
        n.SetTypecheck(orig.Typecheck())
        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) Orig() Node          { return n.orig }
-func (n *ConstExpr) SetOrig(orig Node)   { panic(n.no("SetOrig")) }
 func (n *ConstExpr) Val() constant.Value { return n.val }
 
 // A ConvExpr is a conversion Type(X).
@@ -664,9 +627,9 @@ type TypeAssertExpr struct {
 
        // Runtime type information provided by walkDotType.
        // Caution: These aren't always populated; see walkDotType.
-       SrcType *AddrExpr // *runtime._type for X's type
-       DstType *AddrExpr // *runtime._type for Type
-       Itab    *AddrExpr // *runtime.itab for Type implementing X's type
+       SrcType *AddrExpr `mknode:"-"` // *runtime._type for X's type
+       DstType *AddrExpr `mknode:"-"` // *runtime._type for Type
+       Itab    *AddrExpr `mknode:"-"` // *runtime.itab for Type implementing X's type
 }
 
 func NewTypeAssertExpr(pos src.XPos, x Node, typ Ntype) *TypeAssertExpr {
index bffd4dd5ef9b25ac181a3c181cf220de29825a16..32ad37fa8064a0690437aecacc3f59788c130ac7 100644 (file)
@@ -115,6 +115,10 @@ 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 (f *Func) Type() *types.Type  { return f.Nname.Type() }
 func (f *Func) Sym() *types.Sym    { return f.Nname.Sym() }
 func (f *Func) Linksym() *obj.LSym { return f.Nname.Linksym() }
index 755ac6ba8781dd0ee00843178c33720d138244a0..4e26bc5011db426f55c6581b37923e551f7aa7fc 100644 (file)
@@ -13,11 +13,16 @@ import (
        "go/types"
        "io/ioutil"
        "log"
+       "reflect"
+       "sort"
        "strings"
 
        "golang.org/x/tools/go/packages"
 )
 
+var irPkg *types.Package
+var buf bytes.Buffer
+
 func main() {
        cfg := &packages.Config{
                Mode: packages.NeedSyntax | packages.NeedTypes,
@@ -26,44 +31,26 @@ func main() {
        if err != nil {
                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)
        fmt.Fprintln(&buf, "package ir")
        fmt.Fprintln(&buf)
        fmt.Fprintln(&buf, `import "fmt"`)
 
+       scope := irPkg.Scope()
        for _, name := range scope.Names() {
-               obj, ok := scope.Lookup(name).(*types.TypeName)
-               if !ok {
+               if strings.HasPrefix(name, "mini") {
                        continue
                }
 
-               typName := obj.Name()
-               typ, ok := obj.Type().(*types.Named).Underlying().(*types.Struct)
+               obj, ok := scope.Lookup(name).(*types.TypeName)
                if !ok {
                        continue
                }
-
-               if strings.HasPrefix(typName, "mini") || !hasMiniNode(typ) {
+               typ := obj.Type().(*types.Named)
+               if !implementsNode(types.NewPointer(typ)) {
                        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)
 
                switch name {
-               case "Name":
-                       fmt.Fprintf(&buf, "func (n *%s) copy() Node {panic(\"%s.copy\")}\n", name, name)
-               default:
-                       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")
+               case "Name", "Func":
+                       // Too specialized to automate.
+                       continue
                }
 
-               fmt.Fprintf(&buf, "func (n *%s) doChildren(do func(Node) error) error { var err error\n", name)
-               forNodeFields(typName, typ, func(name string, is func(types.Type) bool) {
-                       switch {
-                       case is(ptrIdentType), is(ptrNameType):
-                               fmt.Fprintf(&buf, "if n.%s != nil { err = maybeDo(n.%s, err, do) }\n", name, name)
-                       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(typName, typ, func(name string, is func(types.Type) bool) {
-                       switch {
-                       case is(ptrIdentType):
-                               fmt.Fprintf(&buf, "if n.%s != nil { n.%s = edit(n.%s).(*Ident) }\n", name, name, name)
-                       case is(ptrNameType):
-                               fmt.Fprintf(&buf, "if n.%s != nil { n.%s = edit(n.%s).(*Name) }\n", name, name, name)
-                       case is(nodeType):
-                               fmt.Fprintf(&buf, "n.%s = maybeEdit(n.%s, edit)\n", name, name)
-                       case is(ntypeType):
-                               fmt.Fprintf(&buf, "n.%s = toNtype(maybeEdit(n.%s, edit))\n", name, name)
-                       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")
+               forNodeFields(typ,
+                       "func (n *%[1]s) copy() Node { c := *n\n",
+                       "",
+                       "c.%[1]s = copy%[2]s(c.%[1]s)",
+                       "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")
+
+               forNodeFields(typ,
+                       "func (n *%[1]s) editChildren(edit func(Node) Node) {\n",
+                       "if n.%[1]s != nil { n.%[1]s = edit(n.%[1]s).(%[2]s) }",
+                       "edit%[2]s(n.%[1]s, edit)",
+                       "}\n")
        }
 
-       for _, name := range []string{"CaseClause", "CommClause"} {
-               sliceHelper(&buf, name)
-       }
+       makeHelpers()
 
        out, err := format.Source(buf.Bytes())
        if err != nil {
@@ -155,20 +96,32 @@ func main() {
        }
 }
 
-func sliceHelper(buf *bytes.Buffer, name string) {
-       tmpl := fmt.Sprintf(`
-func copy%[1]ss(list []*%[2]s) []*%[2]s {
+// needHelper maps needed slice helpers from their base name to their
+// respective slice-element type.
+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 {
                return nil
        }
-       c := make([]*%[2]s, len(list))
+       c := make([]%[2]s, len(list))
        copy(c, list)
        return c
 }
-func maybeDo%[1]ss(list []*%[2]s, err error, do func(Node) error) error {
-       if err != nil {
-               return err
-       }
+func do%[1]s(list []%[2]s, do func(Node) error) error {
        for _, x := range list {
                if x != 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
 }
-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 {
                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(typName string, typ *types.Struct, f func(name string, is func(types.Type) bool)) {
-       for i, n := 0, typ.NumFields(); i < n; i++ {
-               v := typ.Field(i)
-               if v.Embedded() {
-                       if typ, ok := v.Type().Underlying().(*types.Struct); ok {
-                               forNodeFields(typName, typ, f)
-                               continue
-                       }
+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()
                }
-               switch typName {
-               case "Func":
-                       if strings.ToLower(strings.TrimSuffix(v.Name(), "_")) != "body" {
-                               continue
+
+               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
                        }
-               case "Name":
-                       continue
+               } else if what == "*Field" {
+                       // Special case for *Field.
+                       tmpl = sliceTmpl
+                       if slice != nil {
+                               what = "Fields"
+                       } else {
+                               what = "Field"
+                       }
+               } else {
+                       return false
                }
-               switch v.Name() {
-               case "orig":
-                       continue
+
+               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 implementsNode(typ types.Type) bool {
+       if _, ok := typ.Underlying().(*types.Interface); ok {
+               // TODO(mdempsky): Check the interface implements Node.
+               // Worst case, node_gen.go will fail to compile if we're wrong.
+               return true
+       }
+
+       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"
+                       })
                }
-               f(v.Name(), func(t types.Type) bool { return types.Identical(t, v.Type()) })
        }
+
+       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++ {
-               v := typ.Field(i)
-               if v.Name() == "miniNode" {
+               if value, ok := reflect.StructTag(typ.Tag(i)).Lookup("mknode"); ok {
+                       if value != "-" {
+                               panic(fmt.Sprintf("unexpected tag value: %q", value))
+                       }
+                       continue
+               }
+
+               f := typ.Field(i)
+               if pred(f) {
                        return true
                }
-               if v.Embedded() {
-                       if typ, ok := v.Type().Underlying().(*types.Struct); ok && hasMiniNode(typ) {
-                               return true
+               if f.Embedded() {
+                       if typ, ok := f.Type().Underlying().(*types.Struct); ok {
+                               if anyField(typ, pred) {
+                                       return true
+                               }
                        }
                }
        }
index d6135ee29a828df941b5e110daace13e87bde4bf..b12e833f73fe8fa601a79fe4f096c0203059668f 100644 (file)
@@ -143,6 +143,10 @@ 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)    {}
+
 // CloneName makes a cloned copy of the name.
 // It's not ir.Copy(n) because in general that operation is a mistake on names,
 // which uniquely identify variables.
index 4c48e82d779a39e9d5b28ae38766136306a61b54..21e4eff9fbe77167c00089064946f7135042edb6 100644 (file)
@@ -7,22 +7,27 @@ import "fmt"
 func (n *AddStringExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *AddStringExpr) copy() Node {
        c := *n
-       c.init = c.init.Copy()
-       c.List = c.List.Copy()
+       c.init = copyNodes(c.init)
+       c.List = copyNodes(c.List)
        return &c
 }
 func (n *AddStringExpr) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDoList(n.List, err, do)
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if err := doNodes(n.List, do); err != nil {
+               return err
+       }
        if n.Prealloc != nil {
-               err = maybeDo(n.Prealloc, err, do)
+               if err := do(n.Prealloc); err != nil {
+                       return err
+               }
        }
-       return err
+       return nil
 }
 func (n *AddStringExpr) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       editList(n.List, edit)
+       editNodes(n.init, edit)
+       editNodes(n.List, edit)
        if n.Prealloc != nil {
                n.Prealloc = edit(n.Prealloc).(*Name)
        }
@@ -31,21 +36,30 @@ func (n *AddStringExpr) editChildren(edit func(Node) Node) {
 func (n *AddrExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *AddrExpr) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *AddrExpr) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDo(n.X, err, do)
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if n.X != nil {
+               if err := do(n.X); err != nil {
+                       return err
+               }
+       }
        if n.Prealloc != nil {
-               err = maybeDo(n.Prealloc, err, do)
+               if err := do(n.Prealloc); err != nil {
+                       return err
+               }
        }
-       return err
+       return nil
 }
 func (n *AddrExpr) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       n.X = maybeEdit(n.X, edit)
+       editNodes(n.init, edit)
+       if n.X != nil {
+               n.X = edit(n.X).(Node)
+       }
        if n.Prealloc != nil {
                n.Prealloc = edit(n.Prealloc).(*Name)
        }
@@ -57,193 +71,273 @@ func (n *ArrayType) copy() Node {
        return &c
 }
 func (n *ArrayType) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDo(n.Len, err, do)
-       err = maybeDo(n.Elem, err, do)
-       return err
+       if n.Len != nil {
+               if err := do(n.Len); err != nil {
+                       return err
+               }
+       }
+       if n.Elem != nil {
+               if err := do(n.Elem); err != nil {
+                       return err
+               }
+       }
+       return nil
 }
 func (n *ArrayType) editChildren(edit func(Node) Node) {
-       n.Len = maybeEdit(n.Len, edit)
-       n.Elem = toNtype(maybeEdit(n.Elem, edit))
+       if n.Len != nil {
+               n.Len = edit(n.Len).(Node)
+       }
+       if n.Elem != nil {
+               n.Elem = edit(n.Elem).(Ntype)
+       }
 }
 
 func (n *AssignListStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *AssignListStmt) copy() Node {
        c := *n
-       c.init = c.init.Copy()
-       c.Lhs = c.Lhs.Copy()
-       c.Rhs = c.Rhs.Copy()
+       c.init = copyNodes(c.init)
+       c.Lhs = copyNodes(c.Lhs)
+       c.Rhs = copyNodes(c.Rhs)
        return &c
 }
 func (n *AssignListStmt) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDoList(n.Lhs, err, do)
-       err = maybeDoList(n.Rhs, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if err := doNodes(n.Lhs, do); err != nil {
+               return err
+       }
+       if err := doNodes(n.Rhs, do); err != nil {
+               return err
+       }
+       return nil
 }
 func (n *AssignListStmt) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       editList(n.Lhs, edit)
-       editList(n.Rhs, edit)
+       editNodes(n.init, edit)
+       editNodes(n.Lhs, edit)
+       editNodes(n.Rhs, edit)
 }
 
 func (n *AssignOpStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *AssignOpStmt) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *AssignOpStmt) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDo(n.X, err, do)
-       err = maybeDo(n.Y, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if n.X != nil {
+               if err := do(n.X); err != nil {
+                       return err
+               }
+       }
+       if n.Y != nil {
+               if err := do(n.Y); err != nil {
+                       return err
+               }
+       }
+       return nil
 }
 func (n *AssignOpStmt) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       n.X = maybeEdit(n.X, edit)
-       n.Y = maybeEdit(n.Y, edit)
+       editNodes(n.init, edit)
+       if n.X != nil {
+               n.X = edit(n.X).(Node)
+       }
+       if n.Y != nil {
+               n.Y = edit(n.Y).(Node)
+       }
 }
 
 func (n *AssignStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *AssignStmt) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *AssignStmt) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDo(n.X, err, do)
-       err = maybeDo(n.Y, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if n.X != nil {
+               if err := do(n.X); err != nil {
+                       return err
+               }
+       }
+       if n.Y != nil {
+               if err := do(n.Y); err != nil {
+                       return err
+               }
+       }
+       return nil
 }
 func (n *AssignStmt) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       n.X = maybeEdit(n.X, edit)
-       n.Y = maybeEdit(n.Y, edit)
+       editNodes(n.init, edit)
+       if n.X != nil {
+               n.X = edit(n.X).(Node)
+       }
+       if n.Y != nil {
+               n.Y = edit(n.Y).(Node)
+       }
 }
 
 func (n *BasicLit) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *BasicLit) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *BasicLit) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       return nil
 }
 func (n *BasicLit) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
+       editNodes(n.init, edit)
 }
 
 func (n *BinaryExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *BinaryExpr) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *BinaryExpr) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDo(n.X, err, do)
-       err = maybeDo(n.Y, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if n.X != nil {
+               if err := do(n.X); err != nil {
+                       return err
+               }
+       }
+       if n.Y != nil {
+               if err := do(n.Y); err != nil {
+                       return err
+               }
+       }
+       return nil
 }
 func (n *BinaryExpr) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       n.X = maybeEdit(n.X, edit)
-       n.Y = maybeEdit(n.Y, edit)
+       editNodes(n.init, edit)
+       if n.X != nil {
+               n.X = edit(n.X).(Node)
+       }
+       if n.Y != nil {
+               n.Y = edit(n.Y).(Node)
+       }
 }
 
 func (n *BlockStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *BlockStmt) copy() Node {
        c := *n
-       c.init = c.init.Copy()
-       c.List = c.List.Copy()
+       c.init = copyNodes(c.init)
+       c.List = copyNodes(c.List)
        return &c
 }
 func (n *BlockStmt) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDoList(n.List, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if err := doNodes(n.List, do); err != nil {
+               return err
+       }
+       return nil
 }
 func (n *BlockStmt) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       editList(n.List, edit)
+       editNodes(n.init, edit)
+       editNodes(n.List, edit)
 }
 
 func (n *BranchStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *BranchStmt) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *BranchStmt) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       return nil
 }
 func (n *BranchStmt) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
+       editNodes(n.init, edit)
 }
 
 func (n *CallExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *CallExpr) copy() Node {
        c := *n
-       c.init = c.init.Copy()
-       c.Args = c.Args.Copy()
-       c.Rargs = c.Rargs.Copy()
-       c.Body = c.Body.Copy()
+       c.init = copyNodes(c.init)
+       c.Args = copyNodes(c.Args)
+       c.Rargs = copyNodes(c.Rargs)
+       c.Body = copyNodes(c.Body)
        return &c
 }
 func (n *CallExpr) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDo(n.X, err, do)
-       err = maybeDoList(n.Args, err, do)
-       err = maybeDoList(n.Rargs, err, do)
-       err = maybeDoList(n.Body, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if n.X != nil {
+               if err := do(n.X); err != nil {
+                       return err
+               }
+       }
+       if err := doNodes(n.Args, do); err != nil {
+               return err
+       }
+       if err := doNodes(n.Rargs, do); err != nil {
+               return err
+       }
+       if err := doNodes(n.Body, do); err != nil {
+               return err
+       }
+       return nil
 }
 func (n *CallExpr) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       n.X = maybeEdit(n.X, edit)
-       editList(n.Args, edit)
-       editList(n.Rargs, edit)
-       editList(n.Body, edit)
+       editNodes(n.init, edit)
+       if n.X != nil {
+               n.X = edit(n.X).(Node)
+       }
+       editNodes(n.Args, edit)
+       editNodes(n.Rargs, edit)
+       editNodes(n.Body, edit)
 }
 
 func (n *CaseClause) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *CaseClause) copy() Node {
        c := *n
-       c.init = c.init.Copy()
-       c.List = c.List.Copy()
-       c.Body = c.Body.Copy()
+       c.init = copyNodes(c.init)
+       c.List = copyNodes(c.List)
+       c.Body = copyNodes(c.Body)
        return &c
 }
 func (n *CaseClause) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
        if n.Var != nil {
-               err = maybeDo(n.Var, err, do)
+               if err := do(n.Var); err != nil {
+                       return err
+               }
+       }
+       if err := doNodes(n.List, do); err != nil {
+               return err
+       }
+       if err := doNodes(n.Body, do); err != nil {
+               return err
        }
-       err = maybeDoList(n.List, err, do)
-       err = maybeDoList(n.Body, err, do)
-       return err
+       return nil
 }
 func (n *CaseClause) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
+       editNodes(n.init, edit)
        if n.Var != nil {
                n.Var = edit(n.Var).(*Name)
        }
-       editList(n.List, edit)
-       editList(n.Body, edit)
+       editNodes(n.List, edit)
+       editNodes(n.Body, edit)
 }
 
 func (n *ChanType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -252,30 +346,38 @@ func (n *ChanType) copy() Node {
        return &c
 }
 func (n *ChanType) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDo(n.Elem, err, do)
-       return err
+       if n.Elem != nil {
+               if err := do(n.Elem); err != nil {
+                       return err
+               }
+       }
+       return nil
 }
 func (n *ChanType) editChildren(edit func(Node) Node) {
-       n.Elem = toNtype(maybeEdit(n.Elem, edit))
+       if n.Elem != nil {
+               n.Elem = edit(n.Elem).(Ntype)
+       }
 }
 
 func (n *ClosureExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *ClosureExpr) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *ClosureExpr) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
        if n.Prealloc != nil {
-               err = maybeDo(n.Prealloc, err, do)
+               if err := do(n.Prealloc); err != nil {
+                       return err
+               }
        }
-       return err
+       return nil
 }
 func (n *ClosureExpr) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
+       editNodes(n.init, edit)
        if n.Prealloc != nil {
                n.Prealloc = edit(n.Prealloc).(*Name)
        }
@@ -284,59 +386,80 @@ func (n *ClosureExpr) editChildren(edit func(Node) Node) {
 func (n *ClosureReadExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *ClosureReadExpr) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *ClosureReadExpr) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       return nil
 }
 func (n *ClosureReadExpr) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
+       editNodes(n.init, edit)
 }
 
 func (n *CommClause) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *CommClause) copy() Node {
        c := *n
-       c.init = c.init.Copy()
-       c.Body = c.Body.Copy()
+       c.init = copyNodes(c.init)
+       c.Body = copyNodes(c.Body)
        return &c
 }
 func (n *CommClause) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDo(n.Comm, err, do)
-       err = maybeDoList(n.Body, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if n.Comm != nil {
+               if err := do(n.Comm); err != nil {
+                       return err
+               }
+       }
+       if err := doNodes(n.Body, do); err != nil {
+               return err
+       }
+       return nil
 }
 func (n *CommClause) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       n.Comm = maybeEdit(n.Comm, edit)
-       editList(n.Body, edit)
+       editNodes(n.init, edit)
+       if n.Comm != nil {
+               n.Comm = edit(n.Comm).(Node)
+       }
+       editNodes(n.Body, edit)
 }
 
 func (n *CompLitExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *CompLitExpr) copy() Node {
        c := *n
-       c.init = c.init.Copy()
-       c.List = c.List.Copy()
+       c.init = copyNodes(c.init)
+       c.List = copyNodes(c.List)
        return &c
 }
 func (n *CompLitExpr) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDo(n.Ntype, err, do)
-       err = maybeDoList(n.List, err, do)
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if n.Ntype != nil {
+               if err := do(n.Ntype); err != nil {
+                       return err
+               }
+       }
+       if err := doNodes(n.List, do); err != nil {
+               return err
+       }
        if n.Prealloc != nil {
-               err = maybeDo(n.Prealloc, err, do)
+               if err := do(n.Prealloc); err != nil {
+                       return err
+               }
        }
-       return err
+       return nil
 }
 func (n *CompLitExpr) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       n.Ntype = toNtype(maybeEdit(n.Ntype, edit))
-       editList(n.List, edit)
+       editNodes(n.init, edit)
+       if n.Ntype != nil {
+               n.Ntype = edit(n.Ntype).(Ntype)
+       }
+       editNodes(n.List, edit)
        if n.Prealloc != nil {
                n.Prealloc = edit(n.Prealloc).(*Name)
        }
@@ -345,33 +468,41 @@ func (n *CompLitExpr) editChildren(edit func(Node) Node) {
 func (n *ConstExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *ConstExpr) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *ConstExpr) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       return nil
 }
 func (n *ConstExpr) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
+       editNodes(n.init, edit)
 }
 
 func (n *ConvExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *ConvExpr) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *ConvExpr) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDo(n.X, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if n.X != nil {
+               if err := do(n.X); err != nil {
+                       return err
+               }
+       }
+       return nil
 }
 func (n *ConvExpr) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       n.X = maybeEdit(n.X, edit)
+       editNodes(n.init, edit)
+       if n.X != nil {
+               n.X = edit(n.X).(Node)
+       }
 }
 
 func (n *Decl) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -380,11 +511,12 @@ func (n *Decl) copy() Node {
        return &c
 }
 func (n *Decl) doChildren(do func(Node) error) error {
-       var err error
        if n.X != nil {
-               err = maybeDo(n.X, err, do)
+               if err := do(n.X); err != nil {
+                       return err
+               }
        }
-       return err
+       return nil
 }
 func (n *Decl) editChildren(edit func(Node) Node) {
        if n.X != nil {
@@ -395,59 +527,66 @@ func (n *Decl) editChildren(edit func(Node) Node) {
 func (n *ForStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *ForStmt) copy() Node {
        c := *n
-       c.init = c.init.Copy()
-       c.Late = c.Late.Copy()
-       c.Body = c.Body.Copy()
+       c.init = copyNodes(c.init)
+       c.Late = copyNodes(c.Late)
+       c.Body = copyNodes(c.Body)
        return &c
 }
 func (n *ForStmt) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDo(n.Cond, err, do)
-       err = maybeDoList(n.Late, err, do)
-       err = maybeDo(n.Post, err, do)
-       err = maybeDoList(n.Body, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if n.Cond != nil {
+               if err := do(n.Cond); err != nil {
+                       return err
+               }
+       }
+       if err := doNodes(n.Late, do); err != nil {
+               return err
+       }
+       if n.Post != nil {
+               if err := do(n.Post); err != nil {
+                       return err
+               }
+       }
+       if err := doNodes(n.Body, do); err != nil {
+               return err
+       }
+       return nil
 }
 func (n *ForStmt) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       n.Cond = maybeEdit(n.Cond, edit)
-       editList(n.Late, edit)
-       n.Post = maybeEdit(n.Post, edit)
-       editList(n.Body, edit)
+       editNodes(n.init, edit)
+       if n.Cond != nil {
+               n.Cond = edit(n.Cond).(Node)
+       }
+       editNodes(n.Late, edit)
+       if n.Post != nil {
+               n.Post = edit(n.Post).(Node)
+       }
+       editNodes(n.Body, edit)
 }
 
 func (n *Func) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
-func (n *Func) copy() Node {
-       c := *n
-       c.Body = c.Body.Copy()
-       return &c
-}
-func (n *Func) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.Body, err, do)
-       return err
-}
-func (n *Func) editChildren(edit func(Node) Node) {
-       editList(n.Body, edit)
-}
 
 func (n *FuncType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *FuncType) copy() Node {
        c := *n
-       if c.Recv != nil {
-               c.Recv = c.Recv.copy()
-       }
+       c.Recv = copyField(c.Recv)
        c.Params = copyFields(c.Params)
        c.Results = copyFields(c.Results)
        return &c
 }
 func (n *FuncType) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoField(n.Recv, err, do)
-       err = maybeDoFields(n.Params, err, do)
-       err = maybeDoFields(n.Results, err, do)
-       return err
+       if err := doField(n.Recv, do); err != nil {
+               return err
+       }
+       if err := doFields(n.Params, do); err != nil {
+               return err
+       }
+       if err := doFields(n.Results, do); err != nil {
+               return err
+       }
+       return nil
 }
 func (n *FuncType) editChildren(edit func(Node) Node) {
        editField(n.Recv, edit)
@@ -458,111 +597,149 @@ func (n *FuncType) editChildren(edit func(Node) Node) {
 func (n *GoDeferStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *GoDeferStmt) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *GoDeferStmt) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDo(n.Call, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if n.Call != nil {
+               if err := do(n.Call); err != nil {
+                       return err
+               }
+       }
+       return nil
 }
 func (n *GoDeferStmt) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       n.Call = maybeEdit(n.Call, edit)
+       editNodes(n.init, edit)
+       if n.Call != nil {
+               n.Call = edit(n.Call).(Node)
+       }
 }
 
 func (n *Ident) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *Ident) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *Ident) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       return nil
 }
 func (n *Ident) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
+       editNodes(n.init, edit)
 }
 
 func (n *IfStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *IfStmt) copy() Node {
        c := *n
-       c.init = c.init.Copy()
-       c.Body = c.Body.Copy()
-       c.Else = c.Else.Copy()
+       c.init = copyNodes(c.init)
+       c.Body = copyNodes(c.Body)
+       c.Else = copyNodes(c.Else)
        return &c
 }
 func (n *IfStmt) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDo(n.Cond, err, do)
-       err = maybeDoList(n.Body, err, do)
-       err = maybeDoList(n.Else, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if n.Cond != nil {
+               if err := do(n.Cond); err != nil {
+                       return err
+               }
+       }
+       if err := doNodes(n.Body, do); err != nil {
+               return err
+       }
+       if err := doNodes(n.Else, do); err != nil {
+               return err
+       }
+       return nil
 }
 func (n *IfStmt) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       n.Cond = maybeEdit(n.Cond, edit)
-       editList(n.Body, edit)
-       editList(n.Else, edit)
+       editNodes(n.init, edit)
+       if n.Cond != nil {
+               n.Cond = edit(n.Cond).(Node)
+       }
+       editNodes(n.Body, edit)
+       editNodes(n.Else, edit)
 }
 
 func (n *IndexExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *IndexExpr) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *IndexExpr) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDo(n.X, err, do)
-       err = maybeDo(n.Index, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if n.X != nil {
+               if err := do(n.X); err != nil {
+                       return err
+               }
+       }
+       if n.Index != nil {
+               if err := do(n.Index); err != nil {
+                       return err
+               }
+       }
+       return nil
 }
 func (n *IndexExpr) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       n.X = maybeEdit(n.X, edit)
-       n.Index = maybeEdit(n.Index, edit)
+       editNodes(n.init, edit)
+       if n.X != nil {
+               n.X = edit(n.X).(Node)
+       }
+       if n.Index != nil {
+               n.Index = edit(n.Index).(Node)
+       }
 }
 
 func (n *InlineMarkStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *InlineMarkStmt) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *InlineMarkStmt) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       return nil
 }
 func (n *InlineMarkStmt) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
+       editNodes(n.init, edit)
 }
 
 func (n *InlinedCallExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *InlinedCallExpr) copy() Node {
        c := *n
-       c.init = c.init.Copy()
-       c.Body = c.Body.Copy()
-       c.ReturnVars = c.ReturnVars.Copy()
+       c.init = copyNodes(c.init)
+       c.Body = copyNodes(c.Body)
+       c.ReturnVars = copyNodes(c.ReturnVars)
        return &c
 }
 func (n *InlinedCallExpr) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDoList(n.Body, err, do)
-       err = maybeDoList(n.ReturnVars, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if err := doNodes(n.Body, do); err != nil {
+               return err
+       }
+       if err := doNodes(n.ReturnVars, do); err != nil {
+               return err
+       }
+       return nil
 }
 func (n *InlinedCallExpr) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       editList(n.Body, edit)
-       editList(n.ReturnVars, edit)
+       editNodes(n.init, edit)
+       editNodes(n.Body, edit)
+       editNodes(n.ReturnVars, edit)
 }
 
 func (n *InterfaceType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -572,9 +749,10 @@ func (n *InterfaceType) copy() Node {
        return &c
 }
 func (n *InterfaceType) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoFields(n.Methods, err, do)
-       return err
+       if err := doFields(n.Methods, do); err != nil {
+               return err
+       }
+       return nil
 }
 func (n *InterfaceType) editChildren(edit func(Node) Node) {
        editFields(n.Methods, edit)
@@ -583,73 +761,113 @@ func (n *InterfaceType) editChildren(edit func(Node) Node) {
 func (n *KeyExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *KeyExpr) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *KeyExpr) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDo(n.Key, err, do)
-       err = maybeDo(n.Value, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if n.Key != nil {
+               if err := do(n.Key); err != nil {
+                       return err
+               }
+       }
+       if n.Value != nil {
+               if err := do(n.Value); err != nil {
+                       return err
+               }
+       }
+       return nil
 }
 func (n *KeyExpr) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       n.Key = maybeEdit(n.Key, edit)
-       n.Value = maybeEdit(n.Value, edit)
+       editNodes(n.init, edit)
+       if n.Key != nil {
+               n.Key = edit(n.Key).(Node)
+       }
+       if n.Value != nil {
+               n.Value = edit(n.Value).(Node)
+       }
 }
 
 func (n *LabelStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *LabelStmt) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *LabelStmt) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       return nil
 }
 func (n *LabelStmt) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
+       editNodes(n.init, edit)
 }
 
 func (n *LogicalExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *LogicalExpr) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *LogicalExpr) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDo(n.X, err, do)
-       err = maybeDo(n.Y, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if n.X != nil {
+               if err := do(n.X); err != nil {
+                       return err
+               }
+       }
+       if n.Y != nil {
+               if err := do(n.Y); err != nil {
+                       return err
+               }
+       }
+       return nil
 }
 func (n *LogicalExpr) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       n.X = maybeEdit(n.X, edit)
-       n.Y = maybeEdit(n.Y, edit)
+       editNodes(n.init, edit)
+       if n.X != nil {
+               n.X = edit(n.X).(Node)
+       }
+       if n.Y != nil {
+               n.Y = edit(n.Y).(Node)
+       }
 }
 
 func (n *MakeExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *MakeExpr) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *MakeExpr) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDo(n.Len, err, do)
-       err = maybeDo(n.Cap, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if n.Len != nil {
+               if err := do(n.Len); err != nil {
+                       return err
+               }
+       }
+       if n.Cap != nil {
+               if err := do(n.Cap); err != nil {
+                       return err
+               }
+       }
+       return nil
 }
 func (n *MakeExpr) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       n.Len = maybeEdit(n.Len, edit)
-       n.Cap = maybeEdit(n.Cap, edit)
+       editNodes(n.init, edit)
+       if n.Len != nil {
+               n.Len = edit(n.Len).(Node)
+       }
+       if n.Cap != nil {
+               n.Cap = edit(n.Cap).(Node)
+       }
 }
 
 func (n *MapType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -658,41 +876,48 @@ func (n *MapType) copy() Node {
        return &c
 }
 func (n *MapType) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDo(n.Key, err, do)
-       err = maybeDo(n.Elem, err, do)
-       return err
+       if n.Key != nil {
+               if err := do(n.Key); err != nil {
+                       return err
+               }
+       }
+       if n.Elem != nil {
+               if err := do(n.Elem); err != nil {
+                       return err
+               }
+       }
+       return nil
 }
 func (n *MapType) editChildren(edit func(Node) Node) {
-       n.Key = toNtype(maybeEdit(n.Key, edit))
-       n.Elem = toNtype(maybeEdit(n.Elem, edit))
+       if n.Key != nil {
+               n.Key = edit(n.Key).(Ntype)
+       }
+       if n.Elem != nil {
+               n.Elem = edit(n.Elem).(Ntype)
+       }
 }
 
 func (n *Name) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
-func (n *Name) copy() Node                    { panic("Name.copy") }
-func (n *Name) doChildren(do func(Node) error) error {
-       var err error
-       return err
-}
-func (n *Name) editChildren(edit func(Node) Node) {
-}
 
 func (n *NameOffsetExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *NameOffsetExpr) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *NameOffsetExpr) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
        if n.Name_ != nil {
-               err = maybeDo(n.Name_, err, do)
+               if err := do(n.Name_); err != nil {
+                       return err
+               }
        }
-       return err
+       return nil
 }
 func (n *NameOffsetExpr) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
+       editNodes(n.init, edit)
        if n.Name_ != nil {
                n.Name_ = edit(n.Name_).(*Name)
        }
@@ -701,33 +926,41 @@ func (n *NameOffsetExpr) editChildren(edit func(Node) Node) {
 func (n *NilExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *NilExpr) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *NilExpr) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       return nil
 }
 func (n *NilExpr) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
+       editNodes(n.init, edit)
 }
 
 func (n *ParenExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *ParenExpr) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *ParenExpr) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDo(n.X, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if n.X != nil {
+               if err := do(n.X); err != nil {
+                       return err
+               }
+       }
+       return nil
 }
 func (n *ParenExpr) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       n.X = maybeEdit(n.X, edit)
+       editNodes(n.init, edit)
+       if n.X != nil {
+               n.X = edit(n.X).(Node)
+       }
 }
 
 func (n *PkgName) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -736,8 +969,7 @@ func (n *PkgName) copy() Node {
        return &c
 }
 func (n *PkgName) doChildren(do func(Node) error) error {
-       var err error
-       return err
+       return nil
 }
 func (n *PkgName) editChildren(edit func(Node) Node) {
 }
@@ -745,28 +977,51 @@ func (n *PkgName) editChildren(edit func(Node) Node) {
 func (n *RangeStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *RangeStmt) copy() Node {
        c := *n
-       c.init = c.init.Copy()
-       c.Body = c.Body.Copy()
+       c.init = copyNodes(c.init)
+       c.Body = copyNodes(c.Body)
        return &c
 }
 func (n *RangeStmt) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDo(n.X, err, do)
-       err = maybeDo(n.Key, err, do)
-       err = maybeDo(n.Value, err, do)
-       err = maybeDoList(n.Body, err, do)
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if n.X != nil {
+               if err := do(n.X); err != nil {
+                       return err
+               }
+       }
+       if n.Key != nil {
+               if err := do(n.Key); err != nil {
+                       return err
+               }
+       }
+       if n.Value != nil {
+               if err := do(n.Value); err != nil {
+                       return err
+               }
+       }
+       if err := doNodes(n.Body, do); err != nil {
+               return err
+       }
        if n.Prealloc != nil {
-               err = maybeDo(n.Prealloc, err, do)
+               if err := do(n.Prealloc); err != nil {
+                       return err
+               }
        }
-       return err
+       return nil
 }
 func (n *RangeStmt) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       n.X = maybeEdit(n.X, edit)
-       n.Key = maybeEdit(n.Key, edit)
-       n.Value = maybeEdit(n.Value, edit)
-       editList(n.Body, edit)
+       editNodes(n.init, edit)
+       if n.X != nil {
+               n.X = edit(n.X).(Node)
+       }
+       if n.Key != nil {
+               n.Key = edit(n.Key).(Node)
+       }
+       if n.Value != nil {
+               n.Value = edit(n.Value).(Node)
+       }
+       editNodes(n.Body, edit)
        if n.Prealloc != nil {
                n.Prealloc = edit(n.Prealloc).(*Name)
        }
@@ -775,75 +1030,93 @@ func (n *RangeStmt) editChildren(edit func(Node) Node) {
 func (n *ResultExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *ResultExpr) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *ResultExpr) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       return nil
 }
 func (n *ResultExpr) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
+       editNodes(n.init, edit)
 }
 
 func (n *ReturnStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *ReturnStmt) copy() Node {
        c := *n
-       c.init = c.init.Copy()
-       c.Results = c.Results.Copy()
+       c.init = copyNodes(c.init)
+       c.Results = copyNodes(c.Results)
        return &c
 }
 func (n *ReturnStmt) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDoList(n.Results, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if err := doNodes(n.Results, do); err != nil {
+               return err
+       }
+       return nil
 }
 func (n *ReturnStmt) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       editList(n.Results, edit)
+       editNodes(n.init, edit)
+       editNodes(n.Results, edit)
 }
 
 func (n *SelectStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *SelectStmt) copy() Node {
        c := *n
-       c.init = c.init.Copy()
-       c.Cases = copyComms(c.Cases)
-       c.Compiled = c.Compiled.Copy()
+       c.init = copyNodes(c.init)
+       c.Cases = copyCommClauses(c.Cases)
+       c.Compiled = copyNodes(c.Compiled)
        return &c
 }
 func (n *SelectStmt) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDoComms(n.Cases, err, do)
-       err = maybeDoList(n.Compiled, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if err := doCommClauses(n.Cases, do); err != nil {
+               return err
+       }
+       if err := doNodes(n.Compiled, do); err != nil {
+               return err
+       }
+       return nil
 }
 func (n *SelectStmt) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       editComms(n.Cases, edit)
-       editList(n.Compiled, edit)
+       editNodes(n.init, edit)
+       editCommClauses(n.Cases, edit)
+       editNodes(n.Compiled, edit)
 }
 
 func (n *SelectorExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *SelectorExpr) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *SelectorExpr) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDo(n.X, err, do)
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if n.X != nil {
+               if err := do(n.X); err != nil {
+                       return err
+               }
+       }
        if n.Prealloc != nil {
-               err = maybeDo(n.Prealloc, err, do)
+               if err := do(n.Prealloc); err != nil {
+                       return err
+               }
        }
-       return err
+       return nil
 }
 func (n *SelectorExpr) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       n.X = maybeEdit(n.X, edit)
+       editNodes(n.init, edit)
+       if n.X != nil {
+               n.X = edit(n.X).(Node)
+       }
        if n.Prealloc != nil {
                n.Prealloc = edit(n.Prealloc).(*Name)
        }
@@ -852,64 +1125,121 @@ func (n *SelectorExpr) editChildren(edit func(Node) Node) {
 func (n *SendStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *SendStmt) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *SendStmt) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDo(n.Chan, err, do)
-       err = maybeDo(n.Value, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if n.Chan != nil {
+               if err := do(n.Chan); err != nil {
+                       return err
+               }
+       }
+       if n.Value != nil {
+               if err := do(n.Value); err != nil {
+                       return err
+               }
+       }
+       return nil
 }
 func (n *SendStmt) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       n.Chan = maybeEdit(n.Chan, edit)
-       n.Value = maybeEdit(n.Value, edit)
+       editNodes(n.init, edit)
+       if n.Chan != nil {
+               n.Chan = edit(n.Chan).(Node)
+       }
+       if n.Value != nil {
+               n.Value = edit(n.Value).(Node)
+       }
 }
 
 func (n *SliceExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *SliceExpr) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *SliceExpr) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDo(n.X, err, do)
-       err = maybeDo(n.Low, err, do)
-       err = maybeDo(n.High, err, do)
-       err = maybeDo(n.Max, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if n.X != nil {
+               if err := do(n.X); err != nil {
+                       return err
+               }
+       }
+       if n.Low != nil {
+               if err := do(n.Low); err != nil {
+                       return err
+               }
+       }
+       if n.High != nil {
+               if err := do(n.High); err != nil {
+                       return err
+               }
+       }
+       if n.Max != nil {
+               if err := do(n.Max); err != nil {
+                       return err
+               }
+       }
+       return nil
 }
 func (n *SliceExpr) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       n.X = maybeEdit(n.X, edit)
-       n.Low = maybeEdit(n.Low, edit)
-       n.High = maybeEdit(n.High, edit)
-       n.Max = maybeEdit(n.Max, edit)
+       editNodes(n.init, edit)
+       if n.X != nil {
+               n.X = edit(n.X).(Node)
+       }
+       if n.Low != nil {
+               n.Low = edit(n.Low).(Node)
+       }
+       if n.High != nil {
+               n.High = edit(n.High).(Node)
+       }
+       if n.Max != nil {
+               n.Max = edit(n.Max).(Node)
+       }
 }
 
 func (n *SliceHeaderExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *SliceHeaderExpr) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *SliceHeaderExpr) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDo(n.Ptr, err, do)
-       err = maybeDo(n.Len, err, do)
-       err = maybeDo(n.Cap, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if n.Ptr != nil {
+               if err := do(n.Ptr); err != nil {
+                       return err
+               }
+       }
+       if n.Len != nil {
+               if err := do(n.Len); err != nil {
+                       return err
+               }
+       }
+       if n.Cap != nil {
+               if err := do(n.Cap); err != nil {
+                       return err
+               }
+       }
+       return nil
 }
 func (n *SliceHeaderExpr) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       n.Ptr = maybeEdit(n.Ptr, edit)
-       n.Len = maybeEdit(n.Len, edit)
-       n.Cap = maybeEdit(n.Cap, edit)
+       editNodes(n.init, edit)
+       if n.Ptr != nil {
+               n.Ptr = edit(n.Ptr).(Node)
+       }
+       if n.Len != nil {
+               n.Len = edit(n.Len).(Node)
+       }
+       if n.Cap != nil {
+               n.Cap = edit(n.Cap).(Node)
+       }
 }
 
 func (n *SliceType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -918,46 +1248,65 @@ func (n *SliceType) copy() Node {
        return &c
 }
 func (n *SliceType) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDo(n.Elem, err, do)
-       return err
+       if n.Elem != nil {
+               if err := do(n.Elem); err != nil {
+                       return err
+               }
+       }
+       return nil
 }
 func (n *SliceType) editChildren(edit func(Node) Node) {
-       n.Elem = toNtype(maybeEdit(n.Elem, edit))
+       if n.Elem != nil {
+               n.Elem = edit(n.Elem).(Ntype)
+       }
 }
 
 func (n *StarExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *StarExpr) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *StarExpr) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDo(n.X, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if n.X != nil {
+               if err := do(n.X); err != nil {
+                       return err
+               }
+       }
+       return nil
 }
 func (n *StarExpr) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       n.X = maybeEdit(n.X, edit)
+       editNodes(n.init, edit)
+       if n.X != nil {
+               n.X = edit(n.X).(Node)
+       }
 }
 
 func (n *StructKeyExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *StructKeyExpr) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *StructKeyExpr) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDo(n.Value, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if n.Value != nil {
+               if err := do(n.Value); err != nil {
+                       return err
+               }
+       }
+       return nil
 }
 func (n *StructKeyExpr) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       n.Value = maybeEdit(n.Value, edit)
+       editNodes(n.init, edit)
+       if n.Value != nil {
+               n.Value = edit(n.Value).(Node)
+       }
 }
 
 func (n *StructType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -967,9 +1316,10 @@ func (n *StructType) copy() Node {
        return &c
 }
 func (n *StructType) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoFields(n.Fields, err, do)
-       return err
+       if err := doFields(n.Fields, do); err != nil {
+               return err
+       }
+       return nil
 }
 func (n *StructType) editChildren(edit func(Node) Node) {
        editFields(n.Fields, edit)
@@ -978,43 +1328,67 @@ func (n *StructType) editChildren(edit func(Node) Node) {
 func (n *SwitchStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *SwitchStmt) copy() Node {
        c := *n
-       c.init = c.init.Copy()
-       c.Cases = copyCases(c.Cases)
-       c.Compiled = c.Compiled.Copy()
+       c.init = copyNodes(c.init)
+       c.Cases = copyCaseClauses(c.Cases)
+       c.Compiled = copyNodes(c.Compiled)
        return &c
 }
 func (n *SwitchStmt) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDo(n.Tag, err, do)
-       err = maybeDoCases(n.Cases, err, do)
-       err = maybeDoList(n.Compiled, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if n.Tag != nil {
+               if err := do(n.Tag); err != nil {
+                       return err
+               }
+       }
+       if err := doCaseClauses(n.Cases, do); err != nil {
+               return err
+       }
+       if err := doNodes(n.Compiled, do); err != nil {
+               return err
+       }
+       return nil
 }
 func (n *SwitchStmt) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       n.Tag = maybeEdit(n.Tag, edit)
-       editCases(n.Cases, edit)
-       editList(n.Compiled, edit)
+       editNodes(n.init, edit)
+       if n.Tag != nil {
+               n.Tag = edit(n.Tag).(Node)
+       }
+       editCaseClauses(n.Cases, edit)
+       editNodes(n.Compiled, edit)
 }
 
 func (n *TypeAssertExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *TypeAssertExpr) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *TypeAssertExpr) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDo(n.X, err, do)
-       err = maybeDo(n.Ntype, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if n.X != nil {
+               if err := do(n.X); err != nil {
+                       return err
+               }
+       }
+       if n.Ntype != nil {
+               if err := do(n.Ntype); err != nil {
+                       return err
+               }
+       }
+       return nil
 }
 func (n *TypeAssertExpr) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       n.X = maybeEdit(n.X, edit)
-       n.Ntype = toNtype(maybeEdit(n.Ntype, edit))
+       editNodes(n.init, edit)
+       if n.X != nil {
+               n.X = edit(n.X).(Node)
+       }
+       if n.Ntype != nil {
+               n.Ntype = edit(n.Ntype).(Ntype)
+       }
 }
 
 func (n *TypeSwitchGuard) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -1023,35 +1397,49 @@ func (n *TypeSwitchGuard) copy() Node {
        return &c
 }
 func (n *TypeSwitchGuard) doChildren(do func(Node) error) error {
-       var err error
        if n.Tag != nil {
-               err = maybeDo(n.Tag, err, do)
+               if err := do(n.Tag); err != nil {
+                       return err
+               }
        }
-       err = maybeDo(n.X, err, do)
-       return err
+       if n.X != nil {
+               if err := do(n.X); err != nil {
+                       return err
+               }
+       }
+       return nil
 }
 func (n *TypeSwitchGuard) editChildren(edit func(Node) Node) {
        if n.Tag != nil {
                n.Tag = edit(n.Tag).(*Ident)
        }
-       n.X = maybeEdit(n.X, edit)
+       if n.X != nil {
+               n.X = edit(n.X).(Node)
+       }
 }
 
 func (n *UnaryExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
 func (n *UnaryExpr) copy() Node {
        c := *n
-       c.init = c.init.Copy()
+       c.init = copyNodes(c.init)
        return &c
 }
 func (n *UnaryExpr) doChildren(do func(Node) error) error {
-       var err error
-       err = maybeDoList(n.init, err, do)
-       err = maybeDo(n.X, err, do)
-       return err
+       if err := doNodes(n.init, do); err != nil {
+               return err
+       }
+       if n.X != nil {
+               if err := do(n.X); err != nil {
+                       return err
+               }
+       }
+       return nil
 }
 func (n *UnaryExpr) editChildren(edit func(Node) Node) {
-       editList(n.init, edit)
-       n.X = maybeEdit(n.X, edit)
+       editNodes(n.init, edit)
+       if n.X != nil {
+               n.X = edit(n.X).(Node)
+       }
 }
 
 func (n *typeNode) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
@@ -1060,13 +1448,12 @@ func (n *typeNode) copy() Node {
        return &c
 }
 func (n *typeNode) doChildren(do func(Node) error) error {
-       var err error
-       return err
+       return nil
 }
 func (n *typeNode) editChildren(edit func(Node) Node) {
 }
 
-func copyCases(list []*CaseClause) []*CaseClause {
+func copyCaseClauses(list []*CaseClause) []*CaseClause {
        if list == nil {
                return nil
        }
@@ -1074,10 +1461,7 @@ func copyCases(list []*CaseClause) []*CaseClause {
        copy(c, list)
        return c
 }
-func maybeDoCases(list []*CaseClause, err error, do func(Node) error) error {
-       if err != nil {
-               return err
-       }
+func doCaseClauses(list []*CaseClause, do func(Node) error) error {
        for _, x := range list {
                if x != nil {
                        if err := do(x); err != nil {
@@ -1087,7 +1471,7 @@ func maybeDoCases(list []*CaseClause, err error, do func(Node) error) error {
        }
        return nil
 }
-func editCases(list []*CaseClause, edit func(Node) Node) {
+func editCaseClauses(list []*CaseClause, edit func(Node) Node) {
        for i, x := range list {
                if x != nil {
                        list[i] = edit(x).(*CaseClause)
@@ -1095,7 +1479,7 @@ func editCases(list []*CaseClause, edit func(Node) Node) {
        }
 }
 
-func copyComms(list []*CommClause) []*CommClause {
+func copyCommClauses(list []*CommClause) []*CommClause {
        if list == nil {
                return nil
        }
@@ -1103,10 +1487,7 @@ func copyComms(list []*CommClause) []*CommClause {
        copy(c, list)
        return c
 }
-func maybeDoComms(list []*CommClause, err error, do func(Node) error) error {
-       if err != nil {
-               return err
-       }
+func doCommClauses(list []*CommClause, do func(Node) error) error {
        for _, x := range list {
                if x != nil {
                        if err := do(x); err != nil {
@@ -1116,10 +1497,36 @@ func maybeDoComms(list []*CommClause, err error, do func(Node) error) error {
        }
        return nil
 }
-func editComms(list []*CommClause, edit func(Node) Node) {
+func editCommClauses(list []*CommClause, edit func(Node) Node) {
        for i, x := range list {
                if x != nil {
                        list[i] = edit(x).(*CommClause)
                }
        }
 }
+
+func copyNodes(list []Node) []Node {
+       if list == nil {
+               return nil
+       }
+       c := make([]Node, len(list))
+       copy(c, list)
+       return c
+}
+func doNodes(list []Node, do func(Node) error) error {
+       for _, x := range list {
+               if x != nil {
+                       if err := do(x); err != nil {
+                               return err
+                       }
+               }
+       }
+       return nil
+}
+func editNodes(list []Node, edit func(Node) Node) {
+       for i, x := range list {
+               if x != nil {
+                       list[i] = edit(x).(Node)
+               }
+       }
+}
index 4575dec260b2e389a52e87b0f2b124554b413d71..9c2cba9a082bda0f660e4ca51ac020975729910e 100644 (file)
@@ -322,8 +322,8 @@ func NewRangeStmt(pos src.XPos, key, value, x Node, body []Node) *RangeStmt {
 // A ReturnStmt is a return statement.
 type ReturnStmt struct {
        miniStmt
-       orig    Node  // for typecheckargs rewrite
-       Results Nodes // return list
+       origNode       // for typecheckargs rewrite
+       Results  Nodes // return list
 }
 
 func NewReturnStmt(pos src.XPos, results []Node) *ReturnStmt {
@@ -335,9 +335,6 @@ func NewReturnStmt(pos src.XPos, results []Node) *ReturnStmt {
        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 }.
 type SelectStmt struct {
        miniStmt
index 408f6ed56372ea231951661c87224e0b1a0e691d..7dd394f9ea94c76eeab18749b2635c11ded7c775 100644 (file)
@@ -185,45 +185,32 @@ func (f *Field) String() string {
        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
        return &c
 }
-
-func copyFields(list []*Field) []*Field {
-       out := make([]*Field, len(list))
-       copy(out, list)
-       for i, f := range out {
-               out[i] = f.copy()
+func doField(f *Field, do func(Node) error) error {
+       if f == nil {
+               return nil
        }
-       return out
-}
-
-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)
+       if f.Decl != nil {
+               if err := do(f.Decl); err != nil {
+                       return err
                }
        }
-       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 {
+       if f.Ntype != nil {
+               if err := do(f.Ntype); err != nil {
                        return err
                }
        }
-       return err
+       return nil
 }
-
 func editField(f *Field, edit func(Node) Node) {
        if f == nil {
                return
@@ -232,10 +219,25 @@ func editField(f *Field, edit func(Node) Node) {
                f.Decl = edit(f.Decl).(*Name)
        }
        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) {
        for _, f := range list {
                editField(f, edit)
index 8839e1664d3b581b614fb299ce66b7813811c1f8..4616390b7c14e26e603120f861724607716b629b 100644 (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.
 // If x's children should be processed, do(x) must call DoChildren(x, do) itself.
 func DoList(list Nodes, do func(Node) error) error {
-       for _, x := range list {
-               if x != nil {
-                       if err := do(x); err != nil {
-                               return err
-                       }
-               }
-       }
-       return nil
+       return doNodes(list, do)
 }
 
 // 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)
 }
-
-// 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)
-               }
-       }
-}