Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1 | // Copyright 2009 The Go Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
| 5 | package gc |
| 6 | |
| 7 | import ( |
| 8 | "cmd/internal/obj" |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 9 | "sort" |
| 10 | "strconv" |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 11 | ) |
| 12 | |
| 13 | const ( |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 14 | // expression switch |
| 15 | switchKindExpr = iota // switch a {...} or switch 5 {...} |
| 16 | switchKindTrue // switch true {...} or switch {...} |
| 17 | switchKindFalse // switch false {...} |
| 18 | |
| 19 | // type switch |
| 20 | switchKindType // switch a.(type) {...} |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 21 | ) |
| 22 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 23 | const ( |
| 24 | caseKindDefault = iota // default: |
| 25 | |
| 26 | // expression switch |
| 27 | caseKindExprConst // case 5: |
| 28 | caseKindExprVar // case x: |
| 29 | |
| 30 | // type switch |
| 31 | caseKindTypeNil // case nil: |
| 32 | caseKindTypeConst // case time.Time: (concrete type, has type hash) |
| 33 | caseKindTypeVar // case io.Reader: (interface type) |
| 34 | ) |
| 35 | |
| 36 | const binarySearchMin = 4 // minimum number of cases for binary search |
| 37 | |
| 38 | // An exprSwitch walks an expression switch. |
| 39 | type exprSwitch struct { |
| 40 | exprname *Node // node for the expression being switched on |
| 41 | kind int // kind of switch statement (switchKind*) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 42 | } |
| 43 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 44 | // A typeSwitch walks a type switch. |
| 45 | type typeSwitch struct { |
| 46 | hashname *Node // node for the hash of the type of the variable being switched on |
| 47 | facename *Node // node for the concrete type of the variable being switched on |
| 48 | okname *Node // boolean node used for comma-ok type assertions |
| 49 | } |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 50 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 51 | // A caseClause is a single case clause in a switch statement. |
| 52 | type caseClause struct { |
| 53 | node *Node // points at case statement |
| 54 | ordinal int // position in switch |
| 55 | hash uint32 // hash of a type switch |
| 56 | typ uint8 // type of case |
| 57 | } |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 58 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 59 | // typecheckswitch typechecks a switch statement. |
| 60 | func typecheckswitch(n *Node) { |
| 61 | lno := int(lineno) |
| 62 | typechecklist(n.Ninit, Etop) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 63 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 64 | var nilonly string |
| 65 | var top int |
| 66 | var t *Type |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 67 | |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 68 | if n.Left != nil && n.Left.Op == OTYPESW { |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 69 | // type switch |
| 70 | top = Etype |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 71 | typecheck(&n.Left.Right, Erv) |
| 72 | t = n.Left.Right.Type |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 73 | if t != nil && t.Etype != TINTER { |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 74 | Yyerror("cannot type switch on non-interface value %v", Nconv(n.Left.Right, obj.FmtLong)) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 75 | } |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 76 | } else { |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 77 | // expression switch |
| 78 | top = Erv |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 79 | if n.Left != nil { |
| 80 | typecheck(&n.Left, Erv) |
| 81 | defaultlit(&n.Left, nil) |
| 82 | t = n.Left.Type |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 83 | } else { |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 84 | t = Types[TBOOL] |
| 85 | } |
| 86 | if t != nil { |
| 87 | var badtype *Type |
| 88 | switch { |
Josh Bleecher Snyder | 25da594 | 2015-03-01 07:54:01 +0000 | [diff] [blame] | 89 | case !okforeq[t.Etype]: |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 90 | Yyerror("cannot switch on %v", Nconv(n.Left, obj.FmtLong)) |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 91 | case t.Etype == TARRAY && !Isfixedarray(t): |
| 92 | nilonly = "slice" |
| 93 | case t.Etype == TARRAY && Isfixedarray(t) && algtype1(t, nil) == ANOEQ: |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 94 | Yyerror("cannot switch on %v", Nconv(n.Left, obj.FmtLong)) |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 95 | case t.Etype == TSTRUCT && algtype1(t, &badtype) == ANOEQ: |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 96 | Yyerror("cannot switch on %v (struct containing %v cannot be compared)", Nconv(n.Left, obj.FmtLong), badtype) |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 97 | case t.Etype == TFUNC: |
| 98 | nilonly = "func" |
| 99 | case t.Etype == TMAP: |
| 100 | nilonly = "map" |
| 101 | } |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 102 | } |
| 103 | } |
| 104 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 105 | n.Type = t |
| 106 | |
| 107 | var def *Node |
| 108 | var ll *NodeList |
| 109 | for l := n.List; l != nil; l = l.Next { |
| 110 | ncase := l.N |
| 111 | setlineno(n) |
| 112 | if ncase.List == nil { |
| 113 | // default |
| 114 | if def != nil { |
| 115 | Yyerror("multiple defaults in switch (first at %v)", def.Line()) |
| 116 | } else { |
| 117 | def = ncase |
| 118 | } |
| 119 | } else { |
| 120 | for ll = ncase.List; ll != nil; ll = ll.Next { |
| 121 | setlineno(ll.N) |
| 122 | typecheck(&ll.N, Erv|Etype) |
| 123 | if ll.N.Type == nil || t == nil { |
| 124 | continue |
| 125 | } |
| 126 | setlineno(ncase) |
| 127 | switch top { |
| 128 | // expression switch |
| 129 | case Erv: |
| 130 | defaultlit(&ll.N, t) |
| 131 | switch { |
| 132 | case ll.N.Op == OTYPE: |
Russ Cox | 17228f4 | 2015-04-17 12:03:22 -0400 | [diff] [blame] | 133 | Yyerror("type %v is not an expression", ll.N.Type) |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 134 | case ll.N.Type != nil && assignop(ll.N.Type, t, nil) == 0 && assignop(t, ll.N.Type, nil) == 0: |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 135 | if n.Left != nil { |
| 136 | Yyerror("invalid case %v in switch on %v (mismatched types %v and %v)", ll.N, n.Left, ll.N.Type, t) |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 137 | } else { |
Russ Cox | 17228f4 | 2015-04-17 12:03:22 -0400 | [diff] [blame] | 138 | Yyerror("invalid case %v in switch (mismatched types %v and bool)", ll.N, ll.N.Type) |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 139 | } |
| 140 | case nilonly != "" && !Isconst(ll.N, CTNIL): |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 141 | Yyerror("invalid case %v in switch (can only compare %s %v to nil)", ll.N, nilonly, n.Left) |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 142 | } |
| 143 | |
| 144 | // type switch |
| 145 | case Etype: |
| 146 | var missing, have *Type |
| 147 | var ptr int |
| 148 | switch { |
| 149 | case ll.N.Op == OLITERAL && Istype(ll.N.Type, TNIL): |
| 150 | case ll.N.Op != OTYPE && ll.N.Type != nil: // should this be ||? |
| 151 | Yyerror("%v is not a type", Nconv(ll.N, obj.FmtLong)) |
| 152 | // reset to original type |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 153 | ll.N = n.Left.Right |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 154 | case ll.N.Type.Etype != TINTER && t.Etype == TINTER && !implements(ll.N.Type, t, &missing, &have, &ptr): |
Dave Cheney | 8937780 | 2015-09-07 10:37:26 +1000 | [diff] [blame] | 155 | if have != nil && !missing.Broke && !have.Broke { |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 156 | 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)) |
Dave Cheney | 8937780 | 2015-09-07 10:37:26 +1000 | [diff] [blame] | 157 | } else if !missing.Broke { |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 158 | 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) |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 159 | } |
| 160 | } |
| 161 | } |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | if top == Etype && n.Type != nil { |
| 166 | ll = ncase.List |
Russ Cox | da094f1 | 2015-05-27 10:43:53 -0400 | [diff] [blame] | 167 | if ncase.Rlist != nil { |
| 168 | nvar := ncase.Rlist.N |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 169 | if ll != nil && ll.Next == nil && ll.N.Type != nil && !Istype(ll.N.Type, TNIL) { |
| 170 | // single entry type switch |
Russ Cox | 3c3019a | 2015-05-27 00:44:05 -0400 | [diff] [blame] | 171 | nvar.Name.Param.Ntype = typenod(ll.N.Type) |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 172 | } else { |
| 173 | // multiple entry type switch or default |
Russ Cox | 3c3019a | 2015-05-27 00:44:05 -0400 | [diff] [blame] | 174 | nvar.Name.Param.Ntype = typenod(n.Type) |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 175 | } |
| 176 | |
| 177 | typecheck(&nvar, Erv|Easgn) |
Russ Cox | da094f1 | 2015-05-27 10:43:53 -0400 | [diff] [blame] | 178 | ncase.Rlist.N = nvar |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 179 | } |
| 180 | } |
| 181 | |
| 182 | typechecklist(ncase.Nbody, Etop) |
| 183 | } |
| 184 | |
| 185 | lineno = int32(lno) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 186 | } |
| 187 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 188 | // walkswitch walks a switch statement. |
| 189 | func walkswitch(sw *Node) { |
| 190 | // convert switch {...} to switch true {...} |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 191 | if sw.Left == nil { |
| 192 | sw.Left = Nodbool(true) |
| 193 | typecheck(&sw.Left, Erv) |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 194 | } |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 195 | |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 196 | if sw.Left.Op == OTYPESW { |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 197 | var s typeSwitch |
| 198 | s.walk(sw) |
| 199 | } else { |
| 200 | var s exprSwitch |
| 201 | s.walk(sw) |
| 202 | } |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 203 | } |
| 204 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 205 | // walk generates an AST implementing sw. |
| 206 | // sw is an expression switch. |
| 207 | // The AST is generally of the form of a linear |
| 208 | // search using if..goto, although binary search |
| 209 | // is used with long runs of constants. |
| 210 | func (s *exprSwitch) walk(sw *Node) { |
| 211 | casebody(sw, nil) |
| 212 | |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 213 | cond := sw.Left |
| 214 | sw.Left = nil |
| 215 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 216 | s.kind = switchKindExpr |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 217 | if Isconst(cond, CTBOOL) { |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 218 | s.kind = switchKindTrue |
Russ Cox | 81d5810 | 2015-05-27 00:47:05 -0400 | [diff] [blame] | 219 | if !cond.Val().U.(bool) { |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 220 | s.kind = switchKindFalse |
| 221 | } |
| 222 | } |
| 223 | |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 224 | walkexpr(&cond, &sw.Ninit) |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 225 | t := sw.Type |
| 226 | if t == nil { |
| 227 | return |
| 228 | } |
| 229 | |
| 230 | // convert the switch into OIF statements |
| 231 | var cas *NodeList |
| 232 | if s.kind == switchKindTrue || s.kind == switchKindFalse { |
| 233 | s.exprname = Nodbool(s.kind == switchKindTrue) |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 234 | } else if consttype(cond) >= 0 { |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 235 | // leave constants to enable dead code elimination (issue 9608) |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 236 | s.exprname = cond |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 237 | } else { |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 238 | s.exprname = temp(cond.Type) |
| 239 | cas = list1(Nod(OAS, s.exprname, cond)) |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 240 | typechecklist(cas, Etop) |
| 241 | } |
| 242 | |
| 243 | // enumerate the cases, and lop off the default case |
| 244 | cc := caseClauses(sw, s.kind) |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 245 | sw.List = nil |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 246 | var def *Node |
| 247 | if len(cc) > 0 && cc[0].typ == caseKindDefault { |
| 248 | def = cc[0].node.Right |
| 249 | cc = cc[1:] |
| 250 | } else { |
| 251 | def = Nod(OBREAK, nil, nil) |
| 252 | } |
| 253 | |
| 254 | // handle the cases in order |
| 255 | for len(cc) > 0 { |
| 256 | // deal with expressions one at a time |
Josh Bleecher Snyder | 25da594 | 2015-03-01 07:54:01 +0000 | [diff] [blame] | 257 | if !okforcmp[t.Etype] || cc[0].typ != caseKindExprConst { |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 258 | a := s.walkCases(cc[:1]) |
| 259 | cas = list(cas, a) |
| 260 | cc = cc[1:] |
| 261 | continue |
| 262 | } |
| 263 | |
| 264 | // do binary search on runs of constants |
| 265 | var run int |
| 266 | for run = 1; run < len(cc) && cc[run].typ == caseKindExprConst; run++ { |
| 267 | } |
| 268 | |
| 269 | // sort and compile constants |
| 270 | sort.Sort(caseClauseByExpr(cc[:run])) |
| 271 | a := s.walkCases(cc[:run]) |
| 272 | cas = list(cas, a) |
| 273 | cc = cc[run:] |
| 274 | } |
| 275 | |
| 276 | // handle default case |
| 277 | if nerrors == 0 { |
| 278 | cas = list(cas, def) |
| 279 | sw.Nbody = concat(cas, sw.Nbody) |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 280 | walkstmtlist(sw.Nbody) |
| 281 | } |
| 282 | } |
| 283 | |
| 284 | // walkCases generates an AST implementing the cases in cc. |
| 285 | func (s *exprSwitch) walkCases(cc []*caseClause) *Node { |
| 286 | if len(cc) < binarySearchMin { |
| 287 | // linear search |
| 288 | var cas *NodeList |
| 289 | for _, c := range cc { |
| 290 | n := c.node |
| 291 | lno := int(setlineno(n)) |
| 292 | |
| 293 | a := Nod(OIF, nil, nil) |
| 294 | 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 { |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 295 | a.Left = Nod(OEQ, s.exprname, n.Left) // if name == val |
| 296 | typecheck(&a.Left, Erv) |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 297 | } else if s.kind == switchKindTrue { |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 298 | a.Left = n.Left // if val |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 299 | } else { |
| 300 | // s.kind == switchKindFalse |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 301 | a.Left = Nod(ONOT, n.Left, nil) // if !val |
| 302 | typecheck(&a.Left, Erv) |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 303 | } |
| 304 | a.Nbody = list1(n.Right) // goto l |
| 305 | |
| 306 | cas = list(cas, a) |
| 307 | lineno = int32(lno) |
| 308 | } |
| 309 | return liststmt(cas) |
| 310 | } |
| 311 | |
| 312 | // find the middle and recur |
| 313 | half := len(cc) / 2 |
| 314 | a := Nod(OIF, nil, nil) |
Josh Bleecher Snyder | 4bc9bad | 2015-03-17 16:10:31 -0700 | [diff] [blame] | 315 | mid := cc[half-1].node.Left |
| 316 | le := Nod(OLE, s.exprname, mid) |
| 317 | if Isconst(mid, CTSTR) { |
| 318 | // Search by length and then by value; see exprcmp. |
| 319 | lenlt := Nod(OLT, Nod(OLEN, s.exprname, nil), Nod(OLEN, mid, nil)) |
| 320 | leneq := Nod(OEQ, Nod(OLEN, s.exprname, nil), Nod(OLEN, mid, nil)) |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 321 | a.Left = Nod(OOROR, lenlt, Nod(OANDAND, leneq, le)) |
Josh Bleecher Snyder | 4bc9bad | 2015-03-17 16:10:31 -0700 | [diff] [blame] | 322 | } else { |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 323 | a.Left = le |
Josh Bleecher Snyder | 4bc9bad | 2015-03-17 16:10:31 -0700 | [diff] [blame] | 324 | } |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 325 | typecheck(&a.Left, Erv) |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 326 | a.Nbody = list1(s.walkCases(cc[:half])) |
Russ Cox | ffef180 | 2015-05-22 01:16:52 -0400 | [diff] [blame] | 327 | a.Rlist = list1(s.walkCases(cc[half:])) |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 328 | return a |
| 329 | } |
| 330 | |
| 331 | // casebody builds separate lists of statements and cases. |
| 332 | // It makes labels between cases and statements |
| 333 | // and deals with fallthrough, break, and unreachable statements. |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 334 | func casebody(sw *Node, typeswvar *Node) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 335 | if sw.List == nil { |
| 336 | return |
| 337 | } |
| 338 | |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 339 | lno := setlineno(sw) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 340 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 341 | var cas *NodeList // cases |
| 342 | var stat *NodeList // statements |
| 343 | var def *Node // defaults |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 344 | br := Nod(OBREAK, nil, nil) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 345 | |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 346 | for l := sw.List; l != nil; l = l.Next { |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 347 | n := l.N |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 348 | setlineno(n) |
| 349 | if n.Op != OXCASE { |
Håvard Haugen | 3c9fa38 | 2015-08-30 23:10:03 +0200 | [diff] [blame] | 350 | Fatalf("casebody %v", Oconv(int(n.Op), 0)) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 351 | } |
| 352 | n.Op = OCASE |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 353 | needvar := count(n.List) != 1 || n.List.N.Op == OLITERAL |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 354 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 355 | jmp := Nod(OGOTO, newCaseLabel(), nil) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 356 | if n.List == nil { |
| 357 | if def != nil { |
| 358 | Yyerror("more than one default case") |
| 359 | } |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 360 | // reuse original default case |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 361 | n.Right = jmp |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 362 | def = n |
| 363 | } |
| 364 | |
| 365 | if n.List != nil && n.List.Next == nil { |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 366 | // one case -- reuse OCASE node |
| 367 | n.Left = n.List.N |
| 368 | n.Right = jmp |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 369 | n.List = nil |
| 370 | cas = list(cas, n) |
| 371 | } else { |
| 372 | // expand multi-valued cases |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 373 | for lc := n.List; lc != nil; lc = lc.Next { |
| 374 | cas = list(cas, Nod(OCASE, lc.N, jmp)) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 375 | } |
| 376 | } |
| 377 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 378 | stat = list(stat, Nod(OLABEL, jmp.Left, nil)) |
Russ Cox | da094f1 | 2015-05-27 10:43:53 -0400 | [diff] [blame] | 379 | if typeswvar != nil && needvar && n.Rlist != nil { |
| 380 | l := list1(Nod(ODCL, n.Rlist.N, nil)) |
| 381 | l = list(l, Nod(OAS, n.Rlist.N, typeswvar)) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 382 | typechecklist(l, Etop) |
| 383 | stat = concat(stat, l) |
| 384 | } |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 385 | stat = concat(stat, n.Nbody) |
| 386 | |
| 387 | // botch - shouldn't fall thru declaration |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 388 | last := stat.End.N |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 389 | if last.Xoffset == n.Xoffset && last.Op == OXFALL { |
| 390 | if typeswvar != nil { |
| 391 | setlineno(last) |
| 392 | Yyerror("cannot fallthrough in type switch") |
| 393 | } |
| 394 | |
| 395 | if l.Next == nil { |
| 396 | setlineno(last) |
| 397 | Yyerror("cannot fallthrough final case in switch") |
| 398 | } |
| 399 | |
| 400 | last.Op = OFALL |
| 401 | } else { |
| 402 | stat = list(stat, br) |
| 403 | } |
| 404 | } |
| 405 | |
| 406 | stat = list(stat, br) |
| 407 | if def != nil { |
| 408 | cas = list(cas, def) |
| 409 | } |
| 410 | |
| 411 | sw.List = cas |
| 412 | sw.Nbody = stat |
| 413 | lineno = lno |
| 414 | } |
| 415 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 416 | // nSwitchLabel is the number of switch labels generated. |
| 417 | // This should be per-function, but it is a global counter for now. |
| 418 | var nSwitchLabel int |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 419 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 420 | func newCaseLabel() *Node { |
| 421 | label := strconv.Itoa(nSwitchLabel) |
| 422 | nSwitchLabel++ |
| 423 | return newname(Lookup(label)) |
| 424 | } |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 425 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 426 | // caseClauses generates a slice of caseClauses |
| 427 | // corresponding to the clauses in the switch statement sw. |
| 428 | // Kind is the kind of switch statement. |
| 429 | func caseClauses(sw *Node, kind int) []*caseClause { |
| 430 | var cc []*caseClause |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 431 | for l := sw.List; l != nil; l = l.Next { |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 432 | n := l.N |
| 433 | c := new(caseClause) |
| 434 | cc = append(cc, c) |
| 435 | c.ordinal = len(cc) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 436 | c.node = n |
| 437 | |
| 438 | if n.Left == nil { |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 439 | c.typ = caseKindDefault |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 440 | continue |
| 441 | } |
| 442 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 443 | if kind == switchKindType { |
| 444 | // type switch |
| 445 | switch { |
| 446 | case n.Left.Op == OLITERAL: |
| 447 | c.typ = caseKindTypeNil |
| 448 | case Istype(n.Left.Type, TINTER): |
| 449 | c.typ = caseKindTypeVar |
| 450 | default: |
| 451 | c.typ = caseKindTypeConst |
| 452 | c.hash = typehash(n.Left.Type) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 453 | } |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 454 | } else { |
| 455 | // expression switch |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 456 | switch consttype(n.Left) { |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 457 | case CTFLT, CTINT, CTRUNE, CTSTR: |
| 458 | c.typ = caseKindExprConst |
| 459 | default: |
| 460 | c.typ = caseKindExprVar |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 461 | } |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 462 | } |
| 463 | } |
| 464 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 465 | if cc == nil { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 466 | return nil |
| 467 | } |
| 468 | |
| 469 | // sort by value and diagnose duplicate cases |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 470 | if kind == switchKindType { |
| 471 | // type switch |
| 472 | sort.Sort(caseClauseByType(cc)) |
| 473 | for i, c1 := range cc { |
| 474 | if c1.typ == caseKindTypeNil || c1.typ == caseKindDefault { |
| 475 | break |
| 476 | } |
| 477 | for _, c2 := range cc[i+1:] { |
| 478 | if c2.typ == caseKindTypeNil || c2.typ == caseKindDefault || c1.hash != c2.hash { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 479 | break |
| 480 | } |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 481 | if Eqtype(c1.node.Left.Type, c2.node.Left.Type) { |
Russ Cox | 17228f4 | 2015-04-17 12:03:22 -0400 | [diff] [blame] | 482 | yyerrorl(int(c2.node.Lineno), "duplicate case %v in type switch\n\tprevious case at %v", c2.node.Left.Type, c1.node.Line()) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 483 | } |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 484 | } |
| 485 | } |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 486 | } else { |
| 487 | // expression switch |
| 488 | sort.Sort(caseClauseByExpr(cc)) |
| 489 | for i, c1 := range cc { |
| 490 | if i+1 == len(cc) { |
| 491 | break |
| 492 | } |
| 493 | c2 := cc[i+1] |
| 494 | if exprcmp(c1, c2) != 0 { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 495 | continue |
| 496 | } |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 497 | setlineno(c2.node) |
Russ Cox | 17228f4 | 2015-04-17 12:03:22 -0400 | [diff] [blame] | 498 | Yyerror("duplicate case %v in switch\n\tprevious case at %v", c1.node.Left, c1.node.Line()) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 499 | } |
| 500 | } |
| 501 | |
| 502 | // put list back in processing order |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 503 | sort.Sort(caseClauseByOrd(cc)) |
| 504 | return cc |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 505 | } |
| 506 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 507 | // walk generates an AST that implements sw, |
| 508 | // where sw is a type switch. |
| 509 | // The AST is generally of the form of a linear |
| 510 | // search using if..goto, although binary search |
| 511 | // is used with long runs of concrete types. |
| 512 | func (s *typeSwitch) walk(sw *Node) { |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 513 | cond := sw.Left |
| 514 | sw.Left = nil |
| 515 | |
| 516 | if cond == nil { |
| 517 | sw.List = nil |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 518 | return |
| 519 | } |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 520 | if cond.Right == nil { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 521 | setlineno(sw) |
| 522 | Yyerror("type switch must have an assignment") |
| 523 | return |
| 524 | } |
| 525 | |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 526 | walkexpr(&cond.Right, &sw.Ninit) |
| 527 | if !Istype(cond.Right.Type, TINTER) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 528 | Yyerror("type switch must be on an interface") |
| 529 | return |
| 530 | } |
| 531 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 532 | var cas *NodeList |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 533 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 534 | // predeclare temporary variables and the boolean var |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 535 | s.facename = temp(cond.Right.Type) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 536 | |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 537 | a := Nod(OAS, s.facename, cond.Right) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 538 | typecheck(&a, Etop) |
| 539 | cas = list(cas, a) |
| 540 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 541 | s.okname = temp(Types[TBOOL]) |
| 542 | typecheck(&s.okname, Erv) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 543 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 544 | s.hashname = temp(Types[TUINT32]) |
| 545 | typecheck(&s.hashname, Erv) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 546 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 547 | // set up labels and jumps |
| 548 | casebody(sw, s.facename) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 549 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 550 | // calculate type hash |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 551 | t := cond.Right.Type |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 552 | if isnilinter(t) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 553 | a = syslook("efacethash", 1) |
| 554 | } else { |
| 555 | a = syslook("ifacethash", 1) |
| 556 | } |
Russ Cox | 13f9c8b | 2015-03-08 13:33:49 -0400 | [diff] [blame] | 557 | substArgTypes(a, t) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 558 | a = Nod(OCALL, a, nil) |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 559 | a.List = list1(s.facename) |
| 560 | a = Nod(OAS, s.hashname, a) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 561 | typecheck(&a, Etop) |
| 562 | cas = list(cas, a) |
| 563 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 564 | cc := caseClauses(sw, switchKindType) |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 565 | sw.List = nil |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 566 | var def *Node |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 567 | if len(cc) > 0 && cc[0].typ == caseKindDefault { |
| 568 | def = cc[0].node.Right |
| 569 | cc = cc[1:] |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 570 | } else { |
| 571 | def = Nod(OBREAK, nil, nil) |
| 572 | } |
| 573 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 574 | // insert type equality check into each case block |
| 575 | for _, c := range cc { |
| 576 | n := c.node |
| 577 | switch c.typ { |
| 578 | case caseKindTypeNil: |
| 579 | var v Val |
Russ Cox | 71080fb | 2015-05-26 22:50:45 -0400 | [diff] [blame] | 580 | v.U = new(NilVal) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 581 | a = Nod(OIF, nil, nil) |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 582 | a.Left = Nod(OEQ, s.facename, nodlit(v)) |
| 583 | typecheck(&a.Left, Erv) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 584 | a.Nbody = list1(n.Right) // if i==nil { goto l } |
| 585 | n.Right = a |
| 586 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 587 | case caseKindTypeVar, caseKindTypeConst: |
| 588 | n.Right = s.typeone(n) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 589 | } |
| 590 | } |
| 591 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 592 | // generate list of if statements, binary search for constant sequences |
| 593 | for len(cc) > 0 { |
| 594 | if cc[0].typ != caseKindTypeConst { |
| 595 | n := cc[0].node |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 596 | cas = list(cas, n.Right) |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 597 | cc = cc[1:] |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 598 | continue |
| 599 | } |
| 600 | |
| 601 | // identify run of constants |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 602 | var run int |
| 603 | for run = 1; run < len(cc) && cc[run].typ == caseKindTypeConst; run++ { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 604 | } |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 605 | |
| 606 | // sort by hash |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 607 | sort.Sort(caseClauseByType(cc[:run])) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 608 | |
| 609 | // for debugging: linear search |
| 610 | if false { |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 611 | for i := 0; i < run; i++ { |
| 612 | n := cc[i].node |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 613 | cas = list(cas, n.Right) |
| 614 | } |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 615 | continue |
| 616 | } |
| 617 | |
| 618 | // combine adjacent cases with the same hash |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 619 | ncase := 0 |
| 620 | for i := 0; i < run; i++ { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 621 | ncase++ |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 622 | hash := list1(cc[i].node.Right) |
| 623 | for j := i + 1; j < run && cc[i].hash == cc[j].hash; j++ { |
| 624 | hash = list(hash, cc[j].node.Right) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 625 | } |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 626 | cc[i].node.Right = liststmt(hash) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 627 | } |
| 628 | |
| 629 | // binary search among cases to narrow by hash |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 630 | cas = list(cas, s.walkCases(cc[:ncase])) |
| 631 | cc = cc[ncase:] |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 632 | } |
| 633 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 634 | // handle default case |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 635 | if nerrors == 0 { |
| 636 | cas = list(cas, def) |
| 637 | sw.Nbody = concat(cas, sw.Nbody) |
| 638 | sw.List = nil |
| 639 | walkstmtlist(sw.Nbody) |
| 640 | } |
| 641 | } |
| 642 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 643 | // typeone generates an AST that jumps to the |
| 644 | // case body if the variable is of type t. |
| 645 | func (s *typeSwitch) typeone(t *Node) *Node { |
Russ Cox | da094f1 | 2015-05-27 10:43:53 -0400 | [diff] [blame] | 646 | var name *Node |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 647 | var init *NodeList |
Russ Cox | da094f1 | 2015-05-27 10:43:53 -0400 | [diff] [blame] | 648 | if t.Rlist == nil { |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 649 | name = nblank |
Russ Cox | da094f1 | 2015-05-27 10:43:53 -0400 | [diff] [blame] | 650 | typecheck(&nblank, Erv|Easgn) |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 651 | } else { |
Russ Cox | da094f1 | 2015-05-27 10:43:53 -0400 | [diff] [blame] | 652 | name = t.Rlist.N |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 653 | init = list1(Nod(ODCL, name, nil)) |
Russ Cox | c4092ac | 2015-07-30 00:46:42 -0400 | [diff] [blame] | 654 | a := Nod(OAS, name, nil) |
| 655 | typecheck(&a, Etop) |
| 656 | init = list(init, a) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 657 | } |
| 658 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 659 | a := Nod(OAS2, nil, nil) |
| 660 | a.List = list(list1(name), s.okname) // name, ok = |
| 661 | b := Nod(ODOTTYPE, s.facename, nil) |
| 662 | b.Type = t.Left.Type // interface.(type) |
| 663 | a.Rlist = list1(b) |
| 664 | typecheck(&a, Etop) |
| 665 | init = list(init, a) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 666 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 667 | c := Nod(OIF, nil, nil) |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 668 | c.Left = s.okname |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 669 | c.Nbody = list1(t.Right) // if ok { goto l } |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 670 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 671 | return liststmt(list(init, c)) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 672 | } |
| 673 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 674 | // walkCases generates an AST implementing the cases in cc. |
| 675 | func (s *typeSwitch) walkCases(cc []*caseClause) *Node { |
| 676 | if len(cc) < binarySearchMin { |
| 677 | var cas *NodeList |
| 678 | for _, c := range cc { |
| 679 | n := c.node |
| 680 | if c.typ != caseKindTypeConst { |
Håvard Haugen | 3c9fa38 | 2015-08-30 23:10:03 +0200 | [diff] [blame] | 681 | Fatalf("typeSwitch walkCases") |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 682 | } |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 683 | a := Nod(OIF, nil, nil) |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 684 | a.Left = Nod(OEQ, s.hashname, Nodintconst(int64(c.hash))) |
| 685 | typecheck(&a.Left, Erv) |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 686 | a.Nbody = list1(n.Right) |
| 687 | cas = list(cas, a) |
| 688 | } |
| 689 | return liststmt(cas) |
| 690 | } |
| 691 | |
| 692 | // find the middle and recur |
| 693 | half := len(cc) / 2 |
| 694 | a := Nod(OIF, nil, nil) |
Russ Cox | 66be148 | 2015-05-26 21:30:20 -0400 | [diff] [blame] | 695 | a.Left = Nod(OLE, s.hashname, Nodintconst(int64(cc[half-1].hash))) |
| 696 | typecheck(&a.Left, Erv) |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 697 | a.Nbody = list1(s.walkCases(cc[:half])) |
Russ Cox | ffef180 | 2015-05-22 01:16:52 -0400 | [diff] [blame] | 698 | a.Rlist = list1(s.walkCases(cc[half:])) |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 699 | return a |
| 700 | } |
| 701 | |
| 702 | type caseClauseByOrd []*caseClause |
| 703 | |
| 704 | func (x caseClauseByOrd) Len() int { return len(x) } |
| 705 | func (x caseClauseByOrd) Swap(i, j int) { x[i], x[j] = x[j], x[i] } |
| 706 | func (x caseClauseByOrd) Less(i, j int) bool { |
| 707 | c1, c2 := x[i], x[j] |
| 708 | switch { |
| 709 | // sort default first |
| 710 | case c1.typ == caseKindDefault: |
| 711 | return true |
| 712 | case c2.typ == caseKindDefault: |
| 713 | return false |
| 714 | |
| 715 | // sort nil second |
| 716 | case c1.typ == caseKindTypeNil: |
| 717 | return true |
| 718 | case c2.typ == caseKindTypeNil: |
| 719 | return false |
| 720 | } |
| 721 | |
| 722 | // sort by ordinal |
| 723 | return c1.ordinal < c2.ordinal |
| 724 | } |
| 725 | |
| 726 | type caseClauseByExpr []*caseClause |
| 727 | |
| 728 | func (x caseClauseByExpr) Len() int { return len(x) } |
| 729 | func (x caseClauseByExpr) Swap(i, j int) { x[i], x[j] = x[j], x[i] } |
| 730 | func (x caseClauseByExpr) Less(i, j int) bool { |
| 731 | return exprcmp(x[i], x[j]) < 0 |
| 732 | } |
| 733 | |
| 734 | func exprcmp(c1, c2 *caseClause) int { |
| 735 | // sort non-constants last |
| 736 | if c1.typ != caseKindExprConst { |
| 737 | return +1 |
| 738 | } |
| 739 | if c2.typ != caseKindExprConst { |
| 740 | return -1 |
| 741 | } |
| 742 | |
| 743 | n1 := c1.node.Left |
| 744 | n2 := c2.node.Left |
| 745 | |
| 746 | // sort by type (for switches on interface) |
Russ Cox | 81d5810 | 2015-05-27 00:47:05 -0400 | [diff] [blame] | 747 | ct := int(n1.Val().Ctype()) |
| 748 | if ct > int(n2.Val().Ctype()) { |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 749 | return +1 |
| 750 | } |
Russ Cox | 81d5810 | 2015-05-27 00:47:05 -0400 | [diff] [blame] | 751 | if ct < int(n2.Val().Ctype()) { |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 752 | return -1 |
| 753 | } |
| 754 | if !Eqtype(n1.Type, n2.Type) { |
| 755 | if n1.Type.Vargen > n2.Type.Vargen { |
| 756 | return +1 |
| 757 | } else { |
| 758 | return -1 |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 759 | } |
| 760 | } |
| 761 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 762 | // sort by constant value to enable binary search |
| 763 | switch ct { |
| 764 | case CTFLT: |
Russ Cox | 81d5810 | 2015-05-27 00:47:05 -0400 | [diff] [blame] | 765 | return mpcmpfltflt(n1.Val().U.(*Mpflt), n2.Val().U.(*Mpflt)) |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 766 | case CTINT, CTRUNE: |
Russ Cox | 81d5810 | 2015-05-27 00:47:05 -0400 | [diff] [blame] | 767 | return Mpcmpfixfix(n1.Val().U.(*Mpint), n2.Val().U.(*Mpint)) |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 768 | case CTSTR: |
Josh Bleecher Snyder | 4bc9bad | 2015-03-17 16:10:31 -0700 | [diff] [blame] | 769 | // Sort strings by length and then by value. |
| 770 | // It is much cheaper to compare lengths than values, |
| 771 | // and all we need here is consistency. |
| 772 | // We respect this sorting in exprSwitch.walkCases. |
Russ Cox | 81d5810 | 2015-05-27 00:47:05 -0400 | [diff] [blame] | 773 | a := n1.Val().U.(string) |
| 774 | b := n2.Val().U.(string) |
Josh Bleecher Snyder | 4bc9bad | 2015-03-17 16:10:31 -0700 | [diff] [blame] | 775 | if len(a) < len(b) { |
| 776 | return -1 |
| 777 | } |
| 778 | if len(a) > len(b) { |
| 779 | return +1 |
| 780 | } |
Håvard Haugen | c73df92 | 2015-09-23 23:31:17 +0200 | [diff] [blame] | 781 | if a == b { |
| 782 | return 0 |
| 783 | } |
| 784 | if a < b { |
| 785 | return -1 |
| 786 | } |
| 787 | return +1 |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 788 | } |
| 789 | |
Josh Bleecher Snyder | 85c6f71 | 2015-02-27 20:44:45 +0000 | [diff] [blame] | 790 | return 0 |
| 791 | } |
| 792 | |
| 793 | type caseClauseByType []*caseClause |
| 794 | |
| 795 | func (x caseClauseByType) Len() int { return len(x) } |
| 796 | func (x caseClauseByType) Swap(i, j int) { x[i], x[j] = x[j], x[i] } |
| 797 | func (x caseClauseByType) Less(i, j int) bool { |
| 798 | c1, c2 := x[i], x[j] |
| 799 | switch { |
| 800 | // sort non-constants last |
| 801 | case c1.typ != caseKindTypeConst: |
| 802 | return false |
| 803 | case c2.typ != caseKindTypeConst: |
| 804 | return true |
| 805 | |
| 806 | // sort by hash code |
| 807 | case c1.hash != c2.hash: |
| 808 | return c1.hash < c2.hash |
| 809 | } |
| 810 | |
| 811 | // sort by ordinal |
| 812 | return c1.ordinal < c2.ordinal |
| 813 | } |