mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
Allows removing a bunch of unnecessary fields. Passes toolstash/buildall. Change-Id: Iec2492920e1c3ef352a9bf4296c74a55d9cc9ad6 Reviewed-on: https://go-review.googlesource.com/20677 Reviewed-by: Robert Griesemer <gri@golang.org> Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
847 lines
21 KiB
Go
847 lines
21 KiB
Go
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package gc
|
|
|
|
import (
|
|
"cmd/internal/obj"
|
|
"sort"
|
|
"strconv"
|
|
)
|
|
|
|
const (
|
|
// expression switch
|
|
switchKindExpr = iota // switch a {...} or switch 5 {...}
|
|
switchKindTrue // switch true {...} or switch {...}
|
|
switchKindFalse // switch false {...}
|
|
|
|
// type switch
|
|
switchKindType // switch a.(type) {...}
|
|
)
|
|
|
|
const (
|
|
caseKindDefault = iota // default:
|
|
|
|
// expression switch
|
|
caseKindExprConst // case 5:
|
|
caseKindExprVar // case x:
|
|
|
|
// type switch
|
|
caseKindTypeNil // case nil:
|
|
caseKindTypeConst // case time.Time: (concrete type, has type hash)
|
|
caseKindTypeVar // case io.Reader: (interface type)
|
|
)
|
|
|
|
const binarySearchMin = 4 // minimum number of cases for binary search
|
|
|
|
// An exprSwitch walks an expression switch.
|
|
type exprSwitch struct {
|
|
exprname *Node // node for the expression being switched on
|
|
kind int // kind of switch statement (switchKind*)
|
|
}
|
|
|
|
// A typeSwitch walks a type switch.
|
|
type typeSwitch struct {
|
|
hashname *Node // node for the hash of the type of the variable being switched on
|
|
facename *Node // node for the concrete type of the variable being switched on
|
|
okname *Node // boolean node used for comma-ok type assertions
|
|
}
|
|
|
|
// A caseClause is a single case clause in a switch statement.
|
|
type caseClause struct {
|
|
node *Node // points at case statement
|
|
ordinal int // position in switch
|
|
hash uint32 // hash of a type switch
|
|
typ uint8 // type of case
|
|
}
|
|
|
|
// typecheckswitch typechecks a switch statement.
|
|
func typecheckswitch(n *Node) {
|
|
lno := lineno
|
|
typechecklist(n.Ninit.Slice(), Etop)
|
|
|
|
var nilonly string
|
|
var top int
|
|
var t *Type
|
|
|
|
if n.Left != nil && n.Left.Op == OTYPESW {
|
|
// type switch
|
|
top = Etype
|
|
typecheck(&n.Left.Right, Erv)
|
|
t = n.Left.Right.Type
|
|
if t != nil && t.Etype != TINTER {
|
|
Yyerror("cannot type switch on non-interface value %v", Nconv(n.Left.Right, obj.FmtLong))
|
|
}
|
|
} else {
|
|
// expression switch
|
|
top = Erv
|
|
if n.Left != nil {
|
|
typecheck(&n.Left, Erv)
|
|
defaultlit(&n.Left, nil)
|
|
t = n.Left.Type
|
|
} else {
|
|
t = Types[TBOOL]
|
|
}
|
|
if t != nil {
|
|
var badtype *Type
|
|
switch {
|
|
case !okforeq[t.Etype]:
|
|
Yyerror("cannot switch on %v", Nconv(n.Left, obj.FmtLong))
|
|
case t.Etype == TARRAY && !Isfixedarray(t):
|
|
nilonly = "slice"
|
|
case t.Etype == TARRAY && Isfixedarray(t) && algtype1(t, nil) == ANOEQ:
|
|
Yyerror("cannot switch on %v", Nconv(n.Left, obj.FmtLong))
|
|
case t.Etype == TSTRUCT && algtype1(t, &badtype) == ANOEQ:
|
|
Yyerror("cannot switch on %v (struct containing %v cannot be compared)", Nconv(n.Left, obj.FmtLong), badtype)
|
|
case t.Etype == TFUNC:
|
|
nilonly = "func"
|
|
case t.Etype == TMAP:
|
|
nilonly = "map"
|
|
}
|
|
}
|
|
}
|
|
|
|
n.Type = t
|
|
|
|
var def *Node
|
|
for _, ncase := range n.List.Slice() {
|
|
setlineno(n)
|
|
if ncase.List.Len() == 0 {
|
|
// default
|
|
if def != nil {
|
|
Yyerror("multiple defaults in switch (first at %v)", def.Line())
|
|
} else {
|
|
def = ncase
|
|
}
|
|
} else {
|
|
ls := ncase.List.Slice()
|
|
for i1, n1 := range ls {
|
|
setlineno(n1)
|
|
typecheck(&ls[i1], Erv|Etype)
|
|
n1 = ls[i1]
|
|
if n1.Type == nil || t == nil {
|
|
continue
|
|
}
|
|
setlineno(ncase)
|
|
switch top {
|
|
// expression switch
|
|
case Erv:
|
|
defaultlit(&ls[i1], t)
|
|
n1 = ls[i1]
|
|
switch {
|
|
case n1.Op == OTYPE:
|
|
Yyerror("type %v is not an expression", n1.Type)
|
|
case n1.Type != nil && assignop(n1.Type, t, nil) == 0 && assignop(t, n1.Type, nil) == 0:
|
|
if n.Left != nil {
|
|
Yyerror("invalid case %v in switch on %v (mismatched types %v and %v)", n1, n.Left, n1.Type, t)
|
|
} else {
|
|
Yyerror("invalid case %v in switch (mismatched types %v and bool)", n1, n1.Type)
|
|
}
|
|
case nilonly != "" && !isnil(n1):
|
|
Yyerror("invalid case %v in switch (can only compare %s %v to nil)", n1, nilonly, n.Left)
|
|
case Isinter(t) && !Isinter(n1.Type) && algtype1(n1.Type, nil) == ANOEQ:
|
|
Yyerror("invalid case %v in switch (incomparable type)", Nconv(n1, obj.FmtLong))
|
|
}
|
|
|
|
// type switch
|
|
case Etype:
|
|
var missing, have *Field
|
|
var ptr int
|
|
switch {
|
|
case n1.Op == OLITERAL && Istype(n1.Type, TNIL):
|
|
case n1.Op != OTYPE && n1.Type != nil: // should this be ||?
|
|
Yyerror("%v is not a type", Nconv(n1, obj.FmtLong))
|
|
// reset to original type
|
|
n1 = n.Left.Right
|
|
ls[i1] = n1
|
|
case n1.Type.Etype != TINTER && t.Etype == TINTER && !implements(n1.Type, t, &missing, &have, &ptr):
|
|
if have != nil && !missing.Broke && !have.Broke {
|
|
Yyerror("impossible type switch case: %v cannot have dynamic type %v"+" (wrong type for %v method)\n\thave %v%v\n\twant %v%v", Nconv(n.Left.Right, obj.FmtLong), n1.Type, missing.Sym, have.Sym, Tconv(have.Type, obj.FmtShort), missing.Sym, Tconv(missing.Type, obj.FmtShort))
|
|
} else if !missing.Broke {
|
|
Yyerror("impossible type switch case: %v cannot have dynamic type %v"+" (missing %v method)", Nconv(n.Left.Right, obj.FmtLong), n1.Type, missing.Sym)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if top == Etype && n.Type != nil {
|
|
ll := ncase.List
|
|
if ncase.Rlist.Len() != 0 {
|
|
nvar := ncase.Rlist.First()
|
|
if ll.Len() == 1 && ll.First().Type != nil && !Istype(ll.First().Type, TNIL) {
|
|
// single entry type switch
|
|
nvar.Name.Param.Ntype = typenod(ll.First().Type)
|
|
} else {
|
|
// multiple entry type switch or default
|
|
nvar.Name.Param.Ntype = typenod(n.Type)
|
|
}
|
|
|
|
typecheck(&nvar, Erv|Easgn)
|
|
ncase.Rlist.SetIndex(0, nvar)
|
|
}
|
|
}
|
|
|
|
typechecklist(ncase.Nbody.Slice(), Etop)
|
|
}
|
|
|
|
lineno = lno
|
|
}
|
|
|
|
// walkswitch walks a switch statement.
|
|
func walkswitch(sw *Node) {
|
|
// convert switch {...} to switch true {...}
|
|
if sw.Left == nil {
|
|
sw.Left = Nodbool(true)
|
|
typecheck(&sw.Left, Erv)
|
|
}
|
|
|
|
if sw.Left.Op == OTYPESW {
|
|
var s typeSwitch
|
|
s.walk(sw)
|
|
} else {
|
|
var s exprSwitch
|
|
s.walk(sw)
|
|
}
|
|
}
|
|
|
|
// walk generates an AST implementing sw.
|
|
// sw is an expression switch.
|
|
// The AST is generally of the form of a linear
|
|
// search using if..goto, although binary search
|
|
// is used with long runs of constants.
|
|
func (s *exprSwitch) walk(sw *Node) {
|
|
casebody(sw, nil)
|
|
|
|
cond := sw.Left
|
|
sw.Left = nil
|
|
|
|
s.kind = switchKindExpr
|
|
if Isconst(cond, CTBOOL) {
|
|
s.kind = switchKindTrue
|
|
if !cond.Val().U.(bool) {
|
|
s.kind = switchKindFalse
|
|
}
|
|
}
|
|
|
|
walkexpr(&cond, &sw.Ninit)
|
|
t := sw.Type
|
|
if t == nil {
|
|
return
|
|
}
|
|
|
|
// convert the switch into OIF statements
|
|
var cas []*Node
|
|
if s.kind == switchKindTrue || s.kind == switchKindFalse {
|
|
s.exprname = Nodbool(s.kind == switchKindTrue)
|
|
} else if consttype(cond) >= 0 {
|
|
// leave constants to enable dead code elimination (issue 9608)
|
|
s.exprname = cond
|
|
} else {
|
|
s.exprname = temp(cond.Type)
|
|
cas = []*Node{Nod(OAS, s.exprname, cond)}
|
|
typecheckslice(cas, Etop)
|
|
}
|
|
|
|
// enumerate the cases, and lop off the default case
|
|
cc := caseClauses(sw, s.kind)
|
|
sw.List.Set(nil)
|
|
var def *Node
|
|
if len(cc) > 0 && cc[0].typ == caseKindDefault {
|
|
def = cc[0].node.Right
|
|
cc = cc[1:]
|
|
} else {
|
|
def = Nod(OBREAK, nil, nil)
|
|
}
|
|
|
|
// handle the cases in order
|
|
for len(cc) > 0 {
|
|
// deal with expressions one at a time
|
|
if !okforcmp[t.Etype] || cc[0].typ != caseKindExprConst {
|
|
a := s.walkCases(cc[:1])
|
|
cas = append(cas, a)
|
|
cc = cc[1:]
|
|
continue
|
|
}
|
|
|
|
// do binary search on runs of constants
|
|
var run int
|
|
for run = 1; run < len(cc) && cc[run].typ == caseKindExprConst; run++ {
|
|
}
|
|
|
|
// sort and compile constants
|
|
sort.Sort(caseClauseByExpr(cc[:run]))
|
|
a := s.walkCases(cc[:run])
|
|
cas = append(cas, a)
|
|
cc = cc[run:]
|
|
}
|
|
|
|
// handle default case
|
|
if nerrors == 0 {
|
|
cas = append(cas, def)
|
|
sw.Nbody.Set(append(cas, sw.Nbody.Slice()...))
|
|
walkstmtlist(sw.Nbody.Slice())
|
|
}
|
|
}
|
|
|
|
// walkCases generates an AST implementing the cases in cc.
|
|
func (s *exprSwitch) walkCases(cc []*caseClause) *Node {
|
|
if len(cc) < binarySearchMin {
|
|
// linear search
|
|
var cas []*Node
|
|
for _, c := range cc {
|
|
n := c.node
|
|
lno := setlineno(n)
|
|
|
|
a := Nod(OIF, nil, nil)
|
|
if (s.kind != switchKindTrue && s.kind != switchKindFalse) || assignop(n.Left.Type, s.exprname.Type, nil) == OCONVIFACE || assignop(s.exprname.Type, n.Left.Type, nil) == OCONVIFACE {
|
|
a.Left = Nod(OEQ, s.exprname, n.Left) // if name == val
|
|
typecheck(&a.Left, Erv)
|
|
} else if s.kind == switchKindTrue {
|
|
a.Left = n.Left // if val
|
|
} else {
|
|
// s.kind == switchKindFalse
|
|
a.Left = Nod(ONOT, n.Left, nil) // if !val
|
|
typecheck(&a.Left, Erv)
|
|
}
|
|
a.Nbody.Set1(n.Right) // goto l
|
|
|
|
cas = append(cas, a)
|
|
lineno = lno
|
|
}
|
|
return liststmt(cas)
|
|
}
|
|
|
|
// find the middle and recur
|
|
half := len(cc) / 2
|
|
a := Nod(OIF, nil, nil)
|
|
mid := cc[half-1].node.Left
|
|
le := Nod(OLE, s.exprname, mid)
|
|
if Isconst(mid, CTSTR) {
|
|
// Search by length and then by value; see exprcmp.
|
|
lenlt := Nod(OLT, Nod(OLEN, s.exprname, nil), Nod(OLEN, mid, nil))
|
|
leneq := Nod(OEQ, Nod(OLEN, s.exprname, nil), Nod(OLEN, mid, nil))
|
|
a.Left = Nod(OOROR, lenlt, Nod(OANDAND, leneq, le))
|
|
} else {
|
|
a.Left = le
|
|
}
|
|
typecheck(&a.Left, Erv)
|
|
a.Nbody.Set1(s.walkCases(cc[:half]))
|
|
a.Rlist.Set1(s.walkCases(cc[half:]))
|
|
return a
|
|
}
|
|
|
|
// casebody builds separate lists of statements and cases.
|
|
// It makes labels between cases and statements
|
|
// and deals with fallthrough, break, and unreachable statements.
|
|
func casebody(sw *Node, typeswvar *Node) {
|
|
if sw.List.Len() == 0 {
|
|
return
|
|
}
|
|
|
|
lno := setlineno(sw)
|
|
|
|
var cas []*Node // cases
|
|
var stat []*Node // statements
|
|
var def *Node // defaults
|
|
br := Nod(OBREAK, nil, nil)
|
|
|
|
for i, n := range sw.List.Slice() {
|
|
setlineno(n)
|
|
if n.Op != OXCASE {
|
|
Fatalf("casebody %v", Oconv(n.Op, 0))
|
|
}
|
|
n.Op = OCASE
|
|
needvar := n.List.Len() != 1 || n.List.First().Op == OLITERAL
|
|
|
|
jmp := Nod(OGOTO, newCaseLabel(), nil)
|
|
if n.List.Len() == 0 {
|
|
if def != nil {
|
|
Yyerror("more than one default case")
|
|
}
|
|
// reuse original default case
|
|
n.Right = jmp
|
|
def = n
|
|
}
|
|
|
|
if n.List.Len() == 1 {
|
|
// one case -- reuse OCASE node
|
|
n.Left = n.List.First()
|
|
n.Right = jmp
|
|
n.List.Set(nil)
|
|
cas = append(cas, n)
|
|
} else {
|
|
// expand multi-valued cases
|
|
for _, n1 := range n.List.Slice() {
|
|
cas = append(cas, Nod(OCASE, n1, jmp))
|
|
}
|
|
}
|
|
|
|
stat = append(stat, Nod(OLABEL, jmp.Left, nil))
|
|
if typeswvar != nil && needvar && n.Rlist.Len() != 0 {
|
|
l := []*Node{
|
|
Nod(ODCL, n.Rlist.First(), nil),
|
|
Nod(OAS, n.Rlist.First(), typeswvar),
|
|
}
|
|
typecheckslice(l, Etop)
|
|
stat = append(stat, l...)
|
|
}
|
|
stat = append(stat, n.Nbody.Slice()...)
|
|
|
|
// botch - shouldn't fall thru declaration
|
|
last := stat[len(stat)-1]
|
|
if last.Xoffset == n.Xoffset && last.Op == OXFALL {
|
|
if typeswvar != nil {
|
|
setlineno(last)
|
|
Yyerror("cannot fallthrough in type switch")
|
|
}
|
|
|
|
if i+1 >= sw.List.Len() {
|
|
setlineno(last)
|
|
Yyerror("cannot fallthrough final case in switch")
|
|
}
|
|
|
|
last.Op = OFALL
|
|
} else {
|
|
stat = append(stat, br)
|
|
}
|
|
}
|
|
|
|
stat = append(stat, br)
|
|
if def != nil {
|
|
cas = append(cas, def)
|
|
}
|
|
|
|
sw.List.Set(cas)
|
|
sw.Nbody.Set(stat)
|
|
lineno = lno
|
|
}
|
|
|
|
// nSwitchLabel is the number of switch labels generated.
|
|
// This should be per-function, but it is a global counter for now.
|
|
var nSwitchLabel int
|
|
|
|
func newCaseLabel() *Node {
|
|
label := strconv.Itoa(nSwitchLabel)
|
|
nSwitchLabel++
|
|
return newname(Lookup(label))
|
|
}
|
|
|
|
// caseClauses generates a slice of caseClauses
|
|
// corresponding to the clauses in the switch statement sw.
|
|
// Kind is the kind of switch statement.
|
|
func caseClauses(sw *Node, kind int) []*caseClause {
|
|
var cc []*caseClause
|
|
for _, n := range sw.List.Slice() {
|
|
c := new(caseClause)
|
|
cc = append(cc, c)
|
|
c.ordinal = len(cc)
|
|
c.node = n
|
|
|
|
if n.Left == nil {
|
|
c.typ = caseKindDefault
|
|
continue
|
|
}
|
|
|
|
if kind == switchKindType {
|
|
// type switch
|
|
switch {
|
|
case n.Left.Op == OLITERAL:
|
|
c.typ = caseKindTypeNil
|
|
case Istype(n.Left.Type, TINTER):
|
|
c.typ = caseKindTypeVar
|
|
default:
|
|
c.typ = caseKindTypeConst
|
|
c.hash = typehash(n.Left.Type)
|
|
}
|
|
} else {
|
|
// expression switch
|
|
switch consttype(n.Left) {
|
|
case CTFLT, CTINT, CTRUNE, CTSTR:
|
|
c.typ = caseKindExprConst
|
|
default:
|
|
c.typ = caseKindExprVar
|
|
}
|
|
}
|
|
}
|
|
|
|
if cc == nil {
|
|
return nil
|
|
}
|
|
|
|
// sort by value and diagnose duplicate cases
|
|
if kind == switchKindType {
|
|
// type switch
|
|
sort.Sort(caseClauseByType(cc))
|
|
for i, c1 := range cc {
|
|
if c1.typ == caseKindTypeNil || c1.typ == caseKindDefault {
|
|
break
|
|
}
|
|
for _, c2 := range cc[i+1:] {
|
|
if c2.typ == caseKindTypeNil || c2.typ == caseKindDefault || c1.hash != c2.hash {
|
|
break
|
|
}
|
|
if Eqtype(c1.node.Left.Type, c2.node.Left.Type) {
|
|
yyerrorl(c2.node.Lineno, "duplicate case %v in type switch\n\tprevious case at %v", c2.node.Left.Type, c1.node.Line())
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// expression switch
|
|
sort.Sort(caseClauseByExpr(cc))
|
|
for i, c1 := range cc {
|
|
if i+1 == len(cc) {
|
|
break
|
|
}
|
|
c2 := cc[i+1]
|
|
if exprcmp(c1, c2) != 0 {
|
|
continue
|
|
}
|
|
setlineno(c2.node)
|
|
Yyerror("duplicate case %v in switch\n\tprevious case at %v", c1.node.Left, c1.node.Line())
|
|
}
|
|
}
|
|
|
|
// put list back in processing order
|
|
sort.Sort(caseClauseByOrd(cc))
|
|
return cc
|
|
}
|
|
|
|
// walk generates an AST that implements sw,
|
|
// where sw is a type switch.
|
|
// The AST is generally of the form of a linear
|
|
// search using if..goto, although binary search
|
|
// is used with long runs of concrete types.
|
|
func (s *typeSwitch) walk(sw *Node) {
|
|
cond := sw.Left
|
|
sw.Left = nil
|
|
|
|
if cond == nil {
|
|
sw.List.Set(nil)
|
|
return
|
|
}
|
|
if cond.Right == nil {
|
|
setlineno(sw)
|
|
Yyerror("type switch must have an assignment")
|
|
return
|
|
}
|
|
|
|
walkexpr(&cond.Right, &sw.Ninit)
|
|
if !Istype(cond.Right.Type, TINTER) {
|
|
Yyerror("type switch must be on an interface")
|
|
return
|
|
}
|
|
|
|
var cas []*Node
|
|
|
|
// predeclare temporary variables and the boolean var
|
|
s.facename = temp(cond.Right.Type)
|
|
|
|
a := Nod(OAS, s.facename, cond.Right)
|
|
typecheck(&a, Etop)
|
|
cas = append(cas, a)
|
|
|
|
s.okname = temp(Types[TBOOL])
|
|
typecheck(&s.okname, Erv)
|
|
|
|
s.hashname = temp(Types[TUINT32])
|
|
typecheck(&s.hashname, Erv)
|
|
|
|
// set up labels and jumps
|
|
casebody(sw, s.facename)
|
|
|
|
cc := caseClauses(sw, switchKindType)
|
|
sw.List.Set(nil)
|
|
var def *Node
|
|
if len(cc) > 0 && cc[0].typ == caseKindDefault {
|
|
def = cc[0].node.Right
|
|
cc = cc[1:]
|
|
} else {
|
|
def = Nod(OBREAK, nil, nil)
|
|
}
|
|
var typenil *Node
|
|
if len(cc) > 0 && cc[0].typ == caseKindTypeNil {
|
|
typenil = cc[0].node.Right
|
|
cc = cc[1:]
|
|
}
|
|
|
|
// For empty interfaces, do:
|
|
// if e._type == nil {
|
|
// do nil case if it exists, otherwise default
|
|
// }
|
|
// h := e._type.hash
|
|
// Use a similar strategy for non-empty interfaces.
|
|
|
|
// Get interface descriptor word.
|
|
typ := Nod(OITAB, s.facename, nil)
|
|
|
|
// Check for nil first.
|
|
i := Nod(OIF, nil, nil)
|
|
i.Left = Nod(OEQ, typ, nodnil())
|
|
if typenil != nil {
|
|
// Do explicit nil case right here.
|
|
i.Nbody.Set1(typenil)
|
|
} else {
|
|
// Jump to default case.
|
|
lbl := newCaseLabel()
|
|
i.Nbody.Set1(Nod(OGOTO, lbl, nil))
|
|
// Wrap default case with label.
|
|
blk := Nod(OBLOCK, nil, nil)
|
|
blk.List.Set([]*Node{Nod(OLABEL, lbl, nil), def})
|
|
def = blk
|
|
}
|
|
typecheck(&i.Left, Erv)
|
|
cas = append(cas, i)
|
|
|
|
if !isnilinter(cond.Right.Type) {
|
|
// Load type from itab.
|
|
typ = Nod(ODOTPTR, typ, nil)
|
|
typ.Type = Ptrto(Types[TUINT8])
|
|
typ.Typecheck = 1
|
|
typ.Xoffset = int64(Widthptr) // offset of _type in runtime.itab
|
|
typ.Bounded = true // guaranteed not to fault
|
|
}
|
|
// Load hash from type.
|
|
h := Nod(ODOTPTR, typ, nil)
|
|
h.Type = Types[TUINT32]
|
|
h.Typecheck = 1
|
|
h.Xoffset = int64(2 * Widthptr) // offset of hash in runtime._type
|
|
h.Bounded = true // guaranteed not to fault
|
|
a = Nod(OAS, s.hashname, h)
|
|
typecheck(&a, Etop)
|
|
cas = append(cas, a)
|
|
|
|
// insert type equality check into each case block
|
|
for _, c := range cc {
|
|
n := c.node
|
|
switch c.typ {
|
|
case caseKindTypeVar, caseKindTypeConst:
|
|
n.Right = s.typeone(n)
|
|
default:
|
|
Fatalf("typeSwitch with bad kind: %d", c.typ)
|
|
}
|
|
}
|
|
|
|
// generate list of if statements, binary search for constant sequences
|
|
for len(cc) > 0 {
|
|
if cc[0].typ != caseKindTypeConst {
|
|
n := cc[0].node
|
|
cas = append(cas, n.Right)
|
|
cc = cc[1:]
|
|
continue
|
|
}
|
|
|
|
// identify run of constants
|
|
var run int
|
|
for run = 1; run < len(cc) && cc[run].typ == caseKindTypeConst; run++ {
|
|
}
|
|
|
|
// sort by hash
|
|
sort.Sort(caseClauseByType(cc[:run]))
|
|
|
|
// for debugging: linear search
|
|
if false {
|
|
for i := 0; i < run; i++ {
|
|
n := cc[i].node
|
|
cas = append(cas, n.Right)
|
|
}
|
|
continue
|
|
}
|
|
|
|
// combine adjacent cases with the same hash
|
|
ncase := 0
|
|
for i := 0; i < run; i++ {
|
|
ncase++
|
|
hash := []*Node{cc[i].node.Right}
|
|
for j := i + 1; j < run && cc[i].hash == cc[j].hash; j++ {
|
|
hash = append(hash, cc[j].node.Right)
|
|
}
|
|
cc[i].node.Right = liststmt(hash)
|
|
}
|
|
|
|
// binary search among cases to narrow by hash
|
|
cas = append(cas, s.walkCases(cc[:ncase]))
|
|
cc = cc[ncase:]
|
|
}
|
|
|
|
// handle default case
|
|
if nerrors == 0 {
|
|
cas = append(cas, def)
|
|
sw.Nbody.Set(append(cas, sw.Nbody.Slice()...))
|
|
sw.List.Set(nil)
|
|
walkstmtlist(sw.Nbody.Slice())
|
|
}
|
|
}
|
|
|
|
// typeone generates an AST that jumps to the
|
|
// case body if the variable is of type t.
|
|
func (s *typeSwitch) typeone(t *Node) *Node {
|
|
var name *Node
|
|
var init []*Node
|
|
if t.Rlist.Len() == 0 {
|
|
name = nblank
|
|
typecheck(&nblank, Erv|Easgn)
|
|
} else {
|
|
name = t.Rlist.First()
|
|
init = []*Node{Nod(ODCL, name, nil)}
|
|
a := Nod(OAS, name, nil)
|
|
typecheck(&a, Etop)
|
|
init = append(init, a)
|
|
}
|
|
|
|
a := Nod(OAS2, nil, nil)
|
|
a.List.Set([]*Node{name, s.okname}) // name, ok =
|
|
b := Nod(ODOTTYPE, s.facename, nil)
|
|
b.Type = t.Left.Type // interface.(type)
|
|
a.Rlist.Set1(b)
|
|
typecheck(&a, Etop)
|
|
init = append(init, a)
|
|
|
|
c := Nod(OIF, nil, nil)
|
|
c.Left = s.okname
|
|
c.Nbody.Set1(t.Right) // if ok { goto l }
|
|
|
|
return liststmt(append(init, c))
|
|
}
|
|
|
|
// walkCases generates an AST implementing the cases in cc.
|
|
func (s *typeSwitch) walkCases(cc []*caseClause) *Node {
|
|
if len(cc) < binarySearchMin {
|
|
var cas []*Node
|
|
for _, c := range cc {
|
|
n := c.node
|
|
if c.typ != caseKindTypeConst {
|
|
Fatalf("typeSwitch walkCases")
|
|
}
|
|
a := Nod(OIF, nil, nil)
|
|
a.Left = Nod(OEQ, s.hashname, Nodintconst(int64(c.hash)))
|
|
typecheck(&a.Left, Erv)
|
|
a.Nbody.Set1(n.Right)
|
|
cas = append(cas, a)
|
|
}
|
|
return liststmt(cas)
|
|
}
|
|
|
|
// find the middle and recur
|
|
half := len(cc) / 2
|
|
a := Nod(OIF, nil, nil)
|
|
a.Left = Nod(OLE, s.hashname, Nodintconst(int64(cc[half-1].hash)))
|
|
typecheck(&a.Left, Erv)
|
|
a.Nbody.Set1(s.walkCases(cc[:half]))
|
|
a.Rlist.Set1(s.walkCases(cc[half:]))
|
|
return a
|
|
}
|
|
|
|
type caseClauseByOrd []*caseClause
|
|
|
|
func (x caseClauseByOrd) Len() int { return len(x) }
|
|
func (x caseClauseByOrd) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
|
func (x caseClauseByOrd) Less(i, j int) bool {
|
|
c1, c2 := x[i], x[j]
|
|
switch {
|
|
// sort default first
|
|
case c1.typ == caseKindDefault:
|
|
return true
|
|
case c2.typ == caseKindDefault:
|
|
return false
|
|
|
|
// sort nil second
|
|
case c1.typ == caseKindTypeNil:
|
|
return true
|
|
case c2.typ == caseKindTypeNil:
|
|
return false
|
|
}
|
|
|
|
// sort by ordinal
|
|
return c1.ordinal < c2.ordinal
|
|
}
|
|
|
|
type caseClauseByExpr []*caseClause
|
|
|
|
func (x caseClauseByExpr) Len() int { return len(x) }
|
|
func (x caseClauseByExpr) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
|
func (x caseClauseByExpr) Less(i, j int) bool {
|
|
return exprcmp(x[i], x[j]) < 0
|
|
}
|
|
|
|
func exprcmp(c1, c2 *caseClause) int {
|
|
// sort non-constants last
|
|
if c1.typ != caseKindExprConst {
|
|
return +1
|
|
}
|
|
if c2.typ != caseKindExprConst {
|
|
return -1
|
|
}
|
|
|
|
n1 := c1.node.Left
|
|
n2 := c2.node.Left
|
|
|
|
// sort by type (for switches on interface)
|
|
ct := n1.Val().Ctype()
|
|
if ct > n2.Val().Ctype() {
|
|
return +1
|
|
}
|
|
if ct < n2.Val().Ctype() {
|
|
return -1
|
|
}
|
|
if !Eqtype(n1.Type, n2.Type) {
|
|
if n1.Type.Vargen > n2.Type.Vargen {
|
|
return +1
|
|
} else {
|
|
return -1
|
|
}
|
|
}
|
|
|
|
// sort by constant value to enable binary search
|
|
switch ct {
|
|
case CTFLT:
|
|
return mpcmpfltflt(n1.Val().U.(*Mpflt), n2.Val().U.(*Mpflt))
|
|
case CTINT, CTRUNE:
|
|
return Mpcmpfixfix(n1.Val().U.(*Mpint), n2.Val().U.(*Mpint))
|
|
case CTSTR:
|
|
// Sort strings by length and then by value.
|
|
// It is much cheaper to compare lengths than values,
|
|
// and all we need here is consistency.
|
|
// We respect this sorting in exprSwitch.walkCases.
|
|
a := n1.Val().U.(string)
|
|
b := n2.Val().U.(string)
|
|
if len(a) < len(b) {
|
|
return -1
|
|
}
|
|
if len(a) > len(b) {
|
|
return +1
|
|
}
|
|
if a == b {
|
|
return 0
|
|
}
|
|
if a < b {
|
|
return -1
|
|
}
|
|
return +1
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
type caseClauseByType []*caseClause
|
|
|
|
func (x caseClauseByType) Len() int { return len(x) }
|
|
func (x caseClauseByType) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
|
func (x caseClauseByType) Less(i, j int) bool {
|
|
c1, c2 := x[i], x[j]
|
|
switch {
|
|
// sort non-constants last
|
|
case c1.typ != caseKindTypeConst:
|
|
return false
|
|
case c2.typ != caseKindTypeConst:
|
|
return true
|
|
|
|
// sort by hash code
|
|
case c1.hash != c2.hash:
|
|
return c1.hash < c2.hash
|
|
}
|
|
|
|
// sort by ordinal
|
|
return c1.ordinal < c2.ordinal
|
|
}
|