|  | // 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/compile/internal/types" | 
|  | "fmt" | 
|  | "sort" | 
|  | ) | 
|  |  | 
|  | const ( | 
|  | // expression switch | 
|  | switchKindExpr  = iota // switch a {...} or switch 5 {...} | 
|  | switchKindTrue         // switch true {...} or switch {...} | 
|  | switchKindFalse        // switch false {...} | 
|  | ) | 
|  |  | 
|  | const ( | 
|  | binarySearchMin = 4 // minimum number of cases for binary search | 
|  | integerRangeMin = 2 // minimum size of integer ranges | 
|  | ) | 
|  |  | 
|  | // 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 | 
|  | // isconst indicates whether this case clause is a constant, | 
|  | // for the purposes of the switch code generation. | 
|  | // For expression switches, that's generally literals (case 5:, not case x:). | 
|  | // For type switches, that's concrete types (case time.Time:), not interfaces (case io.Reader:). | 
|  | isconst bool | 
|  | } | 
|  |  | 
|  | // caseClauses are all the case clauses in a switch statement. | 
|  | type caseClauses struct { | 
|  | list   []caseClause // general cases | 
|  | defjmp *Node        // OGOTO for default case or OBREAK if no default case present | 
|  | niljmp *Node        // OGOTO for nil type case in a type switch | 
|  | } | 
|  |  | 
|  | // typecheckswitch typechecks a switch statement. | 
|  | func typecheckswitch(n *Node) { | 
|  | typecheckslice(n.Ninit.Slice(), ctxStmt) | 
|  |  | 
|  | var nilonly string | 
|  | var top int | 
|  | var t *types.Type | 
|  |  | 
|  | if n.Left != nil && n.Left.Op == OTYPESW { | 
|  | // type switch | 
|  | top = Etype | 
|  | n.Left.Right = typecheck(n.Left.Right, ctxExpr) | 
|  | t = n.Left.Right.Type | 
|  | if t != nil && !t.IsInterface() { | 
|  | yyerrorl(n.Pos, "cannot type switch on non-interface value %L", n.Left.Right) | 
|  | } | 
|  | if v := n.Left.Left; v != nil && !v.isBlank() && n.List.Len() == 0 { | 
|  | // We don't actually declare the type switch's guarded | 
|  | // declaration itself. So if there are no cases, we | 
|  | // won't notice that it went unused. | 
|  | yyerrorl(v.Pos, "%v declared and not used", v.Sym) | 
|  | } | 
|  | } else { | 
|  | // expression switch | 
|  | top = ctxExpr | 
|  | if n.Left != nil { | 
|  | n.Left = typecheck(n.Left, ctxExpr) | 
|  | n.Left = defaultlit(n.Left, nil) | 
|  | t = n.Left.Type | 
|  | } else { | 
|  | t = types.Types[TBOOL] | 
|  | } | 
|  | if t != nil { | 
|  | switch { | 
|  | case !okforeq[t.Etype]: | 
|  | yyerrorl(n.Pos, "cannot switch on %L", n.Left) | 
|  | case t.IsSlice(): | 
|  | nilonly = "slice" | 
|  | case t.IsArray() && !IsComparable(t): | 
|  | yyerrorl(n.Pos, "cannot switch on %L", n.Left) | 
|  | case t.IsStruct(): | 
|  | if f := IncomparableField(t); f != nil { | 
|  | yyerrorl(n.Pos, "cannot switch on %L (struct containing %v cannot be compared)", n.Left, f.Type) | 
|  | } | 
|  | case t.Etype == TFUNC: | 
|  | nilonly = "func" | 
|  | case t.IsMap(): | 
|  | nilonly = "map" | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | n.Type = t | 
|  |  | 
|  | var def, niltype *Node | 
|  | for _, ncase := range n.List.Slice() { | 
|  | if ncase.List.Len() == 0 { | 
|  | // default | 
|  | if def != nil { | 
|  | setlineno(ncase) | 
|  | yyerrorl(ncase.Pos, "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], ctxExpr|Etype) | 
|  | n1 = ls[i1] | 
|  | if n1.Type == nil || t == nil { | 
|  | continue | 
|  | } | 
|  |  | 
|  | setlineno(ncase) | 
|  | switch top { | 
|  | // expression switch | 
|  | case ctxExpr: | 
|  | ls[i1] = defaultlit(ls[i1], t) | 
|  | n1 = ls[i1] | 
|  | switch { | 
|  | case n1.Op == OTYPE: | 
|  | yyerrorl(ncase.Pos, "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 { | 
|  | yyerrorl(ncase.Pos, "invalid case %v in switch on %v (mismatched types %v and %v)", n1, n.Left, n1.Type, t) | 
|  | } else { | 
|  | yyerrorl(ncase.Pos, "invalid case %v in switch (mismatched types %v and bool)", n1, n1.Type) | 
|  | } | 
|  | case nilonly != "" && !n1.isNil(): | 
|  | yyerrorl(ncase.Pos, "invalid case %v in switch (can only compare %s %v to nil)", n1, nilonly, n.Left) | 
|  | case t.IsInterface() && !n1.Type.IsInterface() && !IsComparable(n1.Type): | 
|  | yyerrorl(ncase.Pos, "invalid case %L in switch (incomparable type)", n1) | 
|  | } | 
|  |  | 
|  | // type switch | 
|  | case Etype: | 
|  | var missing, have *types.Field | 
|  | var ptr int | 
|  | switch { | 
|  | case n1.Op == OLITERAL && n1.Type.IsKind(TNIL): | 
|  | // case nil: | 
|  | if niltype != nil { | 
|  | yyerrorl(ncase.Pos, "multiple nil cases in type switch (first at %v)", niltype.Line()) | 
|  | } else { | 
|  | niltype = ncase | 
|  | } | 
|  | case n1.Op != OTYPE && n1.Type != nil: // should this be ||? | 
|  | yyerrorl(ncase.Pos, "%L is not a type", n1) | 
|  | // 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() { | 
|  | yyerrorl(ncase.Pos, "impossible type switch case: %L cannot have dynamic type %v"+ | 
|  | " (wrong type for %v method)\n\thave %v%S\n\twant %v%S", n.Left.Right, n1.Type, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type) | 
|  | } else if !missing.Broke() { | 
|  | if ptr != 0 { | 
|  | yyerrorl(ncase.Pos, "impossible type switch case: %L cannot have dynamic type %v"+ | 
|  | " (%v method has pointer receiver)", n.Left.Right, n1.Type, missing.Sym) | 
|  | } else { | 
|  | yyerrorl(ncase.Pos, "impossible type switch case: %L cannot have dynamic type %v"+ | 
|  | " (missing %v method)", n.Left.Right, n1.Type, missing.Sym) | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if n.Type == nil || n.Type.IsUntyped() { | 
|  | // if the value we're switching on has no type or is untyped, | 
|  | // we've already printed an error and don't need to continue | 
|  | // typechecking the body | 
|  | return | 
|  | } | 
|  |  | 
|  | if top == Etype { | 
|  | 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.Type = ll.First().Type | 
|  | } else { | 
|  | // multiple entry type switch or default | 
|  | nvar.Type = n.Type | 
|  | } | 
|  |  | 
|  | nvar = typecheck(nvar, ctxExpr|ctxAssign) | 
|  | ncase.Rlist.SetFirst(nvar) | 
|  | } | 
|  | } | 
|  |  | 
|  | typecheckslice(ncase.Nbody.Slice(), ctxStmt) | 
|  | } | 
|  | switch top { | 
|  | // expression switch | 
|  | case ctxExpr: | 
|  | checkDupExprCases(n.Left, n.List.Slice()) | 
|  | } | 
|  | } | 
|  |  | 
|  | // 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, ctxExpr) | 
|  | sw.Left = defaultlit(sw.Left, nil) | 
|  | } | 
|  |  | 
|  | 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) { | 
|  | // Guard against double walk, see #25776. | 
|  | if sw.List.Len() == 0 && sw.Nbody.Len() > 0 { | 
|  | return // Was fatal, but eliminating every possible source of double-walking is hard | 
|  | } | 
|  |  | 
|  | 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 | 
|  | } | 
|  | } | 
|  |  | 
|  | // Given "switch string(byteslice)", | 
|  | // with all cases being constants (or the default case), | 
|  | // use a zero-cost alias of the byte slice. | 
|  | // In theory, we could be more aggressive, | 
|  | // allowing any side-effect-free expressions in cases, | 
|  | // but it's a bit tricky because some of that information | 
|  | // is unavailable due to the introduction of temporaries during order. | 
|  | // Restricting to constants is simple and probably powerful enough. | 
|  | // Do this before calling walkexpr on cond, | 
|  | // because walkexpr will lower the string | 
|  | // conversion into a runtime call. | 
|  | // See issue 24937 for more discussion. | 
|  | if cond.Op == OBYTES2STR { | 
|  | ok := true | 
|  | for _, cas := range sw.List.Slice() { | 
|  | if cas.Op != OCASE { | 
|  | Fatalf("switch string(byteslice) bad op: %v", cas.Op) | 
|  | } | 
|  | if cas.Left != nil && !Isconst(cas.Left, CTSTR) { | 
|  | ok = false | 
|  | break | 
|  | } | 
|  | } | 
|  | if ok { | 
|  | cond.Op = OBYTES2STRTMP | 
|  | } | 
|  | } | 
|  |  | 
|  | 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)} // This gets walk()ed again in walkstmtlist just before end of this function.  See #29562. | 
|  | typecheckslice(cas, ctxStmt) | 
|  | } | 
|  |  | 
|  | // Enumerate the cases and prepare the default case. | 
|  | clauses := s.genCaseClauses(sw.List.Slice()) | 
|  | sw.List.Set(nil) | 
|  | cc := clauses.list | 
|  |  | 
|  | // handle the cases in order | 
|  | for len(cc) > 0 { | 
|  | run := 1 | 
|  | if okforcmp[t.Etype] && cc[0].isconst { | 
|  | // do binary search on runs of constants | 
|  | for ; run < len(cc) && cc[run].isconst; run++ { | 
|  | } | 
|  | // sort and compile constants | 
|  | sort.Sort(caseClauseByConstVal(cc[:run])) | 
|  | } | 
|  |  | 
|  | a := s.walkCases(cc[:run]) | 
|  | cas = append(cas, a) | 
|  | cc = cc[run:] | 
|  | } | 
|  |  | 
|  | // handle default case | 
|  | if nerrors == 0 { | 
|  | cas = append(cas, clauses.defjmp) | 
|  | sw.Nbody.Prepend(cas...) | 
|  | 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 rng := n.List.Slice(); rng != nil { | 
|  | // Integer range. | 
|  | // exprname is a temp or a constant, | 
|  | // so it is safe to evaluate twice. | 
|  | // In most cases, this conjunction will be | 
|  | // rewritten by walkinrange into a single comparison. | 
|  | low := nod(OGE, s.exprname, rng[0]) | 
|  | high := nod(OLE, s.exprname, rng[1]) | 
|  | a.Left = nod(OANDAND, low, high) | 
|  | } else 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 | 
|  | } 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, ctxExpr) | 
|  | a.Left = defaultlit(a.Left, nil) | 
|  | 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) | 
|  | n := cc[half-1].node | 
|  | var mid *Node | 
|  | if rng := n.List.Slice(); rng != nil { | 
|  | mid = rng[1] // high end of range | 
|  | } else { | 
|  | mid = n.Left | 
|  | } | 
|  | le := nod(OLE, s.exprname, mid) | 
|  | if Isconst(mid, CTSTR) { | 
|  | // Search by length and then by value; see caseClauseByConstVal. | 
|  | 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, ctxExpr) | 
|  | a.Left = defaultlit(a.Left, nil) | 
|  | 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 _, n := range sw.List.Slice() { | 
|  | setlineno(n) | 
|  | if n.Op != OXCASE { | 
|  | Fatalf("casebody %v", n.Op) | 
|  | } | 
|  | n.Op = OCASE | 
|  | needvar := n.List.Len() != 1 || n.List.First().Op == OLITERAL | 
|  |  | 
|  | lbl := autolabel(".s") | 
|  | jmp := nodSym(OGOTO, nil, lbl) | 
|  | switch n.List.Len() { | 
|  | case 0: | 
|  | // default | 
|  | if def != nil { | 
|  | yyerrorl(n.Pos, "more than one default case") | 
|  | } | 
|  | // reuse original default case | 
|  | n.Right = jmp | 
|  | def = n | 
|  | case 1: | 
|  | // one case -- reuse OCASE node | 
|  | n.Left = n.List.First() | 
|  | n.Right = jmp | 
|  | n.List.Set(nil) | 
|  | cas = append(cas, n) | 
|  | default: | 
|  | // Expand multi-valued cases and detect ranges of integer cases. | 
|  | if typeswvar != nil || sw.Left.Type.IsInterface() || !n.List.First().Type.IsInteger() || n.List.Len() < integerRangeMin { | 
|  | // Can't use integer ranges. Expand each case into a separate node. | 
|  | for _, n1 := range n.List.Slice() { | 
|  | cas = append(cas, nod(OCASE, n1, jmp)) | 
|  | } | 
|  | break | 
|  | } | 
|  | // Find integer ranges within runs of constants. | 
|  | s := n.List.Slice() | 
|  | j := 0 | 
|  | for j < len(s) { | 
|  | // Find a run of constants. | 
|  | var run int | 
|  | for run = j; run < len(s) && Isconst(s[run], CTINT); run++ { | 
|  | } | 
|  | if run-j >= integerRangeMin { | 
|  | // Search for integer ranges in s[j:run]. | 
|  | // Typechecking is done, so all values are already in an appropriate range. | 
|  | search := s[j:run] | 
|  | sort.Sort(constIntNodesByVal(search)) | 
|  | for beg, end := 0, 1; end <= len(search); end++ { | 
|  | if end < len(search) && search[end].Int64() == search[end-1].Int64()+1 { | 
|  | continue | 
|  | } | 
|  | if end-beg >= integerRangeMin { | 
|  | // Record range in List. | 
|  | c := nod(OCASE, nil, jmp) | 
|  | c.List.Set2(search[beg], search[end-1]) | 
|  | cas = append(cas, c) | 
|  | } else { | 
|  | // Not large enough for range; record separately. | 
|  | for _, n := range search[beg:end] { | 
|  | cas = append(cas, nod(OCASE, n, jmp)) | 
|  | } | 
|  | } | 
|  | beg = end | 
|  | } | 
|  | j = run | 
|  | } | 
|  | // Advance to next constant, adding individual non-constant | 
|  | // or as-yet-unhandled constant cases as we go. | 
|  | for ; j < len(s) && (j < run || !Isconst(s[j], CTINT)); j++ { | 
|  | cas = append(cas, nod(OCASE, s[j], jmp)) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | stat = append(stat, nodSym(OLABEL, nil, lbl)) | 
|  | if typeswvar != nil && needvar && n.Rlist.Len() != 0 { | 
|  | l := []*Node{ | 
|  | nod(ODCL, n.Rlist.First(), nil), | 
|  | nod(OAS, n.Rlist.First(), typeswvar), | 
|  | } | 
|  | typecheckslice(l, ctxStmt) | 
|  | stat = append(stat, l...) | 
|  | } | 
|  | stat = append(stat, n.Nbody.Slice()...) | 
|  |  | 
|  | // Search backwards for the index of the fallthrough | 
|  | // statement. Do not assume it'll be in the last | 
|  | // position, since in some cases (e.g. when the statement | 
|  | // list contains autotmp_ variables), one or more OVARKILL | 
|  | // nodes will be at the end of the list. | 
|  | fallIndex := len(stat) - 1 | 
|  | for stat[fallIndex].Op == OVARKILL { | 
|  | fallIndex-- | 
|  | } | 
|  | last := stat[fallIndex] | 
|  | if last.Op != OFALL { | 
|  | 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 | 
|  | } | 
|  |  | 
|  | // genCaseClauses generates the caseClauses value for clauses. | 
|  | func (s *exprSwitch) genCaseClauses(clauses []*Node) caseClauses { | 
|  | var cc caseClauses | 
|  | for _, n := range clauses { | 
|  | if n.Left == nil && n.List.Len() == 0 { | 
|  | // default case | 
|  | if cc.defjmp != nil { | 
|  | Fatalf("duplicate default case not detected during typechecking") | 
|  | } | 
|  | cc.defjmp = n.Right | 
|  | continue | 
|  | } | 
|  | c := caseClause{node: n, ordinal: len(cc.list)} | 
|  | if n.List.Len() > 0 { | 
|  | c.isconst = true | 
|  | } | 
|  | switch consttype(n.Left) { | 
|  | case CTFLT, CTINT, CTRUNE, CTSTR: | 
|  | c.isconst = true | 
|  | } | 
|  | cc.list = append(cc.list, c) | 
|  | } | 
|  |  | 
|  | if cc.defjmp == nil { | 
|  | cc.defjmp = nod(OBREAK, nil, nil) | 
|  | } | 
|  | return cc | 
|  | } | 
|  |  | 
|  | // genCaseClauses generates the caseClauses value for clauses. | 
|  | func (s *typeSwitch) genCaseClauses(clauses []*Node) caseClauses { | 
|  | var cc caseClauses | 
|  | for _, n := range clauses { | 
|  | switch { | 
|  | case n.Left == nil: | 
|  | // default case | 
|  | if cc.defjmp != nil { | 
|  | Fatalf("duplicate default case not detected during typechecking") | 
|  | } | 
|  | cc.defjmp = n.Right | 
|  | continue | 
|  | case n.Left.Op == OLITERAL: | 
|  | // nil case in type switch | 
|  | if cc.niljmp != nil { | 
|  | Fatalf("duplicate nil case not detected during typechecking") | 
|  | } | 
|  | cc.niljmp = n.Right | 
|  | continue | 
|  | } | 
|  |  | 
|  | // general case | 
|  | c := caseClause{ | 
|  | node:    n, | 
|  | ordinal: len(cc.list), | 
|  | isconst: !n.Left.Type.IsInterface(), | 
|  | hash:    typehash(n.Left.Type), | 
|  | } | 
|  | cc.list = append(cc.list, c) | 
|  | } | 
|  |  | 
|  | if cc.defjmp == nil { | 
|  | cc.defjmp = nod(OBREAK, nil, nil) | 
|  | } | 
|  |  | 
|  | // diagnose duplicate cases | 
|  | s.checkDupCases(cc.list) | 
|  | return cc | 
|  | } | 
|  |  | 
|  | func (s *typeSwitch) checkDupCases(cc []caseClause) { | 
|  | if len(cc) < 2 { | 
|  | return | 
|  | } | 
|  | // We store seen types in a map keyed by type hash. | 
|  | // It is possible, but very unlikely, for multiple distinct types to have the same hash. | 
|  | seen := make(map[uint32][]*Node) | 
|  | // To avoid many small allocations of length 1 slices, | 
|  | // also set up a single large slice to slice into. | 
|  | nn := make([]*Node, 0, len(cc)) | 
|  | Outer: | 
|  | for _, c := range cc { | 
|  | prev, ok := seen[c.hash] | 
|  | if !ok { | 
|  | // First entry for this hash. | 
|  | nn = append(nn, c.node) | 
|  | seen[c.hash] = nn[len(nn)-1 : len(nn) : len(nn)] | 
|  | continue | 
|  | } | 
|  | for _, n := range prev { | 
|  | if types.Identical(n.Left.Type, c.node.Left.Type) { | 
|  | yyerrorl(c.node.Pos, "duplicate case %v in type switch\n\tprevious case at %v", c.node.Left.Type, n.Line()) | 
|  | // avoid double-reporting errors | 
|  | continue Outer | 
|  | } | 
|  | } | 
|  | seen[c.hash] = append(seen[c.hash], c.node) | 
|  | } | 
|  | } | 
|  |  | 
|  | func checkDupExprCases(exprname *Node, clauses []*Node) { | 
|  | // boolean (naked) switch, nothing to do. | 
|  | if exprname == nil { | 
|  | return | 
|  | } | 
|  |  | 
|  | var cs constSet | 
|  | for _, ncase := range clauses { | 
|  | for _, n := range ncase.List.Slice() { | 
|  | // Don't check for duplicate bools. Although the spec allows it, | 
|  | // (1) the compiler hasn't checked it in the past, so compatibility mandates it, and | 
|  | // (2) it would disallow useful things like | 
|  | //       case GOARCH == "arm" && GOARM == "5": | 
|  | //       case GOARCH == "arm": | 
|  | //     which would both evaluate to false for non-ARM compiles. | 
|  | if n.Type.IsBoolean() { | 
|  | continue | 
|  | } | 
|  |  | 
|  | if prev := cs.add(n); prev != nil { | 
|  | yyerrorl(ncase.Pos, "duplicate case %s in switch\n\tprevious case at %v", | 
|  | nodeAndVal(n), prev.Line()) | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func nodeAndVal(n *Node) string { | 
|  | show := n.String() | 
|  | val := n.Val().Interface() | 
|  | if s := fmt.Sprintf("%#v", val); show != s { | 
|  | show += " (value " + s + ")" | 
|  | } | 
|  | return show | 
|  | } | 
|  |  | 
|  | // 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 { | 
|  | yyerrorl(sw.Pos, "type switch must have an assignment") | 
|  | return | 
|  | } | 
|  |  | 
|  | cond.Right = walkexpr(cond.Right, &sw.Ninit) | 
|  | if !cond.Right.Type.IsInterface() { | 
|  | yyerrorl(sw.Pos, "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, ctxStmt) | 
|  | cas = append(cas, a) | 
|  |  | 
|  | s.okname = temp(types.Types[TBOOL]) | 
|  | s.okname = typecheck(s.okname, ctxExpr) | 
|  |  | 
|  | s.hashname = temp(types.Types[TUINT32]) | 
|  | s.hashname = typecheck(s.hashname, ctxExpr) | 
|  |  | 
|  | // set up labels and jumps | 
|  | casebody(sw, s.facename) | 
|  |  | 
|  | clauses := s.genCaseClauses(sw.List.Slice()) | 
|  | sw.List.Set(nil) | 
|  | def := clauses.defjmp | 
|  |  | 
|  | // 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. | 
|  | // For empty interfaces this will be the type. | 
|  | // For non-empty interfaces this will be the itab. | 
|  | itab := nod(OITAB, s.facename, nil) | 
|  |  | 
|  | // Check for nil first. | 
|  | i := nod(OIF, nil, nil) | 
|  | i.Left = nod(OEQ, itab, nodnil()) | 
|  | if clauses.niljmp != nil { | 
|  | // Do explicit nil case right here. | 
|  | i.Nbody.Set1(clauses.niljmp) | 
|  | } else { | 
|  | // Jump to default case. | 
|  | lbl := autolabel(".s") | 
|  | i.Nbody.Set1(nodSym(OGOTO, nil, lbl)) | 
|  | // Wrap default case with label. | 
|  | blk := nod(OBLOCK, nil, nil) | 
|  | blk.List.Set2(nodSym(OLABEL, nil, lbl), def) | 
|  | def = blk | 
|  | } | 
|  | i.Left = typecheck(i.Left, ctxExpr) | 
|  | i.Left = defaultlit(i.Left, nil) | 
|  | cas = append(cas, i) | 
|  |  | 
|  | // Load hash from type or itab. | 
|  | h := nodSym(ODOTPTR, itab, nil) | 
|  | h.Type = types.Types[TUINT32] | 
|  | h.SetTypecheck(1) | 
|  | if cond.Right.Type.IsEmptyInterface() { | 
|  | h.Xoffset = int64(2 * Widthptr) // offset of hash in runtime._type | 
|  | } else { | 
|  | h.Xoffset = int64(2 * Widthptr) // offset of hash in runtime.itab | 
|  | } | 
|  | h.SetBounded(true) // guaranteed not to fault | 
|  | a = nod(OAS, s.hashname, h) | 
|  | a = typecheck(a, ctxStmt) | 
|  | cas = append(cas, a) | 
|  |  | 
|  | cc := clauses.list | 
|  |  | 
|  | // insert type equality check into each case block | 
|  | for _, c := range cc { | 
|  | c.node.Right = s.typeone(c.node) | 
|  | } | 
|  |  | 
|  | // generate list of if statements, binary search for constant sequences | 
|  | for len(cc) > 0 { | 
|  | if !cc[0].isconst { | 
|  | 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].isconst; 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 | 
|  | var batch []caseClause | 
|  | for i, j := 0, 0; i < run; i = j { | 
|  | 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) | 
|  | batch = append(batch, cc[i]) | 
|  | } | 
|  |  | 
|  | // binary search among cases to narrow by hash | 
|  | cas = append(cas, s.walkCases(batch)) | 
|  | cc = cc[run:] | 
|  | } | 
|  |  | 
|  | // handle default case | 
|  | if nerrors == 0 { | 
|  | cas = append(cas, def) | 
|  | sw.Nbody.Prepend(cas...) | 
|  | 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 Nodes | 
|  | if t.Rlist.Len() == 0 { | 
|  | name = nblank | 
|  | nblank = typecheck(nblank, ctxExpr|ctxAssign) | 
|  | } else { | 
|  | name = t.Rlist.First() | 
|  | init.Append(nod(ODCL, name, nil)) | 
|  | a := nod(OAS, name, nil) | 
|  | a = typecheck(a, ctxStmt) | 
|  | init.Append(a) | 
|  | } | 
|  |  | 
|  | a := nod(OAS2, nil, nil) | 
|  | a.List.Set2(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, ctxStmt) | 
|  | a = walkexpr(a, &init) | 
|  | init.Append(a) | 
|  |  | 
|  | c := nod(OIF, nil, nil) | 
|  | c.Left = s.okname | 
|  | c.Nbody.Set1(t.Right) // if ok { goto l } | 
|  |  | 
|  | init.Append(c) | 
|  | return init.asblock() | 
|  | } | 
|  |  | 
|  | // 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.isconst { | 
|  | Fatalf("typeSwitch walkCases") | 
|  | } | 
|  | a := nod(OIF, nil, nil) | 
|  | a.Left = nod(OEQ, s.hashname, nodintconst(int64(c.hash))) | 
|  | a.Left = typecheck(a.Left, ctxExpr) | 
|  | a.Left = defaultlit(a.Left, nil) | 
|  | 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, ctxExpr) | 
|  | a.Left = defaultlit(a.Left, nil) | 
|  | a.Nbody.Set1(s.walkCases(cc[:half])) | 
|  | a.Rlist.Set1(s.walkCases(cc[half:])) | 
|  | return a | 
|  | } | 
|  |  | 
|  | // caseClauseByConstVal sorts clauses by constant value to enable binary search. | 
|  | type caseClauseByConstVal []caseClause | 
|  |  | 
|  | func (x caseClauseByConstVal) Len() int      { return len(x) } | 
|  | func (x caseClauseByConstVal) Swap(i, j int) { x[i], x[j] = x[j], x[i] } | 
|  | func (x caseClauseByConstVal) Less(i, j int) bool { | 
|  | // n1 and n2 might be individual constants or integer ranges. | 
|  | // We have checked for duplicates already, | 
|  | // so ranges can be safely represented by any value in the range. | 
|  | n1 := x[i].node | 
|  | var v1 interface{} | 
|  | if s := n1.List.Slice(); s != nil { | 
|  | v1 = s[0].Val().U | 
|  | } else { | 
|  | v1 = n1.Left.Val().U | 
|  | } | 
|  |  | 
|  | n2 := x[j].node | 
|  | var v2 interface{} | 
|  | if s := n2.List.Slice(); s != nil { | 
|  | v2 = s[0].Val().U | 
|  | } else { | 
|  | v2 = n2.Left.Val().U | 
|  | } | 
|  |  | 
|  | switch v1 := v1.(type) { | 
|  | case *Mpflt: | 
|  | return v1.Cmp(v2.(*Mpflt)) < 0 | 
|  | case *Mpint: | 
|  | return v1.Cmp(v2.(*Mpint)) < 0 | 
|  | case string: | 
|  | // 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 := v1 | 
|  | b := v2.(string) | 
|  | if len(a) != len(b) { | 
|  | return len(a) < len(b) | 
|  | } | 
|  | return a < b | 
|  | } | 
|  |  | 
|  | Fatalf("caseClauseByConstVal passed bad clauses %v < %v", x[i].node.Left, x[j].node.Left) | 
|  | return false | 
|  | } | 
|  |  | 
|  | 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] | 
|  | // sort by hash code, then ordinal (for the rare case of hash collisions) | 
|  | if c1.hash != c2.hash { | 
|  | return c1.hash < c2.hash | 
|  | } | 
|  | return c1.ordinal < c2.ordinal | 
|  | } | 
|  |  | 
|  | type constIntNodesByVal []*Node | 
|  |  | 
|  | func (x constIntNodesByVal) Len() int      { return len(x) } | 
|  | func (x constIntNodesByVal) Swap(i, j int) { x[i], x[j] = x[j], x[i] } | 
|  | func (x constIntNodesByVal) Less(i, j int) bool { | 
|  | return x[i].Val().U.(*Mpint).Cmp(x[j].Val().U.(*Mpint)) < 0 | 
|  | } |