mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
1029 lines
19 KiB
Go
1029 lines
19 KiB
Go
|
|
// 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) {
|
||
|
|
var c *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 {
|
||
|
|
var ct int
|
||
|
|
var n int
|
||
|
|
var n1 *Node
|
||
|
|
var n2 *Node
|
||
|
|
|
||
|
|
// 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 {
|
||
|
|
var l1 *Case
|
||
|
|
var l2 *Case
|
||
|
|
var le *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) {
|
||
|
|
var n *Node
|
||
|
|
var c *Node
|
||
|
|
var last *Node
|
||
|
|
var def *Node
|
||
|
|
var cas *NodeList
|
||
|
|
var stat *NodeList
|
||
|
|
var l *NodeList
|
||
|
|
var lc *NodeList
|
||
|
|
var go_ *Node
|
||
|
|
var br *Node
|
||
|
|
var lno int32
|
||
|
|
var needvar int32
|
||
|
|
|
||
|
|
if sw.List == nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
lno = setlineno(sw)
|
||
|
|
|
||
|
|
cas = nil // cases
|
||
|
|
stat = nil // statements
|
||
|
|
def = nil // defaults
|
||
|
|
br = Nod(OBREAK, nil, nil)
|
||
|
|
|
||
|
|
for l = sw.List; l != nil; l = l.Next {
|
||
|
|
n = l.N
|
||
|
|
setlineno(n)
|
||
|
|
if n.Op != OXCASE {
|
||
|
|
Fatal("casebody %v", Oconv(int(n.Op), 0))
|
||
|
|
}
|
||
|
|
n.Op = OCASE
|
||
|
|
needvar = int32(bool2int(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 != 0 && n.Nname != nil {
|
||
|
|
var l *NodeList
|
||
|
|
|
||
|
|
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 c *Case
|
||
|
|
var c1 *Case
|
||
|
|
var c2 *Case
|
||
|
|
var l *NodeList
|
||
|
|
var ord int
|
||
|
|
|
||
|
|
c = 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) != 0 {
|
||
|
|
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)
|
||
|
|
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 {
|
||
|
|
var cas *NodeList
|
||
|
|
var a *Node
|
||
|
|
var n *Node
|
||
|
|
var c *Case
|
||
|
|
var i int
|
||
|
|
var half int
|
||
|
|
var lno int
|
||
|
|
|
||
|
|
cas = nil
|
||
|
|
if ncase < Ncase {
|
||
|
|
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) {
|
||
|
|
var def *Node
|
||
|
|
var cas *NodeList
|
||
|
|
var a *Node
|
||
|
|
var c0 *Case
|
||
|
|
var c *Case
|
||
|
|
var c1 *Case
|
||
|
|
var t *Type
|
||
|
|
var arg int
|
||
|
|
var ncase int
|
||
|
|
|
||
|
|
casebody(sw, nil)
|
||
|
|
|
||
|
|
arg = Snorm
|
||
|
|
if Isconst(sw.Ntest, CTBOOL) != 0 {
|
||
|
|
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 = nil
|
||
|
|
if arg == Strue || arg == Sfalse {
|
||
|
|
exprname = Nodbool(bool2int(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)
|
||
|
|
if c0 != nil && c0.type_ == Tdefault {
|
||
|
|
def = c0.node.Right
|
||
|
|
c0 = c0.link
|
||
|
|
} else {
|
||
|
|
def = Nod(OBREAK, nil, nil)
|
||
|
|
}
|
||
|
|
|
||
|
|
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 init *NodeList
|
||
|
|
var a *Node
|
||
|
|
var b *Node
|
||
|
|
var var_ *Node
|
||
|
|
|
||
|
|
var_ = t.Nname
|
||
|
|
init = 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 {
|
||
|
|
var cas *NodeList
|
||
|
|
var a *Node
|
||
|
|
var n *Node
|
||
|
|
var c *Case
|
||
|
|
var i int
|
||
|
|
var half int
|
||
|
|
|
||
|
|
cas = nil
|
||
|
|
|
||
|
|
if ncase < Ncase {
|
||
|
|
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) {
|
||
|
|
var def *Node
|
||
|
|
var cas *NodeList
|
||
|
|
var hash *NodeList
|
||
|
|
var a *Node
|
||
|
|
var n *Node
|
||
|
|
var c *Case
|
||
|
|
var c0 *Case
|
||
|
|
var c1 *Case
|
||
|
|
var ncase int
|
||
|
|
var t *Type
|
||
|
|
var v Val
|
||
|
|
|
||
|
|
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) != 0) {
|
||
|
|
Yyerror("type switch must be on an interface")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
cas = 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) != 0 {
|
||
|
|
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)
|
||
|
|
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
|
||
|
|
*/
|
||
|
|
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
|
||
|
|
*/
|
||
|
|
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(1)
|
||
|
|
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 lno int
|
||
|
|
var ptr int
|
||
|
|
var nilonly string
|
||
|
|
var t *Type
|
||
|
|
var badtype *Type
|
||
|
|
var missing *Type
|
||
|
|
var have *Type
|
||
|
|
var l *NodeList
|
||
|
|
var ll *NodeList
|
||
|
|
var ncase *Node
|
||
|
|
var nvar *Node
|
||
|
|
var def *Node
|
||
|
|
|
||
|
|
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 {
|
||
|
|
if !(okforeq[t.Etype] != 0) {
|
||
|
|
Yyerror("cannot switch on %v", Nconv(n.Ntest, obj.FmtLong))
|
||
|
|
} else if t.Etype == TARRAY && !(Isfixedarray(t) != 0) {
|
||
|
|
nilonly = "slice"
|
||
|
|
} else if t.Etype == TARRAY && Isfixedarray(t) != 0 && 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 = nil
|
||
|
|
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) != 0) {
|
||
|
|
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) != 0 {
|
||
|
|
} 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) != 0) {
|
||
|
|
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) != 0) {
|
||
|
|
// 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)
|
||
|
|
}
|