blob: 81eb56c3a6e0594a018c3769d12ad1e9b7527f1a [file] [log] [blame]
// 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) {
for c := c0; c != nil; c = c.link {
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
}
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 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
n := 0
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
}
l1 := l
l2 := l
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
}
le := l
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
}
lno := setlineno(sw)
cas := (*NodeList)(nil) // cases
stat := (*NodeList)(nil) // statements
def := (*Node)(nil) // defaults
br := Nod(OBREAK, nil, nil)
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 {
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
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))
if typeswvar != nil && needvar && n.Nname != nil {
l := list1(Nod(ODCL, n.Nname, nil))
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
c := (*Case)(nil)
ord := 0
for l := sw.List; l != nil; l = l.Next {
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
}
if Istype(n.Left.Type, TINTER) {
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)
var c2 *Case
for c1 := c; c1 != nil; c1 = c1.link {
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)
for c1 := c; c1.link != nil; c1 = c1.link {
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 {
cas := (*NodeList)(nil)
if ncase < Ncase {
var a *Node
var n *Node
var lno int
for i := 0; i < ncase; i++ {
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
c := c0
half := ncase >> 1
for i := 1; i < half; i++ {
c = c.link
}
a := Nod(OIF, nil, nil)
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)
arg := Snorm
if Isconst(sw.Ntest, CTBOOL) {
arg = Strue
if sw.Ntest.Val.U.Bval == 0 {
arg = Sfalse
}
}
walkexpr(&sw.Ntest, &sw.Ninit)
t := sw.Type
if t == nil {
return
}
/*
* convert the switch into OIF statements
*/
exprname = nil
cas := (*NodeList)(nil)
if arg == Strue || arg == Sfalse {
exprname = Nodbool(arg == Strue)
} 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)
}
c0 := mkcaselist(sw, arg)
var def *Node
if c0 != nil && c0.type_ == Tdefault {
def = c0.node.Right
c0 = c0.link
} else {
def = Nod(OBREAK, nil, nil)
}
var c *Case
var a *Node
var ncase int
var c1 *Case
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
if okforcmp[t.Etype] == 0 || c0.type_ != Texprconst {
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 {
var_ := t.Nname
init := (*NodeList)(nil)
if var_ == nil {
typecheck(&nblank, Erv|Easgn)
var_ = nblank
} else {
init = list1(Nod(ODCL, var_, nil))
}
a := Nod(OAS2, nil, nil)
a.List = list(list1(var_), boolname) // var,bool =
b := Nod(ODOTTYPE, facename, nil)
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 {
cas := (*NodeList)(nil)
if ncase < Ncase {
var n *Node
var a *Node
for i := 0; i < ncase; i++ {
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
c := c0
half := ncase >> 1
for i := 1; i < half; i++ {
c = c.link
}
a := Nod(OIF, nil, nil)
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)
if !Istype(sw.Ntest.Right.Type, TINTER) {
Yyerror("type switch must be on an interface")
return
}
cas := (*NodeList)(nil)
/*
* predeclare temporary variables
* and the boolean var
*/
facename = temp(sw.Ntest.Right.Type)
a := Nod(OAS, facename, sw.Ntest.Right)
typecheck(&a, Etop)
cas = list(cas, a)
casebody(sw, facename)
boolname = temp(Types[TBOOL])
typecheck(&boolname, Erv)
hashname = temp(Types[TUINT32])
typecheck(&hashname, Erv)
t := sw.Ntest.Right.Type
if isnilinter(t) {
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)
c0 := mkcaselist(sw, Stype)
var def *Node
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
*/
var v Val
var n *Node
for c := c0; c != nil; c = c.link {
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
*/
var ncase int
var c1 *Case
var hash *NodeList
var c *Case
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 {
sw.Ntest = Nodbool(true)
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
lno := int(lineno)
typechecklist(n.Ninit, Etop)
nilonly := ""
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 {
var badtype *Type
if okforeq[t.Etype] == 0 {
Yyerror("cannot switch on %v", Nconv(n.Ntest, obj.FmtLong))
} else if t.Etype == TARRAY && !Isfixedarray(t) {
nilonly = "slice"
} else if t.Etype == TARRAY && Isfixedarray(t) && algtype1(t, nil) == ANOEQ {
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
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 {
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))
} else if ll.N.Type != nil && assignop(ll.N.Type, t, nil) == 0 && assignop(t, ll.N.Type, nil) == 0 {
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))
}
} else if nilonly != "" && !Isconst(ll.N, CTNIL) {
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
if ll.N.Op == OLITERAL && Istype(ll.N.Type, TNIL) {
} 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
} 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 {
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))
} else if missing.Broke == 0 {
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 {
if ll != nil && ll.Next == nil && ll.N.Type != nil && !Istype(ll.N.Type, TNIL) {
// 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)
}