2015-02-13 14:40:36 -05:00
// 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"
2015-02-27 20:44:45 +00:00
"sort"
"strconv"
2015-02-13 14:40:36 -05:00
)
const (
2015-02-27 20:44:45 +00:00
// expression switch
switchKindExpr = iota // switch a {...} or switch 5 {...}
switchKindTrue // switch true {...} or switch {...}
switchKindFalse // switch false {...}
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
// type switch
switchKindType // switch a.(type) {...}
)
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
const (
caseKindDefault = iota // default:
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
// expression switch
caseKindExprConst // case 5:
caseKindExprVar // case x:
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
// type switch
caseKindTypeNil // case nil:
caseKindTypeConst // case time.Time: (concrete type, has type hash)
caseKindTypeVar // case io.Reader: (interface type)
)
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
const binarySearchMin = 4 // minimum number of cases for binary search
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
// 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*)
2015-02-13 14:40:36 -05:00
}
2015-02-27 20:44:45 +00:00
// 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
2015-02-13 14:40:36 -05:00
}
2015-02-27 20:44:45 +00:00
// 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
}
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
// typecheckswitch typechecks a switch statement.
func typecheckswitch ( n * Node ) {
lno := int ( lineno )
typechecklist ( n . Ninit , Etop )
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
var nilonly string
var top int
var t * Type
2015-02-13 14:40:36 -05:00
2015-05-26 21:30:20 -04:00
if n . Left != nil && n . Left . Op == OTYPESW {
2015-02-27 20:44:45 +00:00
// type switch
top = Etype
2015-05-26 21:30:20 -04:00
typecheck ( & n . Left . Right , Erv )
t = n . Left . Right . Type
2015-02-27 20:44:45 +00:00
if t != nil && t . Etype != TINTER {
2015-05-26 21:30:20 -04:00
Yyerror ( "cannot type switch on non-interface value %v" , Nconv ( n . Left . Right , obj . FmtLong ) )
2015-02-27 20:44:45 +00:00
}
} else {
// expression switch
top = Erv
2015-05-26 21:30:20 -04:00
if n . Left != nil {
typecheck ( & n . Left , Erv )
defaultlit ( & n . Left , nil )
t = n . Left . Type
2015-02-13 14:40:36 -05:00
} else {
2015-02-27 20:44:45 +00:00
t = Types [ TBOOL ]
}
if t != nil {
var badtype * Type
switch {
2015-03-01 07:54:01 +00:00
case ! okforeq [ t . Etype ] :
2015-05-26 21:30:20 -04:00
Yyerror ( "cannot switch on %v" , Nconv ( n . Left , obj . FmtLong ) )
2015-02-27 20:44:45 +00:00
case t . Etype == TARRAY && ! Isfixedarray ( t ) :
nilonly = "slice"
case t . Etype == TARRAY && Isfixedarray ( t ) && algtype1 ( t , nil ) == ANOEQ :
2015-05-26 21:30:20 -04:00
Yyerror ( "cannot switch on %v" , Nconv ( n . Left , obj . FmtLong ) )
2015-02-27 20:44:45 +00:00
case t . Etype == TSTRUCT && algtype1 ( t , & badtype ) == ANOEQ :
2015-05-26 21:30:20 -04:00
Yyerror ( "cannot switch on %v (struct containing %v cannot be compared)" , Nconv ( n . Left , obj . FmtLong ) , badtype )
2015-02-27 20:44:45 +00:00
case t . Etype == TFUNC :
nilonly = "func"
case t . Etype == TMAP :
nilonly = "map"
}
2015-02-13 14:40:36 -05:00
}
}
2015-02-27 20:44:45 +00:00
n . Type = t
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
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 :
2015-04-17 12:03:22 -04:00
Yyerror ( "type %v is not an expression" , ll . N . Type )
2015-02-27 20:44:45 +00:00
case ll . N . Type != nil && assignop ( ll . N . Type , t , nil ) == 0 && assignop ( t , ll . N . Type , nil ) == 0 :
2015-05-26 21:30:20 -04:00
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 )
2015-02-27 20:44:45 +00:00
} else {
2015-04-17 12:03:22 -04:00
Yyerror ( "invalid case %v in switch (mismatched types %v and bool)" , ll . N , ll . N . Type )
2015-02-27 20:44:45 +00:00
}
case nilonly != "" && ! Isconst ( ll . N , CTNIL ) :
2015-05-26 21:30:20 -04:00
Yyerror ( "invalid case %v in switch (can only compare %s %v to nil)" , ll . N , nilonly , n . Left )
2015-02-27 20:44:45 +00:00
}
// 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
2015-05-26 21:30:20 -04:00
ll . N = n . Left . Right
2015-02-27 20:44:45 +00:00
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 {
2015-05-26 21:30:20 -04:00
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 ) )
2015-02-27 20:44:45 +00:00
} else if missing . Broke == 0 {
2015-05-26 21:30:20 -04:00
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 )
2015-02-27 20:44:45 +00:00
}
}
}
}
}
if top == Etype && n . Type != nil {
ll = ncase . List
nvar := ncase . Nname
if nvar != nil {
if ll != nil && ll . Next == nil && ll . N . Type != nil && ! Istype ( ll . N . Type , TNIL ) {
// single entry type switch
2015-05-19 15:25:35 -07:00
nvar . Param . Ntype = typenod ( ll . N . Type )
2015-02-27 20:44:45 +00:00
} else {
// multiple entry type switch or default
2015-05-19 15:25:35 -07:00
nvar . Param . Ntype = typenod ( n . Type )
2015-02-27 20:44:45 +00:00
}
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
typecheck ( & nvar , Erv | Easgn )
ncase . Nname = nvar
}
}
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
typechecklist ( ncase . Nbody , Etop )
2015-02-13 14:40:36 -05:00
}
2015-02-27 20:44:45 +00:00
lineno = int32 ( lno )
2015-02-13 14:40:36 -05:00
}
2015-02-27 20:44:45 +00:00
// walkswitch walks a switch statement.
func walkswitch ( sw * Node ) {
// convert switch {...} to switch true {...}
2015-05-26 21:30:20 -04:00
if sw . Left == nil {
sw . Left = Nodbool ( true )
typecheck ( & sw . Left , Erv )
2015-02-13 14:40:36 -05:00
}
2015-05-26 21:30:20 -04:00
if sw . Left . Op == OTYPESW {
2015-02-27 20:44:45 +00:00
var s typeSwitch
s . walk ( sw )
} else {
var s exprSwitch
s . walk ( sw )
2015-02-13 14:40:36 -05:00
}
}
2015-02-27 20:44:45 +00:00
// 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 )
2015-02-13 14:40:36 -05:00
2015-05-26 21:30:20 -04:00
cond := sw . Left
sw . Left = nil
2015-02-27 20:44:45 +00:00
s . kind = switchKindExpr
2015-05-26 21:30:20 -04:00
if Isconst ( cond , CTBOOL ) {
2015-02-27 20:44:45 +00:00
s . kind = switchKindTrue
2015-05-26 21:30:20 -04:00
if ! cond . Val . U . ( bool ) {
2015-02-27 20:44:45 +00:00
s . kind = switchKindFalse
2015-02-13 14:40:36 -05:00
}
}
2015-05-26 21:30:20 -04:00
walkexpr ( & cond , & sw . Ninit )
2015-02-27 20:44:45 +00:00
t := sw . Type
if t == nil {
return
}
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
// convert the switch into OIF statements
var cas * NodeList
if s . kind == switchKindTrue || s . kind == switchKindFalse {
s . exprname = Nodbool ( s . kind == switchKindTrue )
2015-05-26 21:30:20 -04:00
} else if consttype ( cond ) >= 0 {
2015-02-27 20:44:45 +00:00
// leave constants to enable dead code elimination (issue 9608)
2015-05-26 21:30:20 -04:00
s . exprname = cond
2015-02-13 14:40:36 -05:00
} else {
2015-05-26 21:30:20 -04:00
s . exprname = temp ( cond . Type )
cas = list1 ( Nod ( OAS , s . exprname , cond ) )
2015-02-27 20:44:45 +00:00
typechecklist ( cas , Etop )
2015-02-13 14:40:36 -05:00
}
2015-02-27 20:44:45 +00:00
// enumerate the cases, and lop off the default case
cc := caseClauses ( sw , s . kind )
2015-05-26 21:30:20 -04:00
sw . List = nil
2015-02-27 20:44:45 +00:00
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 )
}
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
// handle the cases in order
for len ( cc ) > 0 {
// deal with expressions one at a time
2015-03-01 07:54:01 +00:00
if ! okforcmp [ t . Etype ] || cc [ 0 ] . typ != caseKindExprConst {
2015-02-27 20:44:45 +00:00
a := s . walkCases ( cc [ : 1 ] )
cas = list ( cas , a )
cc = cc [ 1 : ]
continue
2015-02-13 14:40:36 -05:00
}
2015-02-27 20:44:45 +00:00
// do binary search on runs of constants
var run int
for run = 1 ; run < len ( cc ) && cc [ run ] . typ == caseKindExprConst ; run ++ {
2015-02-13 14:40:36 -05:00
}
2015-02-27 20:44:45 +00:00
// sort and compile constants
sort . Sort ( caseClauseByExpr ( cc [ : run ] ) )
a := s . walkCases ( cc [ : run ] )
cas = list ( cas , a )
cc = cc [ run : ]
2015-02-13 14:40:36 -05:00
}
2015-02-27 20:44:45 +00:00
// handle default case
if nerrors == 0 {
cas = list ( cas , def )
sw . Nbody = concat ( cas , sw . Nbody )
walkstmtlist ( sw . Nbody )
}
2015-05-26 21:30:20 -04:00
2015-02-13 14:40:36 -05:00
}
2015-02-27 20:44:45 +00:00
// 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 {
2015-05-26 21:30:20 -04:00
a . Left = Nod ( OEQ , s . exprname , n . Left ) // if name == val
typecheck ( & a . Left , Erv )
2015-02-27 20:44:45 +00:00
} else if s . kind == switchKindTrue {
2015-05-26 21:30:20 -04:00
a . Left = n . Left // if val
2015-02-27 20:44:45 +00:00
} else {
// s.kind == switchKindFalse
2015-05-26 21:30:20 -04:00
a . Left = Nod ( ONOT , n . Left , nil ) // if !val
typecheck ( & a . Left , Erv )
2015-02-27 20:44:45 +00:00
}
a . Nbody = list1 ( n . Right ) // goto l
cas = list ( cas , a )
lineno = int32 ( lno )
}
return liststmt ( cas )
}
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
// find the middle and recur
half := len ( cc ) / 2
a := Nod ( OIF , nil , nil )
2015-03-17 16:10:31 -07:00
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 ) )
2015-05-26 21:30:20 -04:00
a . Left = Nod ( OOROR , lenlt , Nod ( OANDAND , leneq , le ) )
2015-03-17 16:10:31 -07:00
} else {
2015-05-26 21:30:20 -04:00
a . Left = le
2015-03-17 16:10:31 -07:00
}
2015-05-26 21:30:20 -04:00
typecheck ( & a . Left , Erv )
2015-02-27 20:44:45 +00:00
a . Nbody = list1 ( s . walkCases ( cc [ : half ] ) )
2015-05-22 01:16:52 -04:00
a . Rlist = list1 ( s . walkCases ( cc [ half : ] ) )
2015-02-27 20:44:45 +00:00
return a
2015-02-13 14:40:36 -05:00
}
2015-02-27 20:44:45 +00:00
// casebody builds separate lists of statements and cases.
// It makes labels between cases and statements
// and deals with fallthrough, break, and unreachable statements.
2015-02-13 14:40:36 -05:00
func casebody ( sw * Node , typeswvar * Node ) {
if sw . List == nil {
return
}
2015-02-23 16:07:24 -05:00
lno := setlineno ( sw )
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
var cas * NodeList // cases
var stat * NodeList // statements
var def * Node // defaults
2015-02-23 16:07:24 -05:00
br := Nod ( OBREAK , nil , nil )
2015-02-13 14:40:36 -05:00
2015-02-23 16:07:24 -05:00
for l := sw . List ; l != nil ; l = l . Next {
2015-02-27 20:44:45 +00:00
n := l . N
2015-02-13 14:40:36 -05:00
setlineno ( n )
if n . Op != OXCASE {
Fatal ( "casebody %v" , Oconv ( int ( n . Op ) , 0 ) )
}
n . Op = OCASE
2015-02-27 20:44:45 +00:00
needvar := count ( n . List ) != 1 || n . List . N . Op == OLITERAL
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
jmp := Nod ( OGOTO , newCaseLabel ( ) , nil )
2015-02-13 14:40:36 -05:00
if n . List == nil {
if def != nil {
Yyerror ( "more than one default case" )
}
// reuse original default case
2015-02-27 20:44:45 +00:00
n . Right = jmp
2015-02-13 14:40:36 -05:00
def = n
}
if n . List != nil && n . List . Next == nil {
2015-02-27 20:44:45 +00:00
// one case -- reuse OCASE node
n . Left = n . List . N
n . Right = jmp
2015-02-13 14:40:36 -05:00
n . List = nil
cas = list ( cas , n )
} else {
// expand multi-valued cases
2015-02-27 20:44:45 +00:00
for lc := n . List ; lc != nil ; lc = lc . Next {
cas = list ( cas , Nod ( OCASE , lc . N , jmp ) )
2015-02-13 14:40:36 -05:00
}
}
2015-02-27 20:44:45 +00:00
stat = list ( stat , Nod ( OLABEL , jmp . Left , nil ) )
2015-02-17 22:13:49 -05:00
if typeswvar != nil && needvar && n . Nname != nil {
2015-02-23 16:07:24 -05:00
l := list1 ( Nod ( ODCL , n . Nname , nil ) )
2015-02-13 14:40:36 -05:00
l = list ( l , Nod ( OAS , n . Nname , typeswvar ) )
typechecklist ( l , Etop )
stat = concat ( stat , l )
}
stat = concat ( stat , n . Nbody )
// botch - shouldn't fall thru declaration
2015-02-27 20:44:45 +00:00
last := stat . End . N
2015-02-13 14:40:36 -05:00
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
}
2015-02-27 20:44:45 +00:00
// nSwitchLabel is the number of switch labels generated.
// This should be per-function, but it is a global counter for now.
var nSwitchLabel int
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
func newCaseLabel ( ) * Node {
label := strconv . Itoa ( nSwitchLabel )
nSwitchLabel ++
return newname ( Lookup ( label ) )
}
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
// 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
2015-02-23 16:07:24 -05:00
for l := sw . List ; l != nil ; l = l . Next {
2015-02-27 20:44:45 +00:00
n := l . N
c := new ( caseClause )
cc = append ( cc , c )
c . ordinal = len ( cc )
2015-02-13 14:40:36 -05:00
c . node = n
if n . Left == nil {
2015-02-27 20:44:45 +00:00
c . typ = caseKindDefault
2015-02-13 14:40:36 -05:00
continue
}
2015-02-27 20:44:45 +00:00
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 )
2015-02-13 14:40:36 -05:00
}
2015-02-27 20:44:45 +00:00
} else {
// expression switch
2015-02-13 14:40:36 -05:00
switch consttype ( n . Left ) {
2015-02-27 20:44:45 +00:00
case CTFLT , CTINT , CTRUNE , CTSTR :
c . typ = caseKindExprConst
default :
c . typ = caseKindExprVar
2015-02-13 14:40:36 -05:00
}
}
}
2015-02-27 20:44:45 +00:00
if cc == nil {
2015-02-13 14:40:36 -05:00
return nil
}
// sort by value and diagnose duplicate cases
2015-02-27 20:44:45 +00:00
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 {
2015-02-13 14:40:36 -05:00
break
}
2015-02-27 20:44:45 +00:00
if Eqtype ( c1 . node . Left . Type , c2 . node . Left . Type ) {
2015-04-17 12:03:22 -04:00
yyerrorl ( int ( c2 . node . Lineno ) , "duplicate case %v in type switch\n\tprevious case at %v" , c2 . node . Left . Type , c1 . node . Line ( ) )
2015-02-13 14:40:36 -05:00
}
}
}
2015-02-27 20:44:45 +00:00
} 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 {
2015-02-13 14:40:36 -05:00
continue
}
2015-02-27 20:44:45 +00:00
setlineno ( c2 . node )
2015-04-17 12:03:22 -04:00
Yyerror ( "duplicate case %v in switch\n\tprevious case at %v" , c1 . node . Left , c1 . node . Line ( ) )
2015-02-13 14:40:36 -05:00
}
}
// put list back in processing order
2015-02-27 20:44:45 +00:00
sort . Sort ( caseClauseByOrd ( cc ) )
return cc
2015-02-13 14:40:36 -05:00
}
2015-02-27 20:44:45 +00:00
// 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 ) {
2015-05-26 21:30:20 -04:00
cond := sw . Left
sw . Left = nil
if cond == nil {
sw . List = nil
2015-02-13 14:40:36 -05:00
return
}
2015-05-26 21:30:20 -04:00
if cond . Right == nil {
2015-02-13 14:40:36 -05:00
setlineno ( sw )
Yyerror ( "type switch must have an assignment" )
return
}
2015-05-26 21:30:20 -04:00
walkexpr ( & cond . Right , & sw . Ninit )
if ! Istype ( cond . Right . Type , TINTER ) {
2015-02-13 14:40:36 -05:00
Yyerror ( "type switch must be on an interface" )
return
}
2015-02-27 20:44:45 +00:00
var cas * NodeList
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
// predeclare temporary variables and the boolean var
2015-05-26 21:30:20 -04:00
s . facename = temp ( cond . Right . Type )
2015-02-13 14:40:36 -05:00
2015-05-26 21:30:20 -04:00
a := Nod ( OAS , s . facename , cond . Right )
2015-02-13 14:40:36 -05:00
typecheck ( & a , Etop )
cas = list ( cas , a )
2015-02-27 20:44:45 +00:00
s . okname = temp ( Types [ TBOOL ] )
typecheck ( & s . okname , Erv )
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
s . hashname = temp ( Types [ TUINT32 ] )
typecheck ( & s . hashname , Erv )
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
// set up labels and jumps
casebody ( sw , s . facename )
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
// calculate type hash
2015-05-26 21:30:20 -04:00
t := cond . Right . Type
2015-02-17 22:13:49 -05:00
if isnilinter ( t ) {
2015-02-13 14:40:36 -05:00
a = syslook ( "efacethash" , 1 )
} else {
a = syslook ( "ifacethash" , 1 )
}
2015-03-08 13:33:49 -04:00
substArgTypes ( a , t )
2015-02-13 14:40:36 -05:00
a = Nod ( OCALL , a , nil )
2015-02-27 20:44:45 +00:00
a . List = list1 ( s . facename )
a = Nod ( OAS , s . hashname , a )
2015-02-13 14:40:36 -05:00
typecheck ( & a , Etop )
cas = list ( cas , a )
2015-02-27 20:44:45 +00:00
cc := caseClauses ( sw , switchKindType )
2015-05-26 21:30:20 -04:00
sw . List = nil
2015-02-23 16:07:24 -05:00
var def * Node
2015-02-27 20:44:45 +00:00
if len ( cc ) > 0 && cc [ 0 ] . typ == caseKindDefault {
def = cc [ 0 ] . node . Right
cc = cc [ 1 : ]
2015-02-13 14:40:36 -05:00
} else {
def = Nod ( OBREAK , nil , nil )
}
2015-02-27 20:44:45 +00:00
// insert type equality check into each case block
for _ , c := range cc {
n := c . node
switch c . typ {
case caseKindTypeNil :
var v Val
2015-05-26 22:50:45 -04:00
v . U = new ( NilVal )
2015-02-13 14:40:36 -05:00
a = Nod ( OIF , nil , nil )
2015-05-26 21:30:20 -04:00
a . Left = Nod ( OEQ , s . facename , nodlit ( v ) )
typecheck ( & a . Left , Erv )
2015-02-13 14:40:36 -05:00
a . Nbody = list1 ( n . Right ) // if i==nil { goto l }
n . Right = a
2015-02-27 20:44:45 +00:00
case caseKindTypeVar , caseKindTypeConst :
n . Right = s . typeone ( n )
2015-02-13 14:40:36 -05:00
}
}
2015-02-27 20:44:45 +00:00
// generate list of if statements, binary search for constant sequences
for len ( cc ) > 0 {
if cc [ 0 ] . typ != caseKindTypeConst {
n := cc [ 0 ] . node
2015-02-13 14:40:36 -05:00
cas = list ( cas , n . Right )
2015-02-27 20:44:45 +00:00
cc = cc [ 1 : ]
2015-02-13 14:40:36 -05:00
continue
}
// identify run of constants
2015-02-27 20:44:45 +00:00
var run int
for run = 1 ; run < len ( cc ) && cc [ run ] . typ == caseKindTypeConst ; run ++ {
2015-02-13 14:40:36 -05:00
}
// sort by hash
2015-02-27 20:44:45 +00:00
sort . Sort ( caseClauseByType ( cc [ : run ] ) )
2015-02-13 14:40:36 -05:00
// for debugging: linear search
if false {
2015-02-27 20:44:45 +00:00
for i := 0 ; i < run ; i ++ {
n := cc [ i ] . node
2015-02-13 14:40:36 -05:00
cas = list ( cas , n . Right )
}
continue
}
// combine adjacent cases with the same hash
2015-02-27 20:44:45 +00:00
ncase := 0
for i := 0 ; i < run ; i ++ {
2015-02-13 14:40:36 -05:00
ncase ++
2015-02-27 20:44:45 +00:00
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 )
2015-02-13 14:40:36 -05:00
}
2015-02-27 20:44:45 +00:00
cc [ i ] . node . Right = liststmt ( hash )
2015-02-13 14:40:36 -05:00
}
// binary search among cases to narrow by hash
2015-02-27 20:44:45 +00:00
cas = list ( cas , s . walkCases ( cc [ : ncase ] ) )
cc = cc [ ncase : ]
2015-02-13 14:40:36 -05:00
}
2015-02-27 20:44:45 +00:00
// handle default case
2015-02-13 14:40:36 -05:00
if nerrors == 0 {
cas = list ( cas , def )
sw . Nbody = concat ( cas , sw . Nbody )
sw . List = nil
walkstmtlist ( sw . Nbody )
}
}
2015-02-27 20:44:45 +00:00
// typeone generates an AST that jumps to the
// case body if the variable is of type t.
func ( s * typeSwitch ) typeone ( t * Node ) * Node {
name := t . Nname
var init * NodeList
if name == nil {
typecheck ( & nblank , Erv | Easgn )
name = nblank
} else {
init = list1 ( Nod ( ODCL , name , nil ) )
2015-02-13 14:40:36 -05:00
}
2015-02-27 20:44:45 +00:00
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 )
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
c := Nod ( OIF , nil , nil )
2015-05-26 21:30:20 -04:00
c . Left = s . okname
2015-02-27 20:44:45 +00:00
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 )
2015-05-26 21:30:20 -04:00
a . Left = Nod ( OEQ , s . hashname , Nodintconst ( int64 ( c . hash ) ) )
typecheck ( & a . Left , Erv )
2015-02-27 20:44:45 +00:00
a . Nbody = list1 ( n . Right )
cas = list ( cas , a )
}
return liststmt ( cas )
2015-02-13 14:40:36 -05:00
}
2015-02-27 20:44:45 +00:00
// find the middle and recur
half := len ( cc ) / 2
a := Nod ( OIF , nil , nil )
2015-05-26 21:30:20 -04:00
a . Left = Nod ( OLE , s . hashname , Nodintconst ( int64 ( cc [ half - 1 ] . hash ) ) )
typecheck ( & a . Left , Erv )
2015-02-27 20:44:45 +00:00
a . Nbody = list1 ( s . walkCases ( cc [ : half ] ) )
2015-05-22 01:16:52 -04:00
a . Rlist = list1 ( s . walkCases ( cc [ half : ] ) )
2015-02-27 20:44:45 +00:00
return a
}
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
type caseClauseByOrd [ ] * caseClause
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
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
2015-02-13 14:40:36 -05:00
}
2015-02-27 20:44:45 +00:00
type caseClauseByExpr [ ] * caseClause
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
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
}
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
func exprcmp ( c1 , c2 * caseClause ) int {
// sort non-constants last
if c1 . typ != caseKindExprConst {
return + 1
}
if c2 . typ != caseKindExprConst {
return - 1
}
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
n1 := c1 . node . Left
n2 := c2 . node . Left
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
// sort by type (for switches on interface)
2015-05-26 22:50:45 -04:00
ct := int ( n1 . Val . Ctype ( ) )
if ct > int ( n2 . Val . Ctype ( ) ) {
2015-02-27 20:44:45 +00:00
return + 1
}
2015-05-26 22:50:45 -04:00
if ct < int ( n2 . Val . Ctype ( ) ) {
2015-02-27 20:44:45 +00:00
return - 1
}
if ! Eqtype ( n1 . Type , n2 . Type ) {
if n1 . Type . Vargen > n2 . Type . Vargen {
return + 1
2015-02-13 14:40:36 -05:00
} else {
2015-02-27 20:44:45 +00:00
return - 1
2015-02-13 14:40:36 -05:00
}
}
2015-02-27 20:44:45 +00:00
// sort by constant value to enable binary search
switch ct {
case CTFLT :
2015-05-14 17:57:42 -07:00
return mpcmpfltflt ( n1 . Val . U . ( * Mpflt ) , n2 . Val . U . ( * Mpflt ) )
2015-02-27 20:44:45 +00:00
case CTINT , CTRUNE :
2015-05-14 17:57:42 -07:00
return Mpcmpfixfix ( n1 . Val . U . ( * Mpint ) , n2 . Val . U . ( * Mpint ) )
2015-02-27 20:44:45 +00:00
case CTSTR :
2015-03-17 16:10:31 -07:00
// 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.
2015-05-14 17:57:42 -07:00
a := n1 . Val . U . ( string )
b := n2 . Val . U . ( string )
2015-03-17 16:10:31 -07:00
if len ( a ) < len ( b ) {
return - 1
}
if len ( a ) > len ( b ) {
return + 1
}
return stringsCompare ( a , b )
2015-02-27 20:44:45 +00:00
}
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
return 0
}
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
type caseClauseByType [ ] * caseClause
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
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
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
// sort by hash code
case c1 . hash != c2 . hash :
return c1 . hash < c2 . hash
}
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
// sort by ordinal
return c1 . ordinal < c2 . ordinal
}
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
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 )
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
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 )
}
2015-02-13 14:40:36 -05:00
}
2015-02-27 20:44:45 +00:00
fmt . Printf ( "\n" )
2015-02-13 14:40:36 -05:00
}