mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
$ sizeof -p cmd/compile/internal/gc Node Node 272 $ Change-Id: I3d9b67eebfc0be0a4b9768d3de3dc76300abd89c Reviewed-on: https://go-review.googlesource.com/10521 Reviewed-by: Ian Lance Taylor <iant@golang.org> Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
845 lines
20 KiB
Go
845 lines
20 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"
|
|
"sort"
|
|
"strconv"
|
|
)
|
|
|
|
const (
|
|
// expression switch
|
|
switchKindExpr = iota // switch a {...} or switch 5 {...}
|
|
switchKindTrue // switch true {...} or switch {...}
|
|
switchKindFalse // switch false {...}
|
|
|
|
// type switch
|
|
switchKindType // switch a.(type) {...}
|
|
)
|
|
|
|
const (
|
|
caseKindDefault = iota // default:
|
|
|
|
// expression switch
|
|
caseKindExprConst // case 5:
|
|
caseKindExprVar // case x:
|
|
|
|
// type switch
|
|
caseKindTypeNil // case nil:
|
|
caseKindTypeConst // case time.Time: (concrete type, has type hash)
|
|
caseKindTypeVar // case io.Reader: (interface type)
|
|
)
|
|
|
|
const binarySearchMin = 4 // minimum number of cases for binary search
|
|
|
|
// 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*)
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// typecheckswitch typechecks a switch statement.
|
|
func typecheckswitch(n *Node) {
|
|
lno := int(lineno)
|
|
typechecklist(n.Ninit, Etop)
|
|
|
|
var nilonly string
|
|
var top int
|
|
var t *Type
|
|
|
|
if n.Left != nil && n.Left.Op == OTYPESW {
|
|
// type switch
|
|
top = Etype
|
|
typecheck(&n.Left.Right, Erv)
|
|
t = n.Left.Right.Type
|
|
if t != nil && t.Etype != TINTER {
|
|
Yyerror("cannot type switch on non-interface value %v", Nconv(n.Left.Right, obj.FmtLong))
|
|
}
|
|
} else {
|
|
// expression switch
|
|
top = Erv
|
|
if n.Left != nil {
|
|
typecheck(&n.Left, Erv)
|
|
defaultlit(&n.Left, nil)
|
|
t = n.Left.Type
|
|
} else {
|
|
t = Types[TBOOL]
|
|
}
|
|
if t != nil {
|
|
var badtype *Type
|
|
switch {
|
|
case !okforeq[t.Etype]:
|
|
Yyerror("cannot switch on %v", Nconv(n.Left, obj.FmtLong))
|
|
case t.Etype == TARRAY && !Isfixedarray(t):
|
|
nilonly = "slice"
|
|
case t.Etype == TARRAY && Isfixedarray(t) && algtype1(t, nil) == ANOEQ:
|
|
Yyerror("cannot switch on %v", Nconv(n.Left, obj.FmtLong))
|
|
case t.Etype == TSTRUCT && algtype1(t, &badtype) == ANOEQ:
|
|
Yyerror("cannot switch on %v (struct containing %v cannot be compared)", Nconv(n.Left, obj.FmtLong), badtype)
|
|
case t.Etype == TFUNC:
|
|
nilonly = "func"
|
|
case t.Etype == TMAP:
|
|
nilonly = "map"
|
|
}
|
|
}
|
|
}
|
|
|
|
n.Type = t
|
|
|
|
var def *Node
|
|
var ll *NodeList
|
|
for l := n.List; l != nil; l = l.Next {
|
|
ncase := l.N
|
|
setlineno(n)
|
|
if ncase.List == nil {
|
|
// default
|
|
if def != nil {
|
|
Yyerror("multiple defaults in switch (first at %v)", def.Line())
|
|
} else {
|
|
def = ncase
|
|
}
|
|
} else {
|
|
for ll = ncase.List; ll != nil; ll = ll.Next {
|
|
setlineno(ll.N)
|
|
typecheck(&ll.N, Erv|Etype)
|
|
if ll.N.Type == nil || t == nil {
|
|
continue
|
|
}
|
|
setlineno(ncase)
|
|
switch top {
|
|
// expression switch
|
|
case Erv:
|
|
defaultlit(&ll.N, t)
|
|
switch {
|
|
case ll.N.Op == OTYPE:
|
|
Yyerror("type %v is not an expression", ll.N.Type)
|
|
case ll.N.Type != nil && assignop(ll.N.Type, t, nil) == 0 && assignop(t, ll.N.Type, nil) == 0:
|
|
if n.Left != nil {
|
|
Yyerror("invalid case %v in switch on %v (mismatched types %v and %v)", ll.N, n.Left, ll.N.Type, t)
|
|
} else {
|
|
Yyerror("invalid case %v in switch (mismatched types %v and bool)", ll.N, ll.N.Type)
|
|
}
|
|
case nilonly != "" && !Isconst(ll.N, CTNIL):
|
|
Yyerror("invalid case %v in switch (can only compare %s %v to nil)", ll.N, nilonly, n.Left)
|
|
}
|
|
|
|
// type switch
|
|
case Etype:
|
|
var missing, have *Type
|
|
var ptr int
|
|
switch {
|
|
case ll.N.Op == OLITERAL && Istype(ll.N.Type, TNIL):
|
|
case ll.N.Op != OTYPE && ll.N.Type != nil: // should this be ||?
|
|
Yyerror("%v is not a type", Nconv(ll.N, obj.FmtLong))
|
|
// reset to original type
|
|
ll.N = n.Left.Right
|
|
case ll.N.Type.Etype != TINTER && t.Etype == TINTER && !implements(ll.N.Type, t, &missing, &have, &ptr):
|
|
if have != nil && missing.Broke == 0 && have.Broke == 0 {
|
|
Yyerror("impossible type switch case: %v cannot have dynamic type %v"+" (wrong type for %v method)\n\thave %v%v\n\twant %v%v", Nconv(n.Left.Right, obj.FmtLong), ll.N.Type, missing.Sym, have.Sym, Tconv(have.Type, obj.FmtShort), missing.Sym, Tconv(missing.Type, obj.FmtShort))
|
|
} else if missing.Broke == 0 {
|
|
Yyerror("impossible type switch case: %v cannot have dynamic type %v"+" (missing %v method)", Nconv(n.Left.Right, obj.FmtLong), ll.N.Type, missing.Sym)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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.Param.Ntype = typenod(ll.N.Type)
|
|
} else {
|
|
// multiple entry type switch or default
|
|
nvar.Param.Ntype = typenod(n.Type)
|
|
}
|
|
|
|
typecheck(&nvar, Erv|Easgn)
|
|
ncase.Nname = nvar
|
|
}
|
|
}
|
|
|
|
typechecklist(ncase.Nbody, Etop)
|
|
}
|
|
|
|
lineno = int32(lno)
|
|
}
|
|
|
|
// walkswitch walks a switch statement.
|
|
func walkswitch(sw *Node) {
|
|
// convert switch {...} to switch true {...}
|
|
if sw.Left == nil {
|
|
sw.Left = Nodbool(true)
|
|
typecheck(&sw.Left, Erv)
|
|
}
|
|
|
|
if sw.Left.Op == OTYPESW {
|
|
var s typeSwitch
|
|
s.walk(sw)
|
|
} else {
|
|
var s exprSwitch
|
|
s.walk(sw)
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
|
|
cond := sw.Left
|
|
sw.Left = nil
|
|
|
|
s.kind = switchKindExpr
|
|
if Isconst(cond, CTBOOL) {
|
|
s.kind = switchKindTrue
|
|
if !cond.Val.U.(bool) {
|
|
s.kind = switchKindFalse
|
|
}
|
|
}
|
|
|
|
walkexpr(&cond, &sw.Ninit)
|
|
t := sw.Type
|
|
if t == nil {
|
|
return
|
|
}
|
|
|
|
// convert the switch into OIF statements
|
|
var cas *NodeList
|
|
if s.kind == switchKindTrue || s.kind == switchKindFalse {
|
|
s.exprname = Nodbool(s.kind == switchKindTrue)
|
|
} else if consttype(cond) >= 0 {
|
|
// leave constants to enable dead code elimination (issue 9608)
|
|
s.exprname = cond
|
|
} else {
|
|
s.exprname = temp(cond.Type)
|
|
cas = list1(Nod(OAS, s.exprname, cond))
|
|
typechecklist(cas, Etop)
|
|
}
|
|
|
|
// enumerate the cases, and lop off the default case
|
|
cc := caseClauses(sw, s.kind)
|
|
sw.List = nil
|
|
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)
|
|
}
|
|
|
|
// handle the cases in order
|
|
for len(cc) > 0 {
|
|
// deal with expressions one at a time
|
|
if !okforcmp[t.Etype] || cc[0].typ != caseKindExprConst {
|
|
a := s.walkCases(cc[:1])
|
|
cas = list(cas, a)
|
|
cc = cc[1:]
|
|
continue
|
|
}
|
|
|
|
// do binary search on runs of constants
|
|
var run int
|
|
for run = 1; run < len(cc) && cc[run].typ == caseKindExprConst; run++ {
|
|
}
|
|
|
|
// sort and compile constants
|
|
sort.Sort(caseClauseByExpr(cc[:run]))
|
|
a := s.walkCases(cc[:run])
|
|
cas = list(cas, a)
|
|
cc = cc[run:]
|
|
}
|
|
|
|
// handle default case
|
|
if nerrors == 0 {
|
|
cas = list(cas, def)
|
|
sw.Nbody = concat(cas, sw.Nbody)
|
|
walkstmtlist(sw.Nbody)
|
|
}
|
|
|
|
}
|
|
|
|
// walkCases generates an AST implementing the cases in cc.
|
|
func (s *exprSwitch) walkCases(cc []*caseClause) *Node {
|
|
if len(cc) < binarySearchMin {
|
|
// linear search
|
|
var cas *NodeList
|
|
for _, c := range cc {
|
|
n := c.node
|
|
lno := int(setlineno(n))
|
|
|
|
a := Nod(OIF, nil, nil)
|
|
if (s.kind != switchKindTrue && s.kind != switchKindFalse) || assignop(n.Left.Type, s.exprname.Type, nil) == OCONVIFACE || assignop(s.exprname.Type, n.Left.Type, nil) == OCONVIFACE {
|
|
a.Left = Nod(OEQ, s.exprname, n.Left) // if name == val
|
|
typecheck(&a.Left, Erv)
|
|
} else if s.kind == switchKindTrue {
|
|
a.Left = n.Left // if val
|
|
} else {
|
|
// s.kind == switchKindFalse
|
|
a.Left = Nod(ONOT, n.Left, nil) // if !val
|
|
typecheck(&a.Left, Erv)
|
|
}
|
|
a.Nbody = list1(n.Right) // goto l
|
|
|
|
cas = list(cas, a)
|
|
lineno = int32(lno)
|
|
}
|
|
return liststmt(cas)
|
|
}
|
|
|
|
// find the middle and recur
|
|
half := len(cc) / 2
|
|
a := Nod(OIF, nil, nil)
|
|
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))
|
|
a.Left = Nod(OOROR, lenlt, Nod(OANDAND, leneq, le))
|
|
} else {
|
|
a.Left = le
|
|
}
|
|
typecheck(&a.Left, Erv)
|
|
a.Nbody = list1(s.walkCases(cc[:half]))
|
|
a.Rlist = list1(s.walkCases(cc[half:]))
|
|
return a
|
|
}
|
|
|
|
// casebody builds separate lists of statements and cases.
|
|
// It makes labels between cases and statements
|
|
// and deals with fallthrough, break, and unreachable statements.
|
|
func casebody(sw *Node, typeswvar *Node) {
|
|
if sw.List == nil {
|
|
return
|
|
}
|
|
|
|
lno := setlineno(sw)
|
|
|
|
var cas *NodeList // cases
|
|
var stat *NodeList // statements
|
|
var def *Node // 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 := count(n.List) != 1 || n.List.N.Op == OLITERAL
|
|
|
|
jmp := Nod(OGOTO, newCaseLabel(), nil)
|
|
if n.List == nil {
|
|
if def != nil {
|
|
Yyerror("more than one default case")
|
|
}
|
|
// reuse original default case
|
|
n.Right = jmp
|
|
def = n
|
|
}
|
|
|
|
if n.List != nil && n.List.Next == nil {
|
|
// one case -- reuse OCASE node
|
|
n.Left = n.List.N
|
|
n.Right = jmp
|
|
n.List = nil
|
|
cas = list(cas, n)
|
|
} else {
|
|
// expand multi-valued cases
|
|
for lc := n.List; lc != nil; lc = lc.Next {
|
|
cas = list(cas, Nod(OCASE, lc.N, jmp))
|
|
}
|
|
}
|
|
|
|
stat = list(stat, Nod(OLABEL, jmp.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
|
|
}
|
|
|
|
// nSwitchLabel is the number of switch labels generated.
|
|
// This should be per-function, but it is a global counter for now.
|
|
var nSwitchLabel int
|
|
|
|
func newCaseLabel() *Node {
|
|
label := strconv.Itoa(nSwitchLabel)
|
|
nSwitchLabel++
|
|
return newname(Lookup(label))
|
|
}
|
|
|
|
// 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
|
|
for l := sw.List; l != nil; l = l.Next {
|
|
n := l.N
|
|
c := new(caseClause)
|
|
cc = append(cc, c)
|
|
c.ordinal = len(cc)
|
|
c.node = n
|
|
|
|
if n.Left == nil {
|
|
c.typ = caseKindDefault
|
|
continue
|
|
}
|
|
|
|
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)
|
|
}
|
|
} else {
|
|
// expression switch
|
|
switch consttype(n.Left) {
|
|
case CTFLT, CTINT, CTRUNE, CTSTR:
|
|
c.typ = caseKindExprConst
|
|
default:
|
|
c.typ = caseKindExprVar
|
|
}
|
|
}
|
|
}
|
|
|
|
if cc == nil {
|
|
return nil
|
|
}
|
|
|
|
// sort by value and diagnose duplicate cases
|
|
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 {
|
|
break
|
|
}
|
|
if Eqtype(c1.node.Left.Type, c2.node.Left.Type) {
|
|
yyerrorl(int(c2.node.Lineno), "duplicate case %v in type switch\n\tprevious case at %v", c2.node.Left.Type, c1.node.Line())
|
|
}
|
|
}
|
|
}
|
|
} 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 {
|
|
continue
|
|
}
|
|
setlineno(c2.node)
|
|
Yyerror("duplicate case %v in switch\n\tprevious case at %v", c1.node.Left, c1.node.Line())
|
|
}
|
|
}
|
|
|
|
// put list back in processing order
|
|
sort.Sort(caseClauseByOrd(cc))
|
|
return cc
|
|
}
|
|
|
|
// 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) {
|
|
cond := sw.Left
|
|
sw.Left = nil
|
|
|
|
if cond == nil {
|
|
sw.List = nil
|
|
return
|
|
}
|
|
if cond.Right == nil {
|
|
setlineno(sw)
|
|
Yyerror("type switch must have an assignment")
|
|
return
|
|
}
|
|
|
|
walkexpr(&cond.Right, &sw.Ninit)
|
|
if !Istype(cond.Right.Type, TINTER) {
|
|
Yyerror("type switch must be on an interface")
|
|
return
|
|
}
|
|
|
|
var cas *NodeList
|
|
|
|
// predeclare temporary variables and the boolean var
|
|
s.facename = temp(cond.Right.Type)
|
|
|
|
a := Nod(OAS, s.facename, cond.Right)
|
|
typecheck(&a, Etop)
|
|
cas = list(cas, a)
|
|
|
|
s.okname = temp(Types[TBOOL])
|
|
typecheck(&s.okname, Erv)
|
|
|
|
s.hashname = temp(Types[TUINT32])
|
|
typecheck(&s.hashname, Erv)
|
|
|
|
// set up labels and jumps
|
|
casebody(sw, s.facename)
|
|
|
|
// calculate type hash
|
|
t := cond.Right.Type
|
|
if isnilinter(t) {
|
|
a = syslook("efacethash", 1)
|
|
} else {
|
|
a = syslook("ifacethash", 1)
|
|
}
|
|
substArgTypes(a, t)
|
|
a = Nod(OCALL, a, nil)
|
|
a.List = list1(s.facename)
|
|
a = Nod(OAS, s.hashname, a)
|
|
typecheck(&a, Etop)
|
|
cas = list(cas, a)
|
|
|
|
cc := caseClauses(sw, switchKindType)
|
|
sw.List = nil
|
|
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)
|
|
}
|
|
|
|
// insert type equality check into each case block
|
|
for _, c := range cc {
|
|
n := c.node
|
|
switch c.typ {
|
|
case caseKindTypeNil:
|
|
var v Val
|
|
v.Ctype = CTNIL
|
|
a = Nod(OIF, nil, nil)
|
|
a.Left = Nod(OEQ, s.facename, nodlit(v))
|
|
typecheck(&a.Left, Erv)
|
|
a.Nbody = list1(n.Right) // if i==nil { goto l }
|
|
n.Right = a
|
|
|
|
case caseKindTypeVar, caseKindTypeConst:
|
|
n.Right = s.typeone(n)
|
|
}
|
|
}
|
|
|
|
// generate list of if statements, binary search for constant sequences
|
|
for len(cc) > 0 {
|
|
if cc[0].typ != caseKindTypeConst {
|
|
n := cc[0].node
|
|
cas = list(cas, n.Right)
|
|
cc = cc[1:]
|
|
continue
|
|
}
|
|
|
|
// identify run of constants
|
|
var run int
|
|
for run = 1; run < len(cc) && cc[run].typ == caseKindTypeConst; run++ {
|
|
}
|
|
|
|
// sort by hash
|
|
sort.Sort(caseClauseByType(cc[:run]))
|
|
|
|
// for debugging: linear search
|
|
if false {
|
|
for i := 0; i < run; i++ {
|
|
n := cc[i].node
|
|
cas = list(cas, n.Right)
|
|
}
|
|
continue
|
|
}
|
|
|
|
// combine adjacent cases with the same hash
|
|
ncase := 0
|
|
for i := 0; i < run; i++ {
|
|
ncase++
|
|
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)
|
|
}
|
|
cc[i].node.Right = liststmt(hash)
|
|
}
|
|
|
|
// binary search among cases to narrow by hash
|
|
cas = list(cas, s.walkCases(cc[:ncase]))
|
|
cc = cc[ncase:]
|
|
}
|
|
|
|
// handle default case
|
|
if nerrors == 0 {
|
|
cas = list(cas, def)
|
|
sw.Nbody = concat(cas, sw.Nbody)
|
|
sw.List = nil
|
|
walkstmtlist(sw.Nbody)
|
|
}
|
|
}
|
|
|
|
// typeone generates an AST that jumps to the
|
|
// case body if the variable is of type t.
|
|
func (s *typeSwitch) typeone(t *Node) *Node {
|
|
name := t.Nname
|
|
var init *NodeList
|
|
if name == nil {
|
|
typecheck(&nblank, Erv|Easgn)
|
|
name = nblank
|
|
} else {
|
|
init = list1(Nod(ODCL, name, nil))
|
|
}
|
|
|
|
a := Nod(OAS2, nil, nil)
|
|
a.List = list(list1(name), s.okname) // name, ok =
|
|
b := Nod(ODOTTYPE, s.facename, nil)
|
|
b.Type = t.Left.Type // interface.(type)
|
|
a.Rlist = list1(b)
|
|
typecheck(&a, Etop)
|
|
init = list(init, a)
|
|
|
|
c := Nod(OIF, nil, nil)
|
|
c.Left = s.okname
|
|
c.Nbody = list1(t.Right) // if ok { goto l }
|
|
|
|
return liststmt(list(init, c))
|
|
}
|
|
|
|
// walkCases generates an AST implementing the cases in cc.
|
|
func (s *typeSwitch) walkCases(cc []*caseClause) *Node {
|
|
if len(cc) < binarySearchMin {
|
|
var cas *NodeList
|
|
for _, c := range cc {
|
|
n := c.node
|
|
if c.typ != caseKindTypeConst {
|
|
Fatal("typeSwitch walkCases")
|
|
}
|
|
a := Nod(OIF, nil, nil)
|
|
a.Left = Nod(OEQ, s.hashname, Nodintconst(int64(c.hash)))
|
|
typecheck(&a.Left, Erv)
|
|
a.Nbody = list1(n.Right)
|
|
cas = list(cas, a)
|
|
}
|
|
return liststmt(cas)
|
|
}
|
|
|
|
// find the middle and recur
|
|
half := len(cc) / 2
|
|
a := Nod(OIF, nil, nil)
|
|
a.Left = Nod(OLE, s.hashname, Nodintconst(int64(cc[half-1].hash)))
|
|
typecheck(&a.Left, Erv)
|
|
a.Nbody = list1(s.walkCases(cc[:half]))
|
|
a.Rlist = list1(s.walkCases(cc[half:]))
|
|
return a
|
|
}
|
|
|
|
type caseClauseByOrd []*caseClause
|
|
|
|
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
|
|
}
|
|
|
|
type caseClauseByExpr []*caseClause
|
|
|
|
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
|
|
}
|
|
|
|
func exprcmp(c1, c2 *caseClause) int {
|
|
// sort non-constants last
|
|
if c1.typ != caseKindExprConst {
|
|
return +1
|
|
}
|
|
if c2.typ != caseKindExprConst {
|
|
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 +1
|
|
}
|
|
if ct < int(n2.Val.Ctype) {
|
|
return -1
|
|
}
|
|
if !Eqtype(n1.Type, n2.Type) {
|
|
if n1.Type.Vargen > n2.Type.Vargen {
|
|
return +1
|
|
} else {
|
|
return -1
|
|
}
|
|
}
|
|
|
|
// sort by constant value to enable binary search
|
|
switch ct {
|
|
case CTFLT:
|
|
return mpcmpfltflt(n1.Val.U.(*Mpflt), n2.Val.U.(*Mpflt))
|
|
case CTINT, CTRUNE:
|
|
return Mpcmpfixfix(n1.Val.U.(*Mpint), n2.Val.U.(*Mpint))
|
|
case CTSTR:
|
|
// 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.
|
|
a := n1.Val.U.(string)
|
|
b := n2.Val.U.(string)
|
|
if len(a) < len(b) {
|
|
return -1
|
|
}
|
|
if len(a) > len(b) {
|
|
return +1
|
|
}
|
|
return stringsCompare(a, b)
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
type caseClauseByType []*caseClause
|
|
|
|
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
|
|
|
|
// sort by hash code
|
|
case c1.hash != c2.hash:
|
|
return c1.hash < c2.hash
|
|
}
|
|
|
|
// sort by ordinal
|
|
return c1.ordinal < c2.ordinal
|
|
}
|
|
|
|
func dumpcase(cc []*caseClause) {
|
|
for _, c := range cc {
|
|
switch c.typ {
|
|
case caseKindDefault:
|
|
fmt.Printf("case-default\n")
|
|
fmt.Printf("\tord=%d\n", c.ordinal)
|
|
|
|
case caseKindExprConst:
|
|
fmt.Printf("case-exprconst\n")
|
|
fmt.Printf("\tord=%d\n", c.ordinal)
|
|
|
|
case caseKindExprVar:
|
|
fmt.Printf("case-exprvar\n")
|
|
fmt.Printf("\tord=%d\n", c.ordinal)
|
|
fmt.Printf("\top=%v\n", Oconv(int(c.node.Left.Op), 0))
|
|
|
|
case caseKindTypeNil:
|
|
fmt.Printf("case-typenil\n")
|
|
fmt.Printf("\tord=%d\n", c.ordinal)
|
|
|
|
case caseKindTypeConst:
|
|
fmt.Printf("case-typeconst\n")
|
|
fmt.Printf("\tord=%d\n", c.ordinal)
|
|
fmt.Printf("\thash=%x\n", c.hash)
|
|
|
|
case caseKindTypeVar:
|
|
fmt.Printf("case-typevar\n")
|
|
fmt.Printf("\tord=%d\n", c.ordinal)
|
|
|
|
default:
|
|
fmt.Printf("case-???\n")
|
|
fmt.Printf("\tord=%d\n", c.ordinal)
|
|
fmt.Printf("\top=%v\n", Oconv(int(c.node.Left.Op), 0))
|
|
fmt.Printf("\thash=%x\n", c.hash)
|
|
}
|
|
}
|
|
|
|
fmt.Printf("\n")
|
|
}
|