// 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) }