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"
)
const (
Snorm = 0 + iota
Strue
Sfalse
Stype
Tdefault
Texprconst
Texprvar
Ttypenil
Ttypeconst
Ttypevar
Ncase = 4
)
type Case struct {
node * Node
hash uint32
type_ uint8
diag uint8
ordinal uint16
link * Case
}
var C * Case
func dumpcase ( c0 * Case ) {
2015-02-23 16:07:24 -05:00
for c := c0 ; c != nil ; c = c . link {
2015-02-13 14:40:36 -05:00
switch c . type_ {
case Tdefault :
fmt . Printf ( "case-default\n" )
fmt . Printf ( "\tord=%d\n" , c . ordinal )
case Texprconst :
fmt . Printf ( "case-exprconst\n" )
fmt . Printf ( "\tord=%d\n" , c . ordinal )
case Texprvar :
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 Ttypenil :
fmt . Printf ( "case-typenil\n" )
fmt . Printf ( "\tord=%d\n" , c . ordinal )
case Ttypeconst :
fmt . Printf ( "case-typeconst\n" )
fmt . Printf ( "\tord=%d\n" , c . ordinal )
fmt . Printf ( "\thash=%x\n" , c . hash )
case Ttypevar :
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" )
}
func ordlcmp ( c1 * Case , c2 * Case ) int {
// sort default first
if c1 . type_ == Tdefault {
return - 1
}
if c2 . type_ == Tdefault {
return + 1
}
// sort nil second
if c1 . type_ == Ttypenil {
return - 1
}
if c2 . type_ == Ttypenil {
return + 1
}
// sort by ordinal
if c1 . ordinal > c2 . ordinal {
return + 1
}
if c1 . ordinal < c2 . ordinal {
return - 1
}
return 0
}
func exprcmp ( c1 * Case , c2 * Case ) int {
// sort non-constants last
if c1 . type_ != Texprconst {
return + 1
}
if c2 . type_ != Texprconst {
return - 1
}
2015-02-23 16:07:24 -05:00
n1 := c1 . node . Left
n2 := c2 . node . Left
2015-02-13 14:40:36 -05:00
// sort by type (for switches on interface)
2015-02-23 16:07:24 -05:00
ct := int ( n1 . Val . Ctype )
2015-02-13 14:40:36 -05:00
if ct != int ( n2 . Val . Ctype ) {
return ct - int ( n2 . Val . Ctype )
}
if ! Eqtype ( n1 . Type , n2 . Type ) {
if n1 . Type . Vargen > n2 . Type . Vargen {
return + 1
} else {
return - 1
}
}
// sort by constant value
2015-02-23 16:07:24 -05:00
n := 0
2015-02-13 14:40:36 -05:00
switch ct {
case CTFLT :
n = mpcmpfltflt ( n1 . Val . U . Fval , n2 . Val . U . Fval )
case CTINT ,
CTRUNE :
n = Mpcmpfixfix ( n1 . Val . U . Xval , n2 . Val . U . Xval )
case CTSTR :
n = cmpslit ( n1 , n2 )
}
return n
}
func typecmp ( c1 * Case , c2 * Case ) int {
// sort non-constants last
if c1 . type_ != Ttypeconst {
return + 1
}
if c2 . type_ != Ttypeconst {
return - 1
}
// sort by hash code
if c1 . hash > c2 . hash {
return + 1
}
if c1 . hash < c2 . hash {
return - 1
}
// sort by ordinal so duplicate error
// happens on later case.
if c1 . ordinal > c2 . ordinal {
return + 1
}
if c1 . ordinal < c2 . ordinal {
return - 1
}
return 0
}
func csort ( l * Case , f func ( * Case , * Case ) int ) * Case {
if l == nil || l . link == nil {
return l
}
2015-02-23 16:07:24 -05:00
l1 := l
l2 := l
2015-02-13 14:40:36 -05:00
for {
l2 = l2 . link
if l2 == nil {
break
}
l2 = l2 . link
if l2 == nil {
break
}
l1 = l1 . link
}
l2 = l1 . link
l1 . link = nil
l1 = csort ( l , f )
l2 = csort ( l2 , f )
/* set up lead element */
if f ( l1 , l2 ) < 0 {
l = l1
l1 = l1 . link
} else {
l = l2
l2 = l2 . link
}
2015-02-23 16:07:24 -05:00
le := l
2015-02-13 14:40:36 -05:00
for {
if l1 == nil {
for l2 != nil {
le . link = l2
le = l2
l2 = l2 . link
}
le . link = nil
break
}
if l2 == nil {
for l1 != nil {
le . link = l1
le = l1
l1 = l1 . link
}
break
}
if f ( l1 , l2 ) < 0 {
le . link = l1
le = l1
l1 = l1 . link
} else {
le . link = l2
le = l2
l2 = l2 . link
}
}
le . link = nil
return l
}
var newlabel_swt_label int
func newlabel_swt ( ) * Node {
newlabel_swt_label ++
namebuf = fmt . Sprintf ( "%.6d" , newlabel_swt_label )
return newname ( Lookup ( namebuf ) )
}
/ *
* build separate list of statements and cases
* make labels between cases and statements
* deal with fallthrough , break , unreachable statements
* /
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-23 16:07:24 -05:00
cas := ( * NodeList ) ( nil ) // cases
stat := ( * NodeList ) ( nil ) // statements
def := ( * Node ) ( nil ) // defaults
br := Nod ( OBREAK , nil , nil )
2015-02-13 14:40:36 -05:00
2015-02-23 16:07:24 -05:00
var c * Node
var go_ * Node
var needvar bool
var lc * NodeList
var last * Node
var n * Node
for l := sw . List ; l != nil ; l = l . Next {
2015-02-13 14:40:36 -05:00
n = l . N
setlineno ( n )
if n . Op != OXCASE {
Fatal ( "casebody %v" , Oconv ( int ( n . Op ) , 0 ) )
}
n . Op = OCASE
2015-02-17 22:13:49 -05:00
needvar = count ( n . List ) != 1 || n . List . N . Op == OLITERAL
2015-02-13 14:40:36 -05:00
go_ = Nod ( OGOTO , newlabel_swt ( ) , nil )
if n . List == nil {
if def != nil {
Yyerror ( "more than one default case" )
}
// reuse original default case
n . Right = go_
def = n
}
if n . List != nil && n . List . Next == nil {
// one case - reuse OCASE node.
c = n . List . N
n . Left = c
n . Right = go_
n . List = nil
cas = list ( cas , n )
} else {
// expand multi-valued cases
for lc = n . List ; lc != nil ; lc = lc . Next {
c = lc . N
cas = list ( cas , Nod ( OCASE , c , go_ ) )
}
}
stat = list ( stat , Nod ( OLABEL , go_ . 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
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
}
func mkcaselist ( sw * Node , arg int ) * Case {
var n * Node
var c1 * Case
2015-02-23 16:07:24 -05:00
c := ( * Case ) ( nil )
ord := 0
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-13 14:40:36 -05:00
n = l . N
c1 = new ( Case )
c1 . link = c
c = c1
ord ++
if int ( uint16 ( ord ) ) != ord {
Fatal ( "too many cases in switch" )
}
c . ordinal = uint16 ( ord )
c . node = n
if n . Left == nil {
c . type_ = Tdefault
continue
}
switch arg {
case Stype :
c . hash = 0
if n . Left . Op == OLITERAL {
c . type_ = Ttypenil
continue
}
2015-02-17 22:13:49 -05:00
if Istype ( n . Left . Type , TINTER ) {
2015-02-13 14:40:36 -05:00
c . type_ = Ttypevar
continue
}
c . hash = typehash ( n . Left . Type )
c . type_ = Ttypeconst
continue
case Snorm ,
Strue ,
Sfalse :
c . type_ = Texprvar
c . hash = typehash ( n . Left . Type )
switch consttype ( n . Left ) {
case CTFLT ,
CTINT ,
CTRUNE ,
CTSTR :
c . type_ = Texprconst
}
continue
}
}
if c == nil {
return nil
}
// sort by value and diagnose duplicate cases
switch arg {
case Stype :
c = csort ( c , typecmp )
2015-02-23 16:07:24 -05:00
var c2 * Case
for c1 := c ; c1 != nil ; c1 = c1 . link {
2015-02-13 14:40:36 -05:00
for c2 = c1 . link ; c2 != nil && c2 . hash == c1 . hash ; c2 = c2 . link {
if c1 . type_ == Ttypenil || c1 . type_ == Tdefault {
break
}
if c2 . type_ == Ttypenil || c2 . type_ == Tdefault {
break
}
if ! Eqtype ( c1 . node . Left . Type , c2 . node . Left . Type ) {
continue
}
yyerrorl ( int ( c2 . node . Lineno ) , "duplicate case %v in type switch\n\tprevious case at %v" , Tconv ( c2 . node . Left . Type , 0 ) , c1 . node . Line ( ) )
}
}
case Snorm ,
Strue ,
Sfalse :
c = csort ( c , exprcmp )
2015-02-23 16:07:24 -05:00
for c1 := c ; c1 . link != nil ; c1 = c1 . link {
2015-02-13 14:40:36 -05:00
if exprcmp ( c1 , c1 . link ) != 0 {
continue
}
setlineno ( c1 . link . node )
Yyerror ( "duplicate case %v in switch\n\tprevious case at %v" , Nconv ( c1 . node . Left , 0 ) , c1 . node . Line ( ) )
}
}
// put list back in processing order
c = csort ( c , ordlcmp )
return c
}
var exprname * Node
func exprbsw ( c0 * Case , ncase int , arg int ) * Node {
2015-02-23 16:07:24 -05:00
cas := ( * NodeList ) ( nil )
2015-02-13 14:40:36 -05:00
if ncase < Ncase {
2015-02-23 16:07:24 -05:00
var a * Node
var n * Node
var lno int
for i := 0 ; i < ncase ; i ++ {
2015-02-13 14:40:36 -05:00
n = c0 . node
lno = int ( setlineno ( n ) )
if ( arg != Strue && arg != Sfalse ) || assignop ( n . Left . Type , exprname . Type , nil ) == OCONVIFACE || assignop ( exprname . Type , n . Left . Type , nil ) == OCONVIFACE {
a = Nod ( OIF , nil , nil )
a . Ntest = Nod ( OEQ , exprname , n . Left ) // if name == val
typecheck ( & a . Ntest , Erv )
a . Nbody = list1 ( n . Right ) // then goto l
} else if arg == Strue {
a = Nod ( OIF , nil , nil )
a . Ntest = n . Left // if val
a . Nbody = list1 ( n . Right ) // then goto l // arg == Sfalse
} else {
a = Nod ( OIF , nil , nil )
a . Ntest = Nod ( ONOT , n . Left , nil ) // if !val
typecheck ( & a . Ntest , Erv )
a . Nbody = list1 ( n . Right ) // then goto l
}
cas = list ( cas , a )
c0 = c0 . link
lineno = int32 ( lno )
}
return liststmt ( cas )
}
// find the middle and recur
2015-02-23 16:07:24 -05:00
c := c0
2015-02-13 14:40:36 -05:00
2015-02-23 16:07:24 -05:00
half := ncase >> 1
for i := 1 ; i < half ; i ++ {
2015-02-13 14:40:36 -05:00
c = c . link
}
2015-02-23 16:07:24 -05:00
a := Nod ( OIF , nil , nil )
2015-02-13 14:40:36 -05:00
a . Ntest = Nod ( OLE , exprname , c . node . Left )
typecheck ( & a . Ntest , Erv )
a . Nbody = list1 ( exprbsw ( c0 , half , arg ) )
a . Nelse = list1 ( exprbsw ( c . link , ncase - half , arg ) )
return a
}
/ *
* normal ( expression ) switch .
* rebuild case statements into if . . goto
* /
func exprswitch ( sw * Node ) {
casebody ( sw , nil )
2015-02-23 16:07:24 -05:00
arg := Snorm
2015-02-17 22:13:49 -05:00
if Isconst ( sw . Ntest , CTBOOL ) {
2015-02-13 14:40:36 -05:00
arg = Strue
if sw . Ntest . Val . U . Bval == 0 {
arg = Sfalse
}
}
walkexpr ( & sw . Ntest , & sw . Ninit )
2015-02-23 16:07:24 -05:00
t := sw . Type
2015-02-13 14:40:36 -05:00
if t == nil {
return
}
/ *
* convert the switch into OIF statements
* /
exprname = nil
2015-02-23 16:07:24 -05:00
cas := ( * NodeList ) ( nil )
2015-02-13 14:40:36 -05:00
if arg == Strue || arg == Sfalse {
2015-02-17 22:13:49 -05:00
exprname = Nodbool ( arg == Strue )
2015-02-13 14:40:36 -05:00
} else if consttype ( sw . Ntest ) >= 0 {
// leave constants to enable dead code elimination (issue 9608)
exprname = sw . Ntest
} else {
exprname = temp ( sw . Ntest . Type )
cas = list1 ( Nod ( OAS , exprname , sw . Ntest ) )
typechecklist ( cas , Etop )
}
2015-02-23 16:07:24 -05:00
c0 := mkcaselist ( sw , arg )
var def * Node
2015-02-13 14:40:36 -05:00
if c0 != nil && c0 . type_ == Tdefault {
def = c0 . node . Right
c0 = c0 . link
} else {
def = Nod ( OBREAK , nil , nil )
}
2015-02-23 16:07:24 -05:00
var c * Case
var a * Node
var ncase int
var c1 * Case
2015-02-13 14:40:36 -05:00
loop :
if c0 == nil {
cas = list ( cas , def )
sw . Nbody = concat ( cas , sw . Nbody )
sw . List = nil
walkstmtlist ( sw . Nbody )
return
}
// deal with the variables one-at-a-time
2015-02-17 22:13:49 -05:00
if okforcmp [ t . Etype ] == 0 || c0 . type_ != Texprconst {
2015-02-13 14:40:36 -05:00
a = exprbsw ( c0 , 1 , arg )
cas = list ( cas , a )
c0 = c0 . link
goto loop
}
// do binary search on run of constants
ncase = 1
for c = c0 ; c . link != nil ; c = c . link {
if c . link . type_ != Texprconst {
break
}
ncase ++
}
// break the chain at the count
c1 = c . link
c . link = nil
// sort and compile constants
c0 = csort ( c0 , exprcmp )
a = exprbsw ( c0 , ncase , arg )
cas = list ( cas , a )
c0 = c1
goto loop
}
var hashname * Node
var facename * Node
var boolname * Node
func typeone ( t * Node ) * Node {
2015-02-23 16:07:24 -05:00
var_ := t . Nname
init := ( * NodeList ) ( nil )
2015-02-13 14:40:36 -05:00
if var_ == nil {
typecheck ( & nblank , Erv | Easgn )
var_ = nblank
} else {
init = list1 ( Nod ( ODCL , var_ , nil ) )
}
2015-02-23 16:07:24 -05:00
a := Nod ( OAS2 , nil , nil )
2015-02-13 14:40:36 -05:00
a . List = list ( list1 ( var_ ) , boolname ) // var,bool =
2015-02-23 16:07:24 -05:00
b := Nod ( ODOTTYPE , facename , nil )
2015-02-13 14:40:36 -05:00
b . Type = t . Left . Type // interface.(type)
a . Rlist = list1 ( b )
typecheck ( & a , Etop )
init = list ( init , a )
b = Nod ( OIF , nil , nil )
b . Ntest = boolname
b . Nbody = list1 ( t . Right ) // if bool { goto l }
a = liststmt ( list ( init , b ) )
return a
}
func typebsw ( c0 * Case , ncase int ) * Node {
2015-02-23 16:07:24 -05:00
cas := ( * NodeList ) ( nil )
2015-02-13 14:40:36 -05:00
if ncase < Ncase {
2015-02-23 16:07:24 -05:00
var n * Node
var a * Node
for i := 0 ; i < ncase ; i ++ {
2015-02-13 14:40:36 -05:00
n = c0 . node
if c0 . type_ != Ttypeconst {
Fatal ( "typebsw" )
}
a = Nod ( OIF , nil , nil )
a . Ntest = Nod ( OEQ , hashname , Nodintconst ( int64 ( c0 . hash ) ) )
typecheck ( & a . Ntest , Erv )
a . Nbody = list1 ( n . Right )
cas = list ( cas , a )
c0 = c0 . link
}
return liststmt ( cas )
}
// find the middle and recur
2015-02-23 16:07:24 -05:00
c := c0
2015-02-13 14:40:36 -05:00
2015-02-23 16:07:24 -05:00
half := ncase >> 1
for i := 1 ; i < half ; i ++ {
2015-02-13 14:40:36 -05:00
c = c . link
}
2015-02-23 16:07:24 -05:00
a := Nod ( OIF , nil , nil )
2015-02-13 14:40:36 -05:00
a . Ntest = Nod ( OLE , hashname , Nodintconst ( int64 ( c . hash ) ) )
typecheck ( & a . Ntest , Erv )
a . Nbody = list1 ( typebsw ( c0 , half ) )
a . Nelse = list1 ( typebsw ( c . link , ncase - half ) )
return a
}
/ *
* convert switch of the form
* switch v := i . ( type ) { case t1 : . . ; case t2 : . . ; }
* into if statements
* /
func typeswitch ( sw * Node ) {
if sw . Ntest == nil {
return
}
if sw . Ntest . Right == nil {
setlineno ( sw )
Yyerror ( "type switch must have an assignment" )
return
}
walkexpr ( & sw . Ntest . Right , & sw . Ninit )
2015-02-17 22:13:49 -05:00
if ! Istype ( sw . Ntest . Right . Type , TINTER ) {
2015-02-13 14:40:36 -05:00
Yyerror ( "type switch must be on an interface" )
return
}
2015-02-23 16:07:24 -05:00
cas := ( * NodeList ) ( nil )
2015-02-13 14:40:36 -05:00
/ *
* predeclare temporary variables
* and the boolean var
* /
facename = temp ( sw . Ntest . Right . Type )
2015-02-23 16:07:24 -05:00
a := Nod ( OAS , facename , sw . Ntest . Right )
2015-02-13 14:40:36 -05:00
typecheck ( & a , Etop )
cas = list ( cas , a )
casebody ( sw , facename )
boolname = temp ( Types [ TBOOL ] )
typecheck ( & boolname , Erv )
hashname = temp ( Types [ TUINT32 ] )
typecheck ( & hashname , Erv )
2015-02-23 16:07:24 -05:00
t := sw . Ntest . 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 )
}
argtype ( a , t )
a = Nod ( OCALL , a , nil )
a . List = list1 ( facename )
a = Nod ( OAS , hashname , a )
typecheck ( & a , Etop )
cas = list ( cas , a )
2015-02-23 16:07:24 -05:00
c0 := mkcaselist ( sw , Stype )
var def * Node
2015-02-13 14:40:36 -05:00
if c0 != nil && c0 . type_ == Tdefault {
def = c0 . node . Right
c0 = c0 . link
} else {
def = Nod ( OBREAK , nil , nil )
}
/ *
* insert if statement into each case block
* /
2015-02-23 16:07:24 -05:00
var v Val
var n * Node
for c := c0 ; c != nil ; c = c . link {
2015-02-13 14:40:36 -05:00
n = c . node
switch c . type_ {
case Ttypenil :
v . Ctype = CTNIL
a = Nod ( OIF , nil , nil )
a . Ntest = Nod ( OEQ , facename , nodlit ( v ) )
typecheck ( & a . Ntest , Erv )
a . Nbody = list1 ( n . Right ) // if i==nil { goto l }
n . Right = a
case Ttypevar ,
Ttypeconst :
n . Right = typeone ( n )
}
}
/ *
* generate list of if statements , binary search for constant sequences
* /
2015-02-23 16:07:24 -05:00
var ncase int
var c1 * Case
var hash * NodeList
var c * Case
2015-02-13 14:40:36 -05:00
for c0 != nil {
if c0 . type_ != Ttypeconst {
n = c0 . node
cas = list ( cas , n . Right )
c0 = c0 . link
continue
}
// identify run of constants
c = c0
c1 = c
for c . link != nil && c . link . type_ == Ttypeconst {
c = c . link
}
c0 = c . link
c . link = nil
// sort by hash
c1 = csort ( c1 , typecmp )
// for debugging: linear search
if false {
for c = c1 ; c != nil ; c = c . link {
n = c . node
cas = list ( cas , n . Right )
}
continue
}
// combine adjacent cases with the same hash
ncase = 0
for c = c1 ; c != nil ; c = c . link {
ncase ++
hash = list1 ( c . node . Right )
for c . link != nil && c . link . hash == c . hash {
hash = list ( hash , c . link . node . Right )
c . link = c . link . link
}
c . node . Right = liststmt ( hash )
}
// binary search among cases to narrow by hash
cas = list ( cas , typebsw ( c1 , ncase ) )
}
if nerrors == 0 {
cas = list ( cas , def )
sw . Nbody = concat ( cas , sw . Nbody )
sw . List = nil
walkstmtlist ( sw . Nbody )
}
}
func walkswitch ( sw * Node ) {
/ *
* reorder the body into ( OLIST , cases , statements )
* cases have OGOTO into statements .
* both have inserted OBREAK statements
* /
if sw . Ntest == nil {
2015-02-17 22:13:49 -05:00
sw . Ntest = Nodbool ( true )
2015-02-13 14:40:36 -05:00
typecheck ( & sw . Ntest , Erv )
}
if sw . Ntest . Op == OTYPESW {
typeswitch ( sw )
//dump("sw", sw);
return
}
exprswitch ( sw )
// Discard old AST elements after a walk. They can confuse racewealk.
sw . Ntest = nil
sw . List = nil
}
/ *
* type check switch statement
* /
func typecheckswitch ( n * Node ) {
var top int
var t * Type
2015-02-23 16:07:24 -05:00
lno := int ( lineno )
2015-02-13 14:40:36 -05:00
typechecklist ( n . Ninit , Etop )
2015-02-23 16:07:24 -05:00
nilonly := ""
2015-02-13 14:40:36 -05:00
if n . Ntest != nil && n . Ntest . Op == OTYPESW {
// type switch
top = Etype
typecheck ( & n . Ntest . Right , Erv )
t = n . Ntest . Right . Type
if t != nil && t . Etype != TINTER {
Yyerror ( "cannot type switch on non-interface value %v" , Nconv ( n . Ntest . Right , obj . FmtLong ) )
}
} else {
// value switch
top = Erv
if n . Ntest != nil {
typecheck ( & n . Ntest , Erv )
defaultlit ( & n . Ntest , nil )
t = n . Ntest . Type
} else {
t = Types [ TBOOL ]
}
if t != nil {
2015-02-23 16:07:24 -05:00
var badtype * Type
2015-02-17 22:13:49 -05:00
if okforeq [ t . Etype ] == 0 {
2015-02-13 14:40:36 -05:00
Yyerror ( "cannot switch on %v" , Nconv ( n . Ntest , obj . FmtLong ) )
2015-02-17 22:13:49 -05:00
} else if t . Etype == TARRAY && ! Isfixedarray ( t ) {
2015-02-13 14:40:36 -05:00
nilonly = "slice"
2015-02-17 22:13:49 -05:00
} else if t . Etype == TARRAY && Isfixedarray ( t ) && algtype1 ( t , nil ) == ANOEQ {
2015-02-13 14:40:36 -05:00
Yyerror ( "cannot switch on %v" , Nconv ( n . Ntest , obj . FmtLong ) )
} else if t . Etype == TSTRUCT && algtype1 ( t , & badtype ) == ANOEQ {
Yyerror ( "cannot switch on %v (struct containing %v cannot be compared)" , Nconv ( n . Ntest , obj . FmtLong ) , Tconv ( badtype , 0 ) )
} else if t . Etype == TFUNC {
nilonly = "func"
} else if t . Etype == TMAP {
nilonly = "map"
}
}
}
n . Type = t
2015-02-23 16:07:24 -05:00
def := ( * Node ) ( nil )
var ptr int
var have * Type
var nvar * Node
var ll * NodeList
var missing * Type
var ncase * Node
for l := n . List ; l != nil ; l = l . Next {
2015-02-13 14:40:36 -05:00
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 {
case Erv : // expression switch
defaultlit ( & ll . N , t )
if ll . N . Op == OTYPE {
Yyerror ( "type %v is not an expression" , Tconv ( ll . N . Type , 0 ) )
2015-02-17 22:13:49 -05:00
} else if ll . N . Type != nil && assignop ( ll . N . Type , t , nil ) == 0 && assignop ( t , ll . N . Type , nil ) == 0 {
2015-02-13 14:40:36 -05:00
if n . Ntest != nil {
Yyerror ( "invalid case %v in switch on %v (mismatched types %v and %v)" , Nconv ( ll . N , 0 ) , Nconv ( n . Ntest , 0 ) , Tconv ( ll . N . Type , 0 ) , Tconv ( t , 0 ) )
} else {
Yyerror ( "invalid case %v in switch (mismatched types %v and bool)" , Nconv ( ll . N , 0 ) , Tconv ( ll . N . Type , 0 ) )
}
2015-02-17 22:13:49 -05:00
} else if nilonly != "" && ! Isconst ( ll . N , CTNIL ) {
2015-02-13 14:40:36 -05:00
Yyerror ( "invalid case %v in switch (can only compare %s %v to nil)" , Nconv ( ll . N , 0 ) , nilonly , Nconv ( n . Ntest , 0 ) )
}
case Etype : // type switch
2015-02-17 22:13:49 -05:00
if ll . N . Op == OLITERAL && Istype ( ll . N . Type , TNIL ) {
2015-02-13 14:40:36 -05:00
} else if 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 . Ntest . Right
2015-02-17 22:13:49 -05:00
} else if 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-02-13 14:40:36 -05: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 . Ntest . Right , obj . FmtLong ) , Tconv ( ll . N . Type , 0 ) , Sconv ( missing . Sym , 0 ) , Sconv ( have . Sym , 0 ) , Tconv ( have . Type , obj . FmtShort ) , Sconv ( missing . Sym , 0 ) , Tconv ( missing . Type , obj . FmtShort ) )
2015-02-17 22:13:49 -05:00
} else if missing . Broke == 0 {
2015-02-13 14:40:36 -05:00
Yyerror ( "impossible type switch case: %v cannot have dynamic type %v" + " (missing %v method)" , Nconv ( n . Ntest . Right , obj . FmtLong ) , Tconv ( ll . N . Type , 0 ) , Sconv ( missing . Sym , 0 ) )
}
}
}
}
}
if top == Etype && n . Type != nil {
ll = ncase . List
nvar = ncase . Nname
if nvar != nil {
2015-02-17 22:13:49 -05:00
if ll != nil && ll . Next == nil && ll . N . Type != nil && ! Istype ( ll . N . Type , TNIL ) {
2015-02-13 14:40:36 -05:00
// single entry type switch
nvar . Ntype = typenod ( ll . N . Type )
} else {
// multiple entry type switch or default
nvar . Ntype = typenod ( n . Type )
}
typecheck ( & nvar , Erv | Easgn )
ncase . Nname = nvar
}
}
typechecklist ( ncase . Nbody , Etop )
}
lineno = int32 ( lno )
}