github.com/q45/go@v0.0.0-20151101211701-a4fb8c13db3f/src/cmd/compile/internal/gc/range.go (about) 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 "cmd/internal/obj" 8 9 // range 10 func typecheckrange(n *Node) { 11 var toomany int 12 var why string 13 var t1 *Type 14 var t2 *Type 15 var v1 *Node 16 var v2 *Node 17 18 // Typechecking order is important here: 19 // 0. first typecheck range expression (slice/map/chan), 20 // it is evaluated only once and so logically it is not part of the loop. 21 // 1. typcheck produced values, 22 // this part can declare new vars and so it must be typechecked before body, 23 // because body can contain a closure that captures the vars. 24 // 2. decldepth++ to denote loop body. 25 // 3. typecheck body. 26 // 4. decldepth--. 27 28 typecheck(&n.Right, Erv) 29 30 t := n.Right.Type 31 if t == nil { 32 goto out 33 } 34 35 // delicate little dance. see typecheckas2 36 for ll := n.List; ll != nil; ll = ll.Next { 37 if ll.N.Name == nil || ll.N.Name.Defn != n { 38 typecheck(&ll.N, Erv|Easgn) 39 } 40 } 41 42 if Isptr[t.Etype] && Isfixedarray(t.Type) { 43 t = t.Type 44 } 45 n.Type = t 46 47 toomany = 0 48 switch t.Etype { 49 default: 50 Yyerror("cannot range over %v", Nconv(n.Right, obj.FmtLong)) 51 goto out 52 53 case TARRAY: 54 t1 = Types[TINT] 55 t2 = t.Type 56 57 case TMAP: 58 t1 = t.Down 59 t2 = t.Type 60 61 case TCHAN: 62 if t.Chan&Crecv == 0 { 63 Yyerror("invalid operation: range %v (receive from send-only type %v)", n.Right, n.Right.Type) 64 goto out 65 } 66 67 t1 = t.Type 68 t2 = nil 69 if count(n.List) == 2 { 70 toomany = 1 71 } 72 73 case TSTRING: 74 t1 = Types[TINT] 75 t2 = runetype 76 } 77 78 if count(n.List) > 2 || toomany != 0 { 79 Yyerror("too many variables in range") 80 } 81 82 v1 = nil 83 if n.List != nil { 84 v1 = n.List.N 85 } 86 v2 = nil 87 if n.List != nil && n.List.Next != nil { 88 v2 = n.List.Next.N 89 } 90 91 // this is not only a optimization but also a requirement in the spec. 92 // "if the second iteration variable is the blank identifier, the range 93 // clause is equivalent to the same clause with only the first variable 94 // present." 95 if isblank(v2) { 96 if v1 != nil { 97 n.List = list1(v1) 98 } 99 v2 = nil 100 } 101 102 if v1 != nil { 103 if v1.Name != nil && v1.Name.Defn == n { 104 v1.Type = t1 105 } else if v1.Type != nil && assignop(t1, v1.Type, &why) == 0 { 106 Yyerror("cannot assign type %v to %v in range%s", t1, Nconv(v1, obj.FmtLong), why) 107 } 108 checkassign(n, v1) 109 } 110 111 if v2 != nil { 112 if v2.Name != nil && v2.Name.Defn == n { 113 v2.Type = t2 114 } else if v2.Type != nil && assignop(t2, v2.Type, &why) == 0 { 115 Yyerror("cannot assign type %v to %v in range%s", t2, Nconv(v2, obj.FmtLong), why) 116 } 117 checkassign(n, v2) 118 } 119 120 // second half of dance 121 out: 122 n.Typecheck = 1 123 124 for ll := n.List; ll != nil; ll = ll.Next { 125 if ll.N.Typecheck == 0 { 126 typecheck(&ll.N, Erv|Easgn) 127 } 128 } 129 130 decldepth++ 131 typechecklist(n.Nbody, Etop) 132 decldepth-- 133 } 134 135 func walkrange(n *Node) { 136 // variable name conventions: 137 // ohv1, hv1, hv2: hidden (old) val 1, 2 138 // ha, hit: hidden aggregate, iterator 139 // hn, hp: hidden len, pointer 140 // hb: hidden bool 141 // a, v1, v2: not hidden aggregate, val 1, 2 142 143 t := n.Type 144 145 a := n.Right 146 lno := int(setlineno(a)) 147 n.Right = nil 148 149 var v1 *Node 150 if n.List != nil { 151 v1 = n.List.N 152 } 153 var v2 *Node 154 if n.List != nil && n.List.Next != nil && !isblank(n.List.Next.N) { 155 v2 = n.List.Next.N 156 } 157 158 // n->list has no meaning anymore, clear it 159 // to avoid erroneous processing by racewalk. 160 n.List = nil 161 162 var body *NodeList 163 var init *NodeList 164 switch t.Etype { 165 default: 166 Fatalf("walkrange") 167 168 case TARRAY: 169 if memclrrange(n, v1, v2, a) { 170 lineno = int32(lno) 171 return 172 } 173 174 // orderstmt arranged for a copy of the array/slice variable if needed. 175 ha := a 176 177 hv1 := temp(Types[TINT]) 178 hn := temp(Types[TINT]) 179 var hp *Node 180 181 init = list(init, Nod(OAS, hv1, nil)) 182 init = list(init, Nod(OAS, hn, Nod(OLEN, ha, nil))) 183 if v2 != nil { 184 hp = temp(Ptrto(n.Type.Type)) 185 tmp := Nod(OINDEX, ha, Nodintconst(0)) 186 tmp.Bounded = true 187 init = list(init, Nod(OAS, hp, Nod(OADDR, tmp, nil))) 188 } 189 190 n.Left = Nod(OLT, hv1, hn) 191 n.Right = Nod(OAS, hv1, Nod(OADD, hv1, Nodintconst(1))) 192 if v1 == nil { 193 body = nil 194 } else if v2 == nil { 195 body = list1(Nod(OAS, v1, hv1)) 196 } else { 197 a := Nod(OAS2, nil, nil) 198 a.List = list(list1(v1), v2) 199 a.Rlist = list(list1(hv1), Nod(OIND, hp, nil)) 200 body = list1(a) 201 202 // Advance pointer as part of increment. 203 // We used to advance the pointer before executing the loop body, 204 // but doing so would make the pointer point past the end of the 205 // array during the final iteration, possibly causing another unrelated 206 // piece of memory not to be garbage collected until the loop finished. 207 // Advancing during the increment ensures that the pointer p only points 208 // pass the end of the array during the final "p++; i++; if(i >= len(x)) break;", 209 // after which p is dead, so it cannot confuse the collector. 210 tmp := Nod(OADD, hp, Nodintconst(t.Type.Width)) 211 212 tmp.Type = hp.Type 213 tmp.Typecheck = 1 214 tmp.Right.Type = Types[Tptr] 215 tmp.Right.Typecheck = 1 216 a = Nod(OAS, hp, tmp) 217 typecheck(&a, Etop) 218 n.Right.Ninit = list1(a) 219 } 220 221 // orderstmt allocated the iterator for us. 222 // we only use a once, so no copy needed. 223 case TMAP: 224 ha := a 225 226 th := hiter(t) 227 hit := prealloc[n] 228 hit.Type = th 229 n.Left = nil 230 keyname := newname(th.Type.Sym) // depends on layout of iterator struct. See reflect.go:hiter 231 valname := newname(th.Type.Down.Sym) // ditto 232 233 fn := syslook("mapiterinit", 1) 234 235 substArgTypes(fn, t.Down, t.Type, th) 236 init = list(init, mkcall1(fn, nil, nil, typename(t), ha, Nod(OADDR, hit, nil))) 237 n.Left = Nod(ONE, Nod(ODOT, hit, keyname), nodnil()) 238 239 fn = syslook("mapiternext", 1) 240 substArgTypes(fn, th) 241 n.Right = mkcall1(fn, nil, nil, Nod(OADDR, hit, nil)) 242 243 key := Nod(ODOT, hit, keyname) 244 key = Nod(OIND, key, nil) 245 if v1 == nil { 246 body = nil 247 } else if v2 == nil { 248 body = list1(Nod(OAS, v1, key)) 249 } else { 250 val := Nod(ODOT, hit, valname) 251 val = Nod(OIND, val, nil) 252 a := Nod(OAS2, nil, nil) 253 a.List = list(list1(v1), v2) 254 a.Rlist = list(list1(key), val) 255 body = list1(a) 256 } 257 258 // orderstmt arranged for a copy of the channel variable. 259 case TCHAN: 260 ha := a 261 262 n.Left = nil 263 264 hv1 := temp(t.Type) 265 hv1.Typecheck = 1 266 if haspointers(t.Type) { 267 init = list(init, Nod(OAS, hv1, nil)) 268 } 269 hb := temp(Types[TBOOL]) 270 271 n.Left = Nod(ONE, hb, Nodbool(false)) 272 a := Nod(OAS2RECV, nil, nil) 273 a.Typecheck = 1 274 a.List = list(list1(hv1), hb) 275 a.Rlist = list1(Nod(ORECV, ha, nil)) 276 n.Left.Ninit = list1(a) 277 if v1 == nil { 278 body = nil 279 } else { 280 body = list1(Nod(OAS, v1, hv1)) 281 } 282 283 // orderstmt arranged for a copy of the string variable. 284 case TSTRING: 285 ha := a 286 287 ohv1 := temp(Types[TINT]) 288 289 hv1 := temp(Types[TINT]) 290 init = list(init, Nod(OAS, hv1, nil)) 291 292 var a *Node 293 var hv2 *Node 294 if v2 == nil { 295 a = Nod(OAS, hv1, mkcall("stringiter", Types[TINT], nil, ha, hv1)) 296 } else { 297 hv2 = temp(runetype) 298 a = Nod(OAS2, nil, nil) 299 a.List = list(list1(hv1), hv2) 300 fn := syslook("stringiter2", 0) 301 a.Rlist = list1(mkcall1(fn, getoutargx(fn.Type), nil, ha, hv1)) 302 } 303 304 n.Left = Nod(ONE, hv1, Nodintconst(0)) 305 n.Left.Ninit = list(list1(Nod(OAS, ohv1, hv1)), a) 306 307 body = nil 308 if v1 != nil { 309 body = list1(Nod(OAS, v1, ohv1)) 310 } 311 if v2 != nil { 312 body = list(body, Nod(OAS, v2, hv2)) 313 } 314 } 315 316 n.Op = OFOR 317 typechecklist(init, Etop) 318 n.Ninit = concat(n.Ninit, init) 319 typechecklist(n.Left.Ninit, Etop) 320 typecheck(&n.Left, Erv) 321 typecheck(&n.Right, Etop) 322 typechecklist(body, Etop) 323 n.Nbody = concat(body, n.Nbody) 324 walkstmt(&n) 325 326 lineno = int32(lno) 327 } 328 329 // Lower n into runtime·memclr if possible, for 330 // fast zeroing of slices and arrays (issue 5373). 331 // Look for instances of 332 // 333 // for i := range a { 334 // a[i] = zero 335 // } 336 // 337 // in which the evaluation of a is side-effect-free. 338 // 339 // Parameters are as in walkrange: "for v1, v2 = range a". 340 func memclrrange(n, v1, v2, a *Node) bool { 341 if Debug['N'] != 0 || instrumenting { 342 return false 343 } 344 if v1 == nil || v2 != nil { 345 return false 346 } 347 if n.Nbody == nil || n.Nbody.N == nil || n.Nbody.Next != nil { 348 return false 349 } 350 stmt := n.Nbody.N // only stmt in body 351 if stmt.Op != OAS || stmt.Left.Op != OINDEX { 352 return false 353 } 354 if !samesafeexpr(stmt.Left.Left, a) || !samesafeexpr(stmt.Left.Right, v1) { 355 return false 356 } 357 elemsize := n.Type.Type.Width 358 if elemsize <= 0 || !iszero(stmt.Right) { 359 return false 360 } 361 362 // Convert to 363 // if len(a) != 0 { 364 // hp = &a[0] 365 // hn = len(a)*sizeof(elem(a)) 366 // memclr(hp, hn) 367 // i = len(a) - 1 368 // } 369 n.Op = OIF 370 371 n.Nbody = nil 372 n.Left = Nod(ONE, Nod(OLEN, a, nil), Nodintconst(0)) 373 374 // hp = &a[0] 375 hp := temp(Ptrto(Types[TUINT8])) 376 377 tmp := Nod(OINDEX, a, Nodintconst(0)) 378 tmp.Bounded = true 379 tmp = Nod(OADDR, tmp, nil) 380 tmp = Nod(OCONVNOP, tmp, nil) 381 tmp.Type = Ptrto(Types[TUINT8]) 382 n.Nbody = list(n.Nbody, Nod(OAS, hp, tmp)) 383 384 // hn = len(a) * sizeof(elem(a)) 385 hn := temp(Types[TUINTPTR]) 386 387 tmp = Nod(OLEN, a, nil) 388 tmp = Nod(OMUL, tmp, Nodintconst(elemsize)) 389 tmp = conv(tmp, Types[TUINTPTR]) 390 n.Nbody = list(n.Nbody, Nod(OAS, hn, tmp)) 391 392 // memclr(hp, hn) 393 fn := mkcall("memclr", nil, nil, hp, hn) 394 395 n.Nbody = list(n.Nbody, fn) 396 397 // i = len(a) - 1 398 v1 = Nod(OAS, v1, Nod(OSUB, Nod(OLEN, a, nil), Nodintconst(1))) 399 400 n.Nbody = list(n.Nbody, v1) 401 402 typecheck(&n.Left, Erv) 403 typechecklist(n.Nbody, Etop) 404 walkstmt(&n) 405 return true 406 }