// 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"
"fmt"
"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 := int(lineno)
typechecklist(n.Ninit, 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
var ll *NodeList
for l := n.List; l != nil; l = l.Next {
ncase := l.N
setlineno(n)
if ncase.List == nil {
// default
if def != nil {
Yyerror("multiple defaults in switch (first at %v)", def.Line())
} else {
def = ncase
}
} else {
for ll = ncase.List; ll != nil; ll = ll.Next {
setlineno(ll.N)
typecheck(&ll.N, Erv|Etype)
if ll.N.Type == nil || t == nil {
continue
}
setlineno(ncase)
switch top {
// expression switch
case Erv:
defaultlit(&ll.N, t)
switch {
case ll.N.Op == OTYPE:
Yyerror("type %v is not an expression", ll.N.Type)
case ll.N.Type != nil && assignop(ll.N.Type, t, nil) == 0 && assignop(t, ll.N.Type, nil) == 0:
if n.Left != nil {
Yyerror("invalid case %v in switch on %v (mismatched types %v and %v)", ll.N, n.Left, ll.N.Type, t)
} else {
Yyerror("invalid case %v in switch (mismatched types %v and bool)", ll.N, ll.N.Type)
}
case nilonly != "" && !Isconst(ll.N, CTNIL):
Yyerror("invalid case %v in switch (can only compare %s %v to nil)", ll.N, nilonly, n.Left)
}
// type switch
case Etype:
var missing, have *Type
var ptr int
switch {
case ll.N.Op == OLITERAL && Istype(ll.N.Type, TNIL):
case ll.N.Op != OTYPE && ll.N.Type != nil: // should this be ||?
Yyerror("%v is not a type", Nconv(ll.N, obj.FmtLong))
// reset to original type
ll.N = n.Left.Right
case ll.N.Type.Etype != TINTER && t.Etype == TINTER && !implements(ll.N.Type, t, &missing, &have, &ptr):
if have != nil && missing.Broke == 0 && have.Broke == 0 {
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), ll.N.Type, missing.Sym, have.Sym, Tconv(have.Type, obj.FmtShort), missing.Sym, Tconv(missing.Type, obj.FmtShort))
} else if missing.Broke == 0 {
Yyerror("impossible type switch case: %v cannot have dynamic type %v"+" (missing %v method)", Nconv(n.Left.Right, obj.FmtLong), ll.N.Type, missing.Sym)
}
}
}
}
}
if top == Etype && n.Type != nil {
ll = ncase.List
if ncase.Rlist != nil {
nvar := ncase.Rlist.N
if ll != nil && ll.Next == nil && ll.N.Type != nil && !Istype(ll.N.Type, TNIL) {
// single entry type switch
nvar.Name.Param.Ntype = typenod(ll.N.Type)
} else {
// multiple entry type switch or default
nvar.Name.Param.Ntype = typenod(n.Type)
}
typecheck(&nvar, Erv|Easgn)
ncase.Rlist.N = nvar
}
}
typechecklist(ncase.Nbody, Etop)
}
lineno = int32(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 *NodeList
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 = list1(Nod(OAS, s.exprname, cond))
typechecklist(cas, Etop)
}
// enumerate the cases, and lop off the default case
cc := caseClauses(sw, s.kind)
sw.List = 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 = list(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 = list(cas, a)
cc = cc[run:]
}
// handle default case
if nerrors == 0 {
cas = list(cas, def)
sw.Nbody = concat(cas, sw.Nbody)
walkstmtlist(sw.Nbody)
}
}
// walkCases generates an AST implementing the cases in cc.
func (s *exprSwitch) walkCases(cc []*caseClause) *Node {
if len(cc) < binarySearchMin {
// linear search
var cas *NodeList
for _, c := range cc {
n := c.node
lno := int(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 = list1(n.Right) // goto l
cas = list(cas, a)
lineno = int32(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 = list1(s.walkCases(cc[:half]))
a.Rlist = list1(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 == nil {
return
}
lno := setlineno(sw)
var cas *NodeList // cases
var stat *NodeList // statements
var def *Node // defaults
br := Nod(OBREAK, nil, nil)
for l := sw.List; l != nil; l = l.Next {
n := l.N
setlineno(n)
if n.Op != OXCASE {
Fatal("casebody %v", Oconv(int(n.Op), 0))
}
n.Op = OCASE
needvar := count(n.List) != 1 || n.List.N.Op == OLITERAL
jmp := Nod(OGOTO, newCaseLabel(), nil)
if n.List == nil {
if def != nil {
Yyerror("more than one default case")
}
// reuse original default case
n.Right = jmp
def = n
}
if n.List != nil && n.List.Next == nil {
// one case -- reuse OCASE node
n.Left = n.List.N
n.Right = jmp
n.List = nil
cas = list(cas, n)
} else {
// expand multi-valued cases
for lc := n.List; lc != nil; lc = lc.Next {
cas = list(cas, Nod(OCASE, lc.N, jmp))
}
}
stat = list(stat, Nod(OLABEL, jmp.Left, nil))
if typeswvar != nil && needvar && n.Rlist != nil {
l := list1(Nod(ODCL, n.Rlist.N, nil))
l = list(l, Nod(OAS, n.Rlist.N, typeswvar))
typechecklist(l, Etop)
stat = concat(stat, l)
}
stat = concat(stat, n.Nbody)
// botch - shouldn't fall thru declaration
last := stat.End.N
if last.Xoffset == n.Xoffset && last.Op == OXFALL {
if typeswvar != nil {
setlineno(last)
Yyerror("cannot fallthrough in type switch")
}
if l.Next == nil {
setlineno(last)
Yyerror("cannot fallthrough final case in switch")
}
last.Op = OFALL
} else {
stat = list(stat, br)
}
}
stat = list(stat, br)
if def != nil {
cas = list(cas, def)
}
sw.List = cas
sw.Nbody = 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 l := sw.List; l != nil; l = l.Next {
n := l.N
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(int(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 = 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 *NodeList
// predeclare temporary variables and the boolean var
s.facename = temp(cond.Right.Type)
a := Nod(OAS, s.facename, cond.Right)
typecheck(&a, Etop)
cas = list(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)
// calculate type hash
t := cond.Right.Type
if isnilinter(t) {
a = syslook("efacethash", 1)
} else {
a = syslook("ifacethash", 1)
}
substArgTypes(a, t)
a = Nod(OCALL, a, nil)
a.List = list1(s.facename)
a = Nod(OAS, s.hashname, a)
typecheck(&a, Etop)
cas = list(cas, a)
cc := caseClauses(sw, switchKindType)
sw.List = 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)
}
// insert type equality check into each case block
for _, c := range cc {
n := c.node
switch c.typ {
case caseKindTypeNil:
var v Val
v.U = new(NilVal)
a = Nod(OIF, nil, nil)
a.Left = Nod(OEQ, s.facename, nodlit(v))
typecheck(&a.Left, Erv)
a.Nbody = list1(n.Right) // if i==nil { goto l }
n.Right = a
case caseKindTypeVar, caseKindTypeConst:
n.Right = s.typeone(n)
}
}
// generate list of if statements, binary search for constant sequences
for len(cc) > 0 {
if cc[0].typ != caseKindTypeConst {
n := cc[0].node
cas = list(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 = list(cas, n.Right)
}
continue
}
// combine adjacent cases with the same hash
ncase := 0
for i := 0; i < run; i++ {
ncase++
hash := list1(cc[i].node.Right)
for j := i + 1; j < run && cc[i].hash == cc[j].hash; j++ {
hash = list(hash, cc[j].node.Right)
}
cc[i].node.Right = liststmt(hash)
}
// binary search among cases to narrow by hash
cas = list(cas, s.walkCases(cc[:ncase]))
cc = cc[ncase:]
}
// handle default case
if nerrors == 0 {
cas = list(cas, def)
sw.Nbody = concat(cas, sw.Nbody)
sw.List = nil
walkstmtlist(sw.Nbody)
}
}
// 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 *NodeList
if t.Rlist == nil {
name = nblank
typecheck(&nblank, Erv|Easgn)
} else {
name = t.Rlist.N
init = list1(Nod(ODCL, name, nil))
a := Nod(OAS, name, nil)
typecheck(&a, Etop)
init = list(init, a)
}
a := Nod(OAS2, nil, nil)
a.List = list(list1(name), s.okname) // name, ok =
b := Nod(ODOTTYPE, s.facename, nil)
b.Type = t.Left.Type // interface.(type)
a.Rlist = list1(b)
typecheck(&a, Etop)
init = list(init, a)
c := Nod(OIF, nil, nil)
c.Left = s.okname
c.Nbody = list1(t.Right) // if ok { goto l }
return liststmt(list(init, c))
}
// walkCases generates an AST implementing the cases in cc.
func (s *typeSwitch) walkCases(cc []*caseClause) *Node {
if len(cc) < binarySearchMin {
var cas *NodeList
for _, c := range cc {
n := c.node
if c.typ != caseKindTypeConst {
Fatal("typeSwitch walkCases")
}
a := Nod(OIF, nil, nil)
a.Left = Nod(OEQ, s.hashname, Nodintconst(int64(c.hash)))
typecheck(&a.Left, Erv)
a.Nbody = list1(n.Right)
cas = list(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 = list1(s.walkCases(cc[:half]))
a.Rlist = list1(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 := int(n1.Val().Ctype())
if ct > int(n2.Val().Ctype()) {
return +1
}
if ct < int(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
}
return stringsCompare(a, b)
}
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
}
func dumpcase(cc []*caseClause) {
for _, c := range cc {
switch c.typ {
case caseKindDefault:
fmt.Printf("case-default\n")
fmt.Printf("\tord=%d\n", c.ordinal)
case caseKindExprConst:
fmt.Printf("case-exprconst\n")
fmt.Printf("\tord=%d\n", c.ordinal)
case caseKindExprVar:
fmt.Printf("case-exprvar\n")
fmt.Printf("\tord=%d\n", c.ordinal)
fmt.Printf("\top=%v\n", Oconv(int(c.node.Left.Op), 0))
case caseKindTypeNil:
fmt.Printf("case-typenil\n")
fmt.Printf("\tord=%d\n", c.ordinal)
case caseKindTypeConst:
fmt.Printf("case-typeconst\n")
fmt.Printf("\tord=%d\n", c.ordinal)
fmt.Printf("\thash=%x\n", c.hash)
case caseKindTypeVar:
fmt.Printf("case-typevar\n")
fmt.Printf("\tord=%d\n", c.ordinal)
default:
fmt.Printf("case-???\n")
fmt.Printf("\tord=%d\n", c.ordinal)
fmt.Printf("\top=%v\n", Oconv(int(c.node.Left.Op), 0))
fmt.Printf("\thash=%x\n", c.hash)
}
}
fmt.Printf("\n")
}