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"
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 ) {
2016-03-02 17:34:42 -08:00
lno := lineno
2015-02-27 20:44:45 +00:00
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
2016-03-04 17:28:07 -08:00
for it := nodeSeqIterate ( n . List ) ; ! it . Done ( ) ; it . Next ( ) {
ncase := it . N ( )
2015-02-27 20:44:45 +00:00
setlineno ( n )
2016-03-04 17:28:07 -08:00
if nodeSeqLen ( ncase . List ) == 0 {
2015-02-27 20:44:45 +00:00
// default
if def != nil {
Yyerror ( "multiple defaults in switch (first at %v)" , def . Line ( ) )
} else {
def = ncase
}
} else {
2016-03-04 17:28:07 -08:00
for it2 := nodeSeqIterate ( ncase . List ) ; ! it2 . Done ( ) ; it2 . Next ( ) {
setlineno ( it2 . N ( ) )
typecheck ( it2 . P ( ) , Erv | Etype )
if it2 . N ( ) . Type == nil || t == nil {
2015-02-27 20:44:45 +00:00
continue
}
setlineno ( ncase )
switch top {
// expression switch
case Erv :
2016-03-04 17:28:07 -08:00
defaultlit ( it2 . P ( ) , t )
2015-02-27 20:44:45 +00:00
switch {
2016-03-04 17:28:07 -08:00
case it2 . N ( ) . Op == OTYPE :
Yyerror ( "type %v is not an expression" , it2 . N ( ) . Type )
case it2 . N ( ) . Type != nil && assignop ( it2 . N ( ) . Type , t , nil ) == 0 && assignop ( t , it2 . N ( ) . Type , nil ) == 0 :
2015-05-26 21:30:20 -04:00
if n . Left != nil {
2016-03-04 17:28:07 -08:00
Yyerror ( "invalid case %v in switch on %v (mismatched types %v and %v)" , it2 . N ( ) , n . Left , it2 . N ( ) . Type , t )
2015-02-27 20:44:45 +00:00
} else {
2016-03-04 17:28:07 -08:00
Yyerror ( "invalid case %v in switch (mismatched types %v and bool)" , it2 . N ( ) , it2 . N ( ) . Type )
2015-02-27 20:44:45 +00:00
}
2016-03-04 17:28:07 -08:00
case nilonly != "" && ! isnil ( it2 . N ( ) ) :
Yyerror ( "invalid case %v in switch (can only compare %s %v to nil)" , it2 . N ( ) , nilonly , n . Left )
case Isinter ( t ) && ! Isinter ( it2 . N ( ) . Type ) && algtype1 ( it2 . N ( ) . Type , nil ) == ANOEQ :
Yyerror ( "invalid case %v in switch (incomparable type)" , Nconv ( it2 . N ( ) , obj . FmtLong ) )
2015-02-27 20:44:45 +00:00
}
// type switch
case Etype :
var missing , have * Type
var ptr int
switch {
2016-03-04 17:28:07 -08:00
case it2 . N ( ) . Op == OLITERAL && Istype ( it2 . N ( ) . Type , TNIL ) :
case it2 . N ( ) . Op != OTYPE && it2 . N ( ) . Type != nil : // should this be ||?
Yyerror ( "%v is not a type" , Nconv ( it2 . N ( ) , obj . FmtLong ) )
2015-02-27 20:44:45 +00:00
// reset to original type
2016-03-04 17:28:07 -08:00
* it2 . P ( ) = n . Left . Right
case it2 . N ( ) . Type . Etype != TINTER && t . Etype == TINTER && ! implements ( it2 . N ( ) . Type , t , & missing , & have , & ptr ) :
2015-09-07 10:37:26 +10:00
if have != nil && ! missing . Broke && ! have . Broke {
2016-03-04 17:28:07 -08: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 ) , it2 . N ( ) . Type , missing . Sym , have . Sym , Tconv ( have . Type , obj . FmtShort ) , missing . Sym , Tconv ( missing . Type , obj . FmtShort ) )
2015-09-07 10:37:26 +10:00
} else if ! missing . Broke {
2016-03-04 17:28:07 -08:00
Yyerror ( "impossible type switch case: %v cannot have dynamic type %v" + " (missing %v method)" , Nconv ( n . Left . Right , obj . FmtLong ) , it2 . N ( ) . Type , missing . Sym )
2015-02-27 20:44:45 +00:00
}
}
}
}
}
if top == Etype && n . Type != nil {
2016-03-04 17:28:07 -08:00
ll := ncase . List
if nodeSeqLen ( ncase . Rlist ) != 0 {
nvar := nodeSeqFirst ( ncase . Rlist )
if nodeSeqLen ( ll ) == 1 && nodeSeqFirst ( ll ) . Type != nil && ! Istype ( nodeSeqFirst ( ll ) . Type , TNIL ) {
2015-02-27 20:44:45 +00:00
// single entry type switch
2016-03-04 17:28:07 -08:00
nvar . Name . Param . Ntype = typenod ( nodeSeqFirst ( ll ) . Type )
2015-02-27 20:44:45 +00:00
} else {
// multiple entry type switch or default
2015-05-27 00:44:05 -04:00
nvar . Name . 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 )
2016-03-04 17:28:07 -08:00
rit := nodeSeqIterate ( ncase . Rlist )
* rit . P ( ) = nvar
2015-02-27 20:44:45 +00:00
}
}
2015-02-13 14:40:36 -05:00
2016-03-03 11:30:17 -08:00
typechecklist ( ncase . Nbody , Etop )
2015-02-13 14:40:36 -05:00
}
2016-03-02 17:34:42 -08:00
lineno = 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-27 00:47:05 -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
2016-02-27 14:31:33 -08:00
var cas [ ] * Node
2015-02-27 20:44:45 +00:00
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 )
2016-02-27 14:31:33 -08:00
cas = [ ] * Node { Nod ( OAS , s . exprname , cond ) }
typecheckslice ( 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 )
2016-03-04 17:28:07 -08:00
setNodeSeq ( & 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 ] )
2016-02-27 14:31:33 -08:00
cas = append ( cas , a )
2015-02-27 20:44:45 +00:00
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 ] )
2016-02-27 14:31:33 -08:00
cas = append ( cas , a )
2015-02-27 20:44:45 +00:00
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 {
2016-02-27 14:31:33 -08:00
cas = append ( cas , def )
sw . Nbody . Set ( append ( cas , sw . Nbody . Slice ( ) ... ) )
2016-03-07 22:54:46 -08:00
walkstmtlist ( sw . Nbody . Slice ( ) )
2015-02-27 20:44:45 +00: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
2016-03-02 17:34:42 -08:00
lno := setlineno ( n )
2015-02-27 20:44:45 +00:00
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
}
2016-02-27 14:31:33 -08:00
a . Nbody . Set ( [ ] * Node { n . Right } ) // goto l
2015-02-27 20:44:45 +00:00
cas = list ( cas , a )
2016-03-02 17:34:42 -08:00
lineno = lno
2015-02-27 20:44:45 +00:00
}
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 )
2016-02-27 14:31:33 -08:00
a . Nbody . Set ( [ ] * Node { s . walkCases ( cc [ : half ] ) } )
2016-03-04 17:28:07 -08:00
setNodeSeq ( & a . Rlist , [ ] * Node { 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 ) {
2016-03-04 17:28:07 -08:00
if nodeSeqLen ( sw . List ) == 0 {
2015-02-13 14:40:36 -05:00
return
}
2015-02-23 16:07:24 -05:00
lno := setlineno ( sw )
2015-02-13 14:40:36 -05:00
2016-03-04 17:28:07 -08:00
var cas [ ] * Node // cases
var stat [ ] * Node // 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
2016-03-04 17:28:07 -08:00
for it := nodeSeqIterate ( sw . List ) ; ! it . Done ( ) ; it . Next ( ) {
n := it . N ( )
2015-02-13 14:40:36 -05:00
setlineno ( n )
if n . Op != OXCASE {
2016-03-07 08:23:55 -08:00
Fatalf ( "casebody %v" , Oconv ( n . Op , 0 ) )
2015-02-13 14:40:36 -05:00
}
n . Op = OCASE
2016-03-04 17:28:07 -08:00
needvar := nodeSeqLen ( n . List ) != 1 || nodeSeqFirst ( n . List ) . Op == OLITERAL
2015-02-13 14:40:36 -05:00
2015-02-27 20:44:45 +00:00
jmp := Nod ( OGOTO , newCaseLabel ( ) , nil )
2016-03-04 17:28:07 -08:00
if nodeSeqLen ( n . List ) == 0 {
2015-02-13 14:40:36 -05:00
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
}
2016-03-04 17:28:07 -08:00
if nodeSeqLen ( n . List ) == 1 {
2015-02-27 20:44:45 +00:00
// one case -- reuse OCASE node
2016-03-04 17:28:07 -08:00
n . Left = nodeSeqFirst ( n . List )
2015-02-27 20:44:45 +00:00
n . Right = jmp
2016-03-04 17:28:07 -08:00
setNodeSeq ( & n . List , nil )
cas = append ( cas , n )
2015-02-13 14:40:36 -05:00
} else {
// expand multi-valued cases
2016-03-04 17:28:07 -08:00
for lcit := nodeSeqIterate ( n . List ) ; ! lcit . Done ( ) ; lcit . Next ( ) {
cas = append ( cas , Nod ( OCASE , lcit . N ( ) , jmp ) )
2015-02-13 14:40:36 -05:00
}
}
2016-02-27 14:31:33 -08:00
stat = append ( stat , Nod ( OLABEL , jmp . Left , nil ) )
2016-03-04 17:28:07 -08:00
if typeswvar != nil && needvar && nodeSeqLen ( n . Rlist ) != 0 {
2016-02-27 14:31:33 -08:00
l := [ ] * Node {
2016-03-04 17:28:07 -08:00
Nod ( ODCL , nodeSeqFirst ( n . Rlist ) , nil ) ,
Nod ( OAS , nodeSeqFirst ( n . Rlist ) , typeswvar ) ,
2016-02-27 14:31:33 -08:00
}
typecheckslice ( l , Etop )
stat = append ( stat , l ... )
2015-02-13 14:40:36 -05:00
}
2016-02-27 14:31:33 -08:00
stat = append ( stat , n . Nbody . Slice ( ) ... )
2015-02-13 14:40:36 -05:00
// botch - shouldn't fall thru declaration
2016-02-27 14:31:33 -08:00
last := stat [ len ( stat ) - 1 ]
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" )
}
2016-03-04 17:28:07 -08:00
if it . Len ( ) <= 1 {
2015-02-13 14:40:36 -05:00
setlineno ( last )
Yyerror ( "cannot fallthrough final case in switch" )
}
last . Op = OFALL
} else {
2016-02-27 14:31:33 -08:00
stat = append ( stat , br )
2015-02-13 14:40:36 -05:00
}
}
2016-02-27 14:31:33 -08:00
stat = append ( stat , br )
2015-02-13 14:40:36 -05:00
if def != nil {
2016-03-04 17:28:07 -08:00
cas = append ( cas , def )
2015-02-13 14:40:36 -05:00
}
2016-03-04 17:28:07 -08:00
setNodeSeq ( & sw . List , cas )
2016-02-27 14:31:33 -08:00
sw . Nbody . Set ( stat )
2015-02-13 14:40:36 -05:00
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
2016-03-04 17:28:07 -08:00
for it := nodeSeqIterate ( sw . List ) ; ! it . Done ( ) ; it . Next ( ) {
n := it . N ( )
2015-02-27 20:44:45 +00:00
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 ) {
2016-03-02 11:01:25 -08:00
yyerrorl ( 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 {
2016-03-04 17:28:07 -08:00
setNodeSeq ( & 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
}
2016-02-27 14:31:33 -08:00
var cas [ ] * Node
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 )
2016-02-27 14:31:33 -08:00
cas = append ( cas , a )
2015-02-13 14:40:36 -05:00
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
cc := caseClauses ( sw , switchKindType )
2016-03-04 17:28:07 -08:00
setNodeSeq ( & 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 )
}
2016-02-21 20:43:14 -08:00
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.
2016-02-27 14:31:33 -08:00
i . Nbody . Set ( [ ] * Node { typenil } )
2016-02-21 20:43:14 -08:00
} else {
// Jump to default case.
lbl := newCaseLabel ( )
2016-02-27 14:31:33 -08:00
i . Nbody . Set ( [ ] * Node { Nod ( OGOTO , lbl , nil ) } )
2016-02-21 20:43:14 -08:00
// Wrap default case with label.
blk := Nod ( OBLOCK , nil , nil )
2016-03-04 17:28:07 -08:00
setNodeSeq ( & blk . List , [ ] * Node { Nod ( OLABEL , lbl , nil ) , def } )
2016-02-21 20:43:14 -08:00
def = blk
}
typecheck ( & i . Left , Erv )
2016-02-27 14:31:33 -08:00
cas = append ( cas , i )
2016-02-21 20:43:14 -08:00
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 )
2016-02-27 14:31:33 -08:00
cas = append ( cas , a )
2015-02-13 14:40:36 -05:00
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 caseKindTypeVar , caseKindTypeConst :
n . Right = s . typeone ( n )
2016-02-21 20:43:14 -08:00
default :
Fatalf ( "typeSwitch with bad kind: %d" , c . typ )
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
2016-02-27 14:31:33 -08:00
cas = append ( 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
2016-02-27 14:31:33 -08:00
cas = append ( cas , n . Right )
2015-02-13 14:40:36 -05:00
}
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
2016-02-27 14:31:33 -08:00
cas = append ( cas , s . walkCases ( cc [ : ncase ] ) )
2015-02-27 20:44:45 +00:00
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 {
2016-02-27 14:31:33 -08:00
cas = append ( cas , def )
sw . Nbody . Set ( append ( cas , sw . Nbody . Slice ( ) ... ) )
2016-03-04 17:28:07 -08:00
setNodeSeq ( & sw . List , nil )
2016-03-07 22:54:46 -08:00
walkstmtlist ( sw . Nbody . Slice ( ) )
2015-02-13 14:40:36 -05:00
}
}
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 {
2015-05-27 10:43:53 -04:00
var name * Node
2015-02-27 20:44:45 +00:00
var init * NodeList
2016-03-04 17:28:07 -08:00
if nodeSeqLen ( t . Rlist ) == 0 {
2015-02-27 20:44:45 +00:00
name = nblank
2015-05-27 10:43:53 -04:00
typecheck ( & nblank , Erv | Easgn )
2015-02-27 20:44:45 +00:00
} else {
2016-03-04 17:28:07 -08:00
name = nodeSeqFirst ( t . Rlist )
2015-02-27 20:44:45 +00:00
init = list1 ( Nod ( ODCL , name , nil ) )
2015-07-30 00:46:42 -04:00
a := Nod ( OAS , name , nil )
typecheck ( & a , Etop )
init = list ( init , a )
2015-02-13 14:40:36 -05:00
}
2015-02-27 20:44:45 +00:00
a := Nod ( OAS2 , nil , nil )
2016-03-04 17:28:07 -08:00
setNodeSeq ( & a . List , [ ] * Node { name , s . okname } ) // name, ok =
2015-02-27 20:44:45 +00:00
b := Nod ( ODOTTYPE , s . facename , nil )
b . Type = t . Left . Type // interface.(type)
2016-03-04 17:28:07 -08:00
setNodeSeq ( & a . Rlist , [ ] * Node { b } )
2015-02-27 20:44:45 +00:00
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
2016-02-27 14:31:33 -08:00
c . Nbody . Set ( [ ] * Node { t . Right } ) // if ok { goto l }
2015-02-27 20:44:45 +00:00
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 {
2015-08-30 23:10:03 +02:00
Fatalf ( "typeSwitch walkCases" )
2015-02-27 20:44:45 +00:00
}
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 )
2016-02-27 14:31:33 -08:00
a . Nbody . Set ( [ ] * Node { n . Right } )
2015-02-27 20:44:45 +00:00
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 )
2016-02-27 14:31:33 -08:00
a . Nbody . Set ( [ ] * Node { s . walkCases ( cc [ : half ] ) } )
2016-03-04 17:28:07 -08:00
setNodeSeq ( & a . Rlist , [ ] * Node { 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-10-26 16:00:59 -07:00
ct := n1 . Val ( ) . Ctype ( )
if ct > n2 . Val ( ) . Ctype ( ) {
2015-02-27 20:44:45 +00:00
return + 1
}
2015-10-26 16:00:59 -07:00
if ct < 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-27 00:47:05 -04:00
return mpcmpfltflt ( n1 . Val ( ) . U . ( * Mpflt ) , n2 . Val ( ) . U . ( * Mpflt ) )
2015-02-27 20:44:45 +00:00
case CTINT , CTRUNE :
2015-05-27 00:47:05 -04: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-27 00:47:05 -04: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
}
2015-09-23 23:31:17 +02:00
if a == b {
return 0
}
if a < b {
return - 1
}
return + 1
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
}