// 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 ( "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 := lineno typecheckslice(n.Ninit.Slice(), Etop) var nilonly string var top int var t *Type if n.Left != nil && n.Left.Op == OTYPESW { // type switch top = Etype n.Left.Right = typecheck(n.Left.Right, Erv) t = n.Left.Right.Type if t != nil && !t.IsInterface() { Yyerror("cannot type switch on non-interface value %v", Nconv(n.Left.Right, FmtLong)) } } else { // expression switch top = Erv if n.Left != nil { n.Left = typecheck(n.Left, Erv) n.Left = defaultlit(n.Left, nil) t = n.Left.Type } else { t = Types[TBOOL] } if t != nil { switch { case !okforeq[t.Etype]: Yyerror("cannot switch on %v", Nconv(n.Left, FmtLong)) case t.IsSlice(): nilonly = "slice" case t.IsArray() && !t.IsComparable(): Yyerror("cannot switch on %v", Nconv(n.Left, FmtLong)) case t.IsStruct(): if f := t.IncomparableField(); f != nil { Yyerror("cannot switch on %v (struct containing %v cannot be compared)", Nconv(n.Left, FmtLong), f.Type) } case t.Etype == TFUNC: nilonly = "func" case t.IsMap(): nilonly = "map" } } } n.Type = t var def *Node for _, ncase := range n.List.Slice() { setlineno(n) if ncase.List.Len() == 0 { // default if def != nil { Yyerror("multiple defaults in switch (first at %v)", def.Line()) } else { def = ncase } } else { ls := ncase.List.Slice() for i1, n1 := range ls { setlineno(n1) ls[i1] = typecheck(ls[i1], Erv|Etype) n1 = ls[i1] if n1.Type == nil || t == nil { continue } setlineno(ncase) switch top { // expression switch case Erv: ls[i1] = defaultlit(ls[i1], t) n1 = ls[i1] switch { case n1.Op == OTYPE: Yyerror("type %v is not an expression", n1.Type) case n1.Type != nil && assignop(n1.Type, t, nil) == 0 && assignop(t, n1.Type, nil) == 0: if n.Left != nil { Yyerror("invalid case %v in switch on %v (mismatched types %v and %v)", n1, n.Left, n1.Type, t) } else { Yyerror("invalid case %v in switch (mismatched types %v and bool)", n1, n1.Type) } case nilonly != "" && !isnil(n1): Yyerror("invalid case %v in switch (can only compare %s %v to nil)", n1, nilonly, n.Left) case t.IsInterface() && !n1.Type.IsInterface() && !n1.Type.IsComparable(): Yyerror("invalid case %v in switch (incomparable type)", Nconv(n1, FmtLong)) } // type switch case Etype: var missing, have *Field var ptr int switch { case n1.Op == OLITERAL && n1.Type.IsKind(TNIL): case n1.Op != OTYPE && n1.Type != nil: // should this be ||? Yyerror("%v is not a type", Nconv(n1, FmtLong)) // reset to original type n1 = n.Left.Right ls[i1] = n1 case !n1.Type.IsInterface() && t.IsInterface() && !implements(n1.Type, t, &missing, &have, &ptr): if have != nil && !missing.Broke && !have.Broke { 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, FmtLong), n1.Type, missing.Sym, have.Sym, Tconv(have.Type, FmtShort), missing.Sym, Tconv(missing.Type, FmtShort)) } else if !missing.Broke { Yyerror("impossible type switch case: %v cannot have dynamic type %v"+" (missing %v method)", Nconv(n.Left.Right, FmtLong), n1.Type, missing.Sym) } } } } } if top == Etype && n.Type != nil { ll := ncase.List if ncase.Rlist.Len() != 0 { nvar := ncase.Rlist.First() if ll.Len() == 1 && ll.First().Type != nil && !ll.First().Type.IsKind(TNIL) { // single entry type switch nvar.Name.Param.Ntype = typenod(ll.First().Type) } else { // multiple entry type switch or default nvar.Name.Param.Ntype = typenod(n.Type) } nvar = typecheck(nvar, Erv|Easgn) ncase.Rlist.SetIndex(0, nvar) } } typecheckslice(ncase.Nbody.Slice(), Etop) } lineno = lno } // walkswitch walks a switch statement. func walkswitch(sw *Node) { // convert switch {...} to switch true {...} if sw.Left == nil { sw.Left = Nodbool(true) sw.Left = 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 } } cond = walkexpr(cond, &sw.Ninit) t := sw.Type if t == nil { return } // convert the switch into OIF statements var cas []*Node 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 = []*Node{Nod(OAS, s.exprname, cond)} typecheckslice(cas, Etop) } // enumerate the cases, and lop off the default case cc := caseClauses(sw, s.kind) sw.List.Set(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 = append(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 = append(cas, a) cc = cc[run:] } // handle default case if nerrors == 0 { cas = append(cas, def) sw.Nbody.Set(append(cas, sw.Nbody.Slice()...)) walkstmtlist(sw.Nbody.Slice()) } } // walkCases generates an AST implementing the cases in cc. func (s *exprSwitch) walkCases(cc []*caseClause) *Node { if len(cc) < binarySearchMin { // linear search var cas []*Node for _, c := range cc { n := c.node lno := 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 a.Left = 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 a.Left = typecheck(a.Left, Erv) } a.Nbody.Set1(n.Right) // goto l cas = append(cas, a) lineno = 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 } a.Left = typecheck(a.Left, Erv) a.Nbody.Set1(s.walkCases(cc[:half])) a.Rlist.Set1(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.Len() == 0 { return } lno := setlineno(sw) var cas []*Node // cases var stat []*Node // statements var def *Node // defaults br := Nod(OBREAK, nil, nil) for i, n := range sw.List.Slice() { setlineno(n) if n.Op != OXCASE { Fatalf("casebody %v", oconv(n.Op, 0)) } n.Op = OCASE needvar := n.List.Len() != 1 || n.List.First().Op == OLITERAL jmp := Nod(OGOTO, newCaseLabel(), nil) if n.List.Len() == 0 { if def != nil { Yyerror("more than one default case") } // reuse original default case n.Right = jmp def = n } if n.List.Len() == 1 { // one case -- reuse OCASE node n.Left = n.List.First() n.Right = jmp n.List.Set(nil) cas = append(cas, n) } else { // expand multi-valued cases for _, n1 := range n.List.Slice() { cas = append(cas, Nod(OCASE, n1, jmp)) } } stat = append(stat, Nod(OLABEL, jmp.Left, nil)) if typeswvar != nil && needvar && n.Rlist.Len() != 0 { l := []*Node{ Nod(ODCL, n.Rlist.First(), nil), Nod(OAS, n.Rlist.First(), typeswvar), } typecheckslice(l, Etop) stat = append(stat, l...) } stat = append(stat, n.Nbody.Slice()...) // botch - shouldn't fall through declaration last := stat[len(stat)-1] if last.Xoffset == n.Xoffset && last.Op == OXFALL { if typeswvar != nil { setlineno(last) Yyerror("cannot fallthrough in type switch") } if i+1 >= sw.List.Len() { setlineno(last) Yyerror("cannot fallthrough final case in switch") } last.Op = OFALL } else { stat = append(stat, br) } } stat = append(stat, br) if def != nil { cas = append(cas, def) } sw.List.Set(cas) sw.Nbody.Set(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 _, n := range sw.List.Slice() { 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 n.Left.Type.IsInterface(): 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(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.Set(nil) return } if cond.Right == nil { setlineno(sw) Yyerror("type switch must have an assignment") return } cond.Right = walkexpr(cond.Right, &sw.Ninit) if !cond.Right.Type.IsInterface() { Yyerror("type switch must be on an interface") return } var cas []*Node // predeclare temporary variables and the boolean var s.facename = temp(cond.Right.Type) a := Nod(OAS, s.facename, cond.Right) a = typecheck(a, Etop) cas = append(cas, a) s.okname = temp(Types[TBOOL]) s.okname = typecheck(s.okname, Erv) s.hashname = temp(Types[TUINT32]) s.hashname = typecheck(s.hashname, Erv) // set up labels and jumps casebody(sw, s.facename) cc := caseClauses(sw, switchKindType) sw.List.Set(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) } 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. i.Nbody.Set1(typenil) } else { // Jump to default case. lbl := newCaseLabel() i.Nbody.Set1(Nod(OGOTO, lbl, nil)) // Wrap default case with label. blk := Nod(OBLOCK, nil, nil) blk.List.Set([]*Node{Nod(OLABEL, lbl, nil), def}) def = blk } i.Left = typecheck(i.Left, Erv) cas = append(cas, i) if !cond.Right.Type.IsEmptyInterface() { // Load type from itab. typ = NodSym(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 := NodSym(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) a = typecheck(a, Etop) cas = append(cas, a) // 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) default: Fatalf("typeSwitch with bad kind: %d", c.typ) } } // generate list of if statements, binary search for constant sequences for len(cc) > 0 { if cc[0].typ != caseKindTypeConst { n := cc[0].node cas = append(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 = append(cas, n.Right) } continue } // combine adjacent cases with the same hash ncase := 0 for i := 0; i < run; i++ { ncase++ hash := []*Node{cc[i].node.Right} for j := i + 1; j < run && cc[i].hash == cc[j].hash; j++ { hash = append(hash, cc[j].node.Right) } cc[i].node.Right = liststmt(hash) } // binary search among cases to narrow by hash cas = append(cas, s.walkCases(cc[:ncase])) cc = cc[ncase:] } // handle default case if nerrors == 0 { cas = append(cas, def) sw.Nbody.Set(append(cas, sw.Nbody.Slice()...)) sw.List.Set(nil) walkstmtlist(sw.Nbody.Slice()) } } // typeone generates an AST that jumps to the // case body if the variable is of type t. func (s *typeSwitch) typeone(t *Node) *Node { var name *Node var init []*Node if t.Rlist.Len() == 0 { name = nblank nblank = typecheck(nblank, Erv|Easgn) } else { name = t.Rlist.First() init = []*Node{Nod(ODCL, name, nil)} a := Nod(OAS, name, nil) a = typecheck(a, Etop) init = append(init, a) } a := Nod(OAS2, nil, nil) a.List.Set([]*Node{name, s.okname}) // name, ok = b := Nod(ODOTTYPE, s.facename, nil) b.Type = t.Left.Type // interface.(type) a.Rlist.Set1(b) a = typecheck(a, Etop) init = append(init, a) c := Nod(OIF, nil, nil) c.Left = s.okname c.Nbody.Set1(t.Right) // if ok { goto l } return liststmt(append(init, c)) } // walkCases generates an AST implementing the cases in cc. func (s *typeSwitch) walkCases(cc []*caseClause) *Node { if len(cc) < binarySearchMin { var cas []*Node for _, c := range cc { n := c.node if c.typ != caseKindTypeConst { Fatalf("typeSwitch walkCases") } a := Nod(OIF, nil, nil) a.Left = Nod(OEQ, s.hashname, Nodintconst(int64(c.hash))) a.Left = typecheck(a.Left, Erv) a.Nbody.Set1(n.Right) cas = append(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))) a.Left = typecheck(a.Left, Erv) a.Nbody.Set1(s.walkCases(cc[:half])) a.Rlist.Set1(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 := n1.Val().Ctype() if ct > n2.Val().Ctype() { return +1 } if ct < 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 n1.Val().U.(*Mpflt).Cmp(n2.Val().U.(*Mpflt)) case CTINT, CTRUNE: return n1.Val().U.(*Mpint).Cmp(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 } if a == b { return 0 } if a < b { return -1 } return +1 } 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 }