1
0
mirror of https://github.com/golang/go synced 2024-11-19 13:44:52 -07:00

cmd/compile: change order.go to use nodeSeq

Passes toolstash -cmp

Update #14473.

Change-Id: I15b35d40a5ec1f4355ee38bc6d131920933ac95c
Reviewed-on: https://go-review.googlesource.com/20237
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
Ian Lance Taylor 2016-03-04 14:29:24 -08:00
parent 16e2e95dd9
commit 80e5b52566
3 changed files with 105 additions and 121 deletions

View File

@ -249,16 +249,9 @@ func cleantemp(top ordermarker, order *Order) {
} }
// Orderstmtlist orders each of the statements in the list. // Orderstmtlist orders each of the statements in the list.
func orderstmtlist(l *NodeList, order *Order) { func orderstmtlist(l nodesOrNodeList, order *Order) {
for ; l != nil; l = l.Next { for it := nodeSeqIterate(l); !it.Done(); it.Next() {
orderstmt(l.N, order) orderstmt(it.N(), order)
}
}
// Orderstmtslice orders each of the statements in the slice.
func orderstmtslice(l []*Node, order *Order) {
for _, n := range l {
orderstmt(n, order)
} }
} }
@ -281,7 +274,7 @@ func orderblock(l **NodeList) {
func orderblockNodes(n *Nodes) { func orderblockNodes(n *Nodes) {
var order Order var order Order
mark := marktemp(&order) mark := marktemp(&order)
orderstmtslice(n.Slice(), &order) orderstmtlist(n.Slice(), &order)
cleantemp(mark, &order) cleantemp(mark, &order)
n.Set(order.out) n.Set(order.out)
} }
@ -309,13 +302,13 @@ func orderstmtinplace(np **Node) {
mark := marktemp(&order) mark := marktemp(&order)
orderstmt(n, &order) orderstmt(n, &order)
cleantemp(mark, &order) cleantemp(mark, &order)
*np = liststmtslice(order.out) *np = liststmt(order.out)
} }
// Orderinit moves n's init list to order->out. // Orderinit moves n's init list to order->out.
func orderinit(n *Node, order *Order) { func orderinit(n *Node, order *Order) {
orderstmtlist(n.Ninit, order) orderstmtlist(n.Ninit, order)
n.Ninit = nil setNodeSeq(&n.Ninit, nil)
} }
// Ismulticall reports whether the list l is f() for a multi-value function. // Ismulticall reports whether the list l is f() for a multi-value function.
@ -358,8 +351,8 @@ func copyret(n *Node, order *Order) *NodeList {
} }
as := Nod(OAS2, nil, nil) as := Nod(OAS2, nil, nil)
as.List = l1 setNodeSeq(&as.List, l1)
as.Rlist = list1(n) setNodeSeq(&as.Rlist, list1(n))
typecheck(&as, Etop) typecheck(&as, Etop)
orderstmt(as, order) orderstmt(as, order)
@ -384,14 +377,15 @@ func ordercall(n *Node, order *Order) {
ordercallargs(&n.List, order) ordercallargs(&n.List, order)
if n.Op == OCALLFUNC { if n.Op == OCALLFUNC {
for l, t := n.List, getinargx(n.Left.Type).Type; l != nil && t != nil; l, t = l.Next, t.Down { t := getinargx(n.Left.Type).Type
for it := nodeSeqIterate(n.List); !it.Done() && t != nil; it.Next() {
// Check for "unsafe-uintptr" tag provided by escape analysis. // Check for "unsafe-uintptr" tag provided by escape analysis.
// If present and the argument is really a pointer being converted // If present and the argument is really a pointer being converted
// to uintptr, arrange for the pointer to be kept alive until the call // to uintptr, arrange for the pointer to be kept alive until the call
// returns, by copying it into a temp and marking that temp // returns, by copying it into a temp and marking that temp
// still alive when we pop the temp stack. // still alive when we pop the temp stack.
if t.Note != nil && *t.Note == unsafeUintptrTag { if t.Note != nil && *t.Note == unsafeUintptrTag {
xp := &l.N xp := it.P()
for (*xp).Op == OCONVNOP && !Isptr[(*xp).Type.Etype] { for (*xp).Op == OCONVNOP && !Isptr[(*xp).Type.Etype] {
xp = &(*xp).Left xp = &(*xp).Left
} }
@ -402,6 +396,7 @@ func ordercall(n *Node, order *Order) {
*xp = x *xp = x
} }
} }
t = t.Down
} }
} }
} }
@ -450,23 +445,23 @@ func ordermapassign(n *Node, order *Order) {
var post []*Node var post []*Node
var m *Node var m *Node
var a *Node var a *Node
for l := n.List; l != nil; l = l.Next { for it := nodeSeqIterate(n.List); !it.Done(); it.Next() {
if l.N.Op == OINDEXMAP { if it.N().Op == OINDEXMAP {
m = l.N m = it.N()
if !istemp(m.Left) { if !istemp(m.Left) {
m.Left = ordercopyexpr(m.Left, m.Left.Type, order, 0) m.Left = ordercopyexpr(m.Left, m.Left.Type, order, 0)
} }
if !istemp(m.Right) { if !istemp(m.Right) {
m.Right = ordercopyexpr(m.Right, m.Right.Type, order, 0) m.Right = ordercopyexpr(m.Right, m.Right.Type, order, 0)
} }
l.N = ordertemp(m.Type, order, false) *it.P() = ordertemp(m.Type, order, false)
a = Nod(OAS, m, l.N) a = Nod(OAS, m, it.N())
typecheck(&a, Etop) typecheck(&a, Etop)
post = append(post, a) post = append(post, a)
} else if instrumenting && n.Op == OAS2FUNC && !isblank(l.N) { } else if instrumenting && n.Op == OAS2FUNC && !isblank(it.N()) {
m = l.N m = it.N()
l.N = ordertemp(m.Type, order, false) *it.P() = ordertemp(m.Type, order, false)
a = Nod(OAS, m, l.N) a = Nod(OAS, m, it.N())
typecheck(&a, Etop) typecheck(&a, Etop)
post = append(post, a) post = append(post, a)
} }
@ -553,7 +548,7 @@ func orderstmt(n *Node, order *Order) {
t := marktemp(order) t := marktemp(order)
orderexprlist(n.List, order) orderexprlist(n.List, order)
r := n.Rlist.N r := nodeSeqFirst(n.Rlist)
orderexpr(&r.Left, order, nil) orderexpr(&r.Left, order, nil)
orderexpr(&r.Right, order, nil) orderexpr(&r.Right, order, nil)
@ -570,7 +565,7 @@ func orderstmt(n *Node, order *Order) {
t := marktemp(order) t := marktemp(order)
orderexprlist(n.List, order) orderexprlist(n.List, order)
ordercall(n.Rlist.N, order) ordercall(nodeSeqFirst(n.Rlist), order)
ordermapassign(n, order) ordermapassign(n, order)
cleantemp(t, order) cleantemp(t, order)
@ -581,17 +576,17 @@ func orderstmt(n *Node, order *Order) {
t := marktemp(order) t := marktemp(order)
orderexprlist(n.List, order) orderexprlist(n.List, order)
orderexpr(&n.Rlist.N.Left, order, nil) // i in i.(T) orderexpr(&nodeSeqFirst(n.Rlist).Left, order, nil) // i in i.(T)
if isblank(n.List.N) { if isblank(nodeSeqFirst(n.List)) {
order.out = append(order.out, n) order.out = append(order.out, n)
} else { } else {
typ := n.Rlist.N.Type typ := nodeSeqFirst(n.Rlist).Type
tmp1 := ordertemp(typ, order, haspointers(typ)) tmp1 := ordertemp(typ, order, haspointers(typ))
order.out = append(order.out, n) order.out = append(order.out, n)
r := Nod(OAS, n.List.N, tmp1) r := Nod(OAS, nodeSeqFirst(n.List), tmp1)
typecheck(&r, Etop) typecheck(&r, Etop)
ordermapassign(r, order) ordermapassign(r, order)
n.List = list(list1(tmp1), n.List.Next.N) setNodeSeq(&n.List, []*Node{tmp1, nodeSeqSecond(n.List)})
} }
cleantemp(t, order) cleantemp(t, order)
@ -602,23 +597,23 @@ func orderstmt(n *Node, order *Order) {
t := marktemp(order) t := marktemp(order)
orderexprlist(n.List, order) orderexprlist(n.List, order)
orderexpr(&n.Rlist.N.Left, order, nil) // arg to recv orderexpr(&nodeSeqFirst(n.Rlist).Left, order, nil) // arg to recv
ch := n.Rlist.N.Left.Type ch := nodeSeqFirst(n.Rlist).Left.Type
tmp1 := ordertemp(ch.Type, order, haspointers(ch.Type)) tmp1 := ordertemp(ch.Type, order, haspointers(ch.Type))
var tmp2 *Node var tmp2 *Node
if !isblank(n.List.Next.N) { if !isblank(nodeSeqSecond(n.List)) {
tmp2 = ordertemp(n.List.Next.N.Type, order, false) tmp2 = ordertemp(nodeSeqSecond(n.List).Type, order, false)
} else { } else {
tmp2 = ordertemp(Types[TBOOL], order, false) tmp2 = ordertemp(Types[TBOOL], order, false)
} }
order.out = append(order.out, n) order.out = append(order.out, n)
r := Nod(OAS, n.List.N, tmp1) r := Nod(OAS, nodeSeqFirst(n.List), tmp1)
typecheck(&r, Etop) typecheck(&r, Etop)
ordermapassign(r, order) ordermapassign(r, order)
r = Nod(OAS, n.List.Next.N, tmp2) r = Nod(OAS, nodeSeqSecond(n.List), tmp2)
typecheck(&r, Etop) typecheck(&r, Etop)
ordermapassign(r, order) ordermapassign(r, order)
n.List = list(list1(tmp1), tmp2) setNodeSeq(&n.List, []*Node{tmp1, tmp2})
cleantemp(t, order) cleantemp(t, order)
// Special: does not save n onto out. // Special: does not save n onto out.
@ -658,7 +653,9 @@ func orderstmt(n *Node, order *Order) {
orderexprlist(n.Left.List, order) orderexprlist(n.Left.List, order)
t1 := marktemp(order) t1 := marktemp(order)
np := &n.Left.List.Next.N // map key it := nodeSeqIterate(n.Left.List)
it.Next()
np := it.P() // map key
*np = ordercopyexpr(*np, (*np).Type, order, 0) *np = ordercopyexpr(*np, (*np).Type, order, 0)
poptemp(t1, order) poptemp(t1, order)
@ -671,9 +668,11 @@ func orderstmt(n *Node, order *Order) {
case ODELETE: case ODELETE:
t := marktemp(order) t := marktemp(order)
orderexpr(&n.List.N, order, nil) it := nodeSeqIterate(n.List)
orderexpr(&n.List.Next.N, order, nil) orderexpr(it.P(), order, nil)
orderaddrtemp(&n.List.Next.N, order) // map key it.Next()
orderexpr(it.P(), order, nil)
orderaddrtemp(it.P(), order) // map key
order.out = append(order.out, n) order.out = append(order.out, n)
cleantemp(t, order) cleantemp(t, order)
@ -702,11 +701,7 @@ func orderstmt(n *Node, order *Order) {
n.Nbody.Set(append(l, n.Nbody.Slice()...)) n.Nbody.Set(append(l, n.Nbody.Slice()...))
l = nil l = nil
cleantempnopop(t, order, &l) cleantempnopop(t, order, &l)
var ll *NodeList setNodeSeq(&n.Rlist, append(l, nodeSeqSlice(n.Rlist)...))
for _, n := range l {
ll = list(ll, n)
}
n.Rlist = concat(ll, n.Rlist)
poptemp(t, order) poptemp(t, order)
orderblockNodes(&n.Nbody) orderblockNodes(&n.Nbody)
orderblock(&n.Rlist) orderblock(&n.Rlist)
@ -746,7 +741,7 @@ func orderstmt(n *Node, order *Order) {
if n.Right.Op == OSTRARRAYBYTE { if n.Right.Op == OSTRARRAYBYTE {
n.Right.Op = OSTRARRAYBYTETMP n.Right.Op = OSTRARRAYBYTETMP
} }
if count(n.List) < 2 || isblank(n.List.Next.N) { if nodeSeqLen(n.List) < 2 || isblank(nodeSeqSecond(n.List)) {
// for i := range x will only use x once, to compute len(x). // for i := range x will only use x once, to compute len(x).
// No need to copy it. // No need to copy it.
break break
@ -779,8 +774,8 @@ func orderstmt(n *Node, order *Order) {
prealloc[n] = ordertemp(Types[TUINT8], order, true) prealloc[n] = ordertemp(Types[TUINT8], order, true)
} }
for l := n.List; l != nil; l = l.Next { for it := nodeSeqIterate(n.List); !it.Done(); it.Next() {
orderexprinplace(&l.N, order) orderexprinplace(it.P(), order)
} }
orderblockNodes(&n.Nbody) orderblockNodes(&n.Nbody)
order.out = append(order.out, n) order.out = append(order.out, n)
@ -805,16 +800,16 @@ func orderstmt(n *Node, order *Order) {
var tmp1 *Node var tmp1 *Node
var tmp2 *Node var tmp2 *Node
var r *Node var r *Node
for l := n.List; l != nil; l = l.Next { for it := nodeSeqIterate(n.List); !it.Done(); it.Next() {
if l.N.Op != OXCASE { if it.N().Op != OXCASE {
Fatalf("order select case %v", Oconv(int(l.N.Op), 0)) Fatalf("order select case %v", Oconv(int(it.N().Op), 0))
} }
r = l.N.Left r = it.N().Left
setlineno(l.N) setlineno(it.N())
// Append any new body prologue to ninit. // Append any new body prologue to ninit.
// The next loop will insert ninit into nbody. // The next loop will insert ninit into nbody.
if l.N.Ninit != nil { if nodeSeqLen(it.N().Ninit) != 0 {
Fatalf("order select ninit") Fatalf("order select ninit")
} }
if r != nil { if r != nil {
@ -829,19 +824,19 @@ func orderstmt(n *Node, order *Order) {
// Delete the ODCL nodes here and recreate them inside the body below. // Delete the ODCL nodes here and recreate them inside the body below.
case OSELRECV, OSELRECV2: case OSELRECV, OSELRECV2:
if r.Colas { if r.Colas {
init := r.Ninit itinit := nodeSeqIterate(r.Ninit)
if init != nil && init.N.Op == ODCL && init.N.Left == r.Left { if itinit.Len() != 0 && itinit.N().Op == ODCL && itinit.N().Left == r.Left {
init = init.Next itinit.Next()
} }
if init != nil && init.N.Op == ODCL && r.List != nil && init.N.Left == r.List.N { if itinit.Len() != 0 && itinit.N().Op == ODCL && nodeSeqLen(r.List) != 0 && itinit.N().Left == nodeSeqFirst(r.List) {
init = init.Next itinit.Next()
} }
if init == nil { if itinit.Done() {
r.Ninit = nil setNodeSeq(&r.Ninit, nil)
} }
} }
if r.Ninit != nil { if nodeSeqLen(r.Ninit) != 0 {
Yyerror("ninit on select recv") Yyerror("ninit on select recv")
dumplist("ninit", r.Ninit) dumplist("ninit", r.Ninit)
} }
@ -875,36 +870,36 @@ func orderstmt(n *Node, order *Order) {
if r.Colas { if r.Colas {
tmp2 = Nod(ODCL, tmp1, nil) tmp2 = Nod(ODCL, tmp1, nil)
typecheck(&tmp2, Etop) typecheck(&tmp2, Etop)
l.N.Ninit = list(l.N.Ninit, tmp2) appendNodeSeqNode(&it.N().Ninit, tmp2)
} }
r.Left = ordertemp(r.Right.Left.Type.Type, order, haspointers(r.Right.Left.Type.Type)) r.Left = ordertemp(r.Right.Left.Type.Type, order, haspointers(r.Right.Left.Type.Type))
tmp2 = Nod(OAS, tmp1, r.Left) tmp2 = Nod(OAS, tmp1, r.Left)
typecheck(&tmp2, Etop) typecheck(&tmp2, Etop)
l.N.Ninit = list(l.N.Ninit, tmp2) appendNodeSeqNode(&it.N().Ninit, tmp2)
} }
if r.List != nil && isblank(r.List.N) { if nodeSeqLen(r.List) != 0 && isblank(nodeSeqFirst(r.List)) {
r.List = nil setNodeSeq(&r.List, nil)
} }
if r.List != nil { if nodeSeqLen(r.List) != 0 {
tmp1 = r.List.N tmp1 = nodeSeqFirst(r.List)
if r.Colas { if r.Colas {
tmp2 = Nod(ODCL, tmp1, nil) tmp2 = Nod(ODCL, tmp1, nil)
typecheck(&tmp2, Etop) typecheck(&tmp2, Etop)
l.N.Ninit = list(l.N.Ninit, tmp2) appendNodeSeqNode(&it.N().Ninit, tmp2)
} }
r.List = list1(ordertemp(tmp1.Type, order, false)) setNodeSeq(&r.List, list1(ordertemp(tmp1.Type, order, false)))
tmp2 = Nod(OAS, tmp1, r.List.N) tmp2 = Nod(OAS, tmp1, nodeSeqFirst(r.List))
typecheck(&tmp2, Etop) typecheck(&tmp2, Etop)
l.N.Ninit = list(l.N.Ninit, tmp2) appendNodeSeqNode(&it.N().Ninit, tmp2)
} }
orderblock(&l.N.Ninit) orderblock(&it.N().Ninit)
case OSEND: case OSEND:
if r.Ninit != nil { if nodeSeqLen(r.Ninit) != 0 {
Yyerror("ninit on select send") Yyerror("ninit on select send")
dumplist("ninit", r.Ninit) dumplist("ninit", r.Ninit)
} }
@ -923,20 +918,17 @@ func orderstmt(n *Node, order *Order) {
} }
} }
orderblockNodes(&l.N.Nbody) orderblockNodes(&it.N().Nbody)
} }
// Now that we have accumulated all the temporaries, clean them. // Now that we have accumulated all the temporaries, clean them.
// Also insert any ninit queued during the previous loop. // Also insert any ninit queued during the previous loop.
// (The temporary cleaning must follow that ninit work.) // (The temporary cleaning must follow that ninit work.)
for l := n.List; l != nil; l = l.Next { for it := nodeSeqIterate(n.List); !it.Done(); it.Next() {
s := make([]*Node, 0, count(l.N.Ninit)) s := nodeSeqSlice(it.N().Ninit)
for ll := l.N.Ninit; ll != nil; ll = ll.Next {
s = append(s, ll.N)
}
cleantempnopop(t, order, &s) cleantempnopop(t, order, &s)
l.N.Nbody.Set(append(s, l.N.Nbody.Slice()...)) it.N().Nbody.Set(append(s, it.N().Nbody.Slice()...))
l.N.Ninit = nil setNodeSeq(&it.N().Ninit, nil)
} }
order.out = append(order.out, n) order.out = append(order.out, n)
@ -963,12 +955,12 @@ func orderstmt(n *Node, order *Order) {
t := marktemp(order) t := marktemp(order)
orderexpr(&n.Left, order, nil) orderexpr(&n.Left, order, nil)
for l := n.List; l != nil; l = l.Next { for it := nodeSeqIterate(n.List); !it.Done(); it.Next() {
if l.N.Op != OXCASE { if it.N().Op != OXCASE {
Fatalf("order switch case %v", Oconv(int(l.N.Op), 0)) Fatalf("order switch case %v", Oconv(int(it.N().Op), 0))
} }
orderexprlistinplace(l.N.List, order) orderexprlistinplace(it.N().List, order)
orderblockNodes(&l.N.Nbody) orderblockNodes(&it.N().Nbody)
} }
order.out = append(order.out, n) order.out = append(order.out, n)
@ -1023,9 +1015,9 @@ func orderexpr(np **Node, order *Order, lhs *Node) {
case OADDSTR: case OADDSTR:
orderexprlist(n.List, order) orderexprlist(n.List, order)
if count(n.List) > 5 { if nodeSeqLen(n.List) > 5 {
t := typ(TARRAY) t := typ(TARRAY)
t.Bound = int64(count(n.List)) t.Bound = int64(nodeSeqLen(n.List))
t.Type = Types[TSTRING] t.Type = Types[TSTRING]
prealloc[n] = ordertemp(t, order, false) prealloc[n] = ordertemp(t, order, false)
} }
@ -1040,15 +1032,15 @@ func orderexpr(np **Node, order *Order, lhs *Node) {
hasbyte := false hasbyte := false
haslit := false haslit := false
for l := n.List; l != nil; l = l.Next { for it := nodeSeqIterate(n.List); !it.Done(); it.Next() {
hasbyte = hasbyte || l.N.Op == OARRAYBYTESTR hasbyte = hasbyte || it.N().Op == OARRAYBYTESTR
haslit = haslit || l.N.Op == OLITERAL && len(l.N.Val().U.(string)) != 0 haslit = haslit || it.N().Op == OLITERAL && len(it.N().Val().U.(string)) != 0
} }
if haslit && hasbyte { if haslit && hasbyte {
for l := n.List; l != nil; l = l.Next { for it := nodeSeqIterate(n.List); !it.Done(); it.Next() {
if l.N.Op == OARRAYBYTESTR { if it.N().Op == OARRAYBYTESTR {
l.N.Op = OARRAYBYTESTRTMP it.N().Op = OARRAYBYTESTRTMP
} }
} }
} }
@ -1113,11 +1105,7 @@ func orderexpr(np **Node, order *Order, lhs *Node) {
var s []*Node var s []*Node
cleantempnopop(mark, order, &s) cleantempnopop(mark, order, &s)
var l *NodeList setNodeSeq(&n.Right.Ninit, append(s, nodeSeqSlice(n.Right.Ninit)...))
for _, n := range s {
l = list(l, n)
}
n.Right.Ninit = concat(l, n.Right.Ninit)
orderexprinplace(&n.Right, order) orderexprinplace(&n.Right, order)
case OCALLFUNC, case OCALLFUNC,
@ -1144,7 +1132,7 @@ func orderexpr(np **Node, order *Order, lhs *Node) {
case OAPPEND: case OAPPEND:
ordercallargs(&n.List, order) ordercallargs(&n.List, order)
if lhs == nil || lhs.Op != ONAME && !samesafeexpr(lhs, n.List.N) { if lhs == nil || lhs.Op != ONAME && !samesafeexpr(lhs, nodeSeqFirst(n.List)) {
n = ordercopyexpr(n, n.Type, order, 0) n = ordercopyexpr(n, n.Type, order, 0)
} }

View File

@ -2366,23 +2366,15 @@ func listtreecopy(l *NodeList, lineno int32) *NodeList {
return out return out
} }
func liststmt(l *NodeList) *Node { func liststmt(l nodesOrNodeList) *Node {
n := Nod(OBLOCK, nil, nil) n := Nod(OBLOCK, nil, nil)
n.List = l setNodeSeq(&n.List, l)
if l != nil { if nodeSeqLen(l) != 0 {
n.Lineno = l.N.Lineno n.Lineno = nodeSeqFirst(l).Lineno
} }
return n return n
} }
func liststmtslice(l []*Node) *Node {
var ll *NodeList
for _, n := range l {
ll = list(ll, n)
}
return liststmt(ll)
}
// return nelem of list // return nelem of list
func structcount(t *Type) int { func structcount(t *Type) int {
var s Iter var s Iter

View File

@ -617,27 +617,31 @@ func nodeSeqLen(ns nodesOrNodeList) int {
} }
} }
// nodeSeqFirst returns the first element of either a *NodeList or a Nodes. // nodeSeqFirst returns the first element of a *NodeList, a Nodes,
// It panics if the sequence is empty. // or a []*Node. It panics if the sequence is empty.
func nodeSeqFirst(ns nodesOrNodeList) *Node { func nodeSeqFirst(ns nodesOrNodeList) *Node {
switch ns := ns.(type) { switch ns := ns.(type) {
case *NodeList: case *NodeList:
return ns.N return ns.N
case Nodes: case Nodes:
return ns.Slice()[0] return ns.Slice()[0]
case []*Node:
return ns[0]
default: default:
panic("can't happen") panic("can't happen")
} }
} }
// nodeSeqSecond returns the second element of either a *NodeList or a Nodes. // nodeSeqSecond returns the second element of a *NodeList, a Nodes,
// It panics if the sequence has fewer than two elements. // or a []*Node. It panics if the sequence has fewer than two elements.
func nodeSeqSecond(ns nodesOrNodeList) *Node { func nodeSeqSecond(ns nodesOrNodeList) *Node {
switch ns := ns.(type) { switch ns := ns.(type) {
case *NodeList: case *NodeList:
return ns.Next.N return ns.Next.N
case Nodes: case Nodes:
return ns.Slice()[1] return ns.Slice()[1]
case []*Node:
return ns[1]
default: default:
panic("can't happen") panic("can't happen")
} }