| // 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" |
| |
| /* |
| * range |
| */ |
| func typecheckrange(n *Node) { |
| var toomany int |
| var why string |
| var t1 *Type |
| var t2 *Type |
| var v1 *Node |
| var v2 *Node |
| |
| // Typechecking order is important here: |
| // 0. first typecheck range expression (slice/map/chan), |
| // it is evaluated only once and so logically it is not part of the loop. |
| // 1. typcheck produced values, |
| // this part can declare new vars and so it must be typechecked before body, |
| // because body can contain a closure that captures the vars. |
| // 2. decldepth++ to denote loop body. |
| // 3. typecheck body. |
| // 4. decldepth--. |
| |
| typecheck(&n.Right, Erv) |
| |
| t := n.Right.Type |
| if t == nil { |
| goto out |
| } |
| |
| // delicate little dance. see typecheckas2 |
| for ll := n.List; ll != nil; ll = ll.Next { |
| if ll.N.Defn != n { |
| typecheck(&ll.N, Erv|Easgn) |
| } |
| } |
| |
| if Isptr[t.Etype] != 0 && Isfixedarray(t.Type) { |
| t = t.Type |
| } |
| n.Type = t |
| |
| toomany = 0 |
| switch t.Etype { |
| default: |
| Yyerror("cannot range over %v", Nconv(n.Right, obj.FmtLong)) |
| goto out |
| |
| case TARRAY: |
| t1 = Types[TINT] |
| t2 = t.Type |
| |
| case TMAP: |
| t1 = t.Down |
| t2 = t.Type |
| |
| case TCHAN: |
| if t.Chan&Crecv == 0 { |
| Yyerror("invalid operation: range %v (receive from send-only type %v)", Nconv(n.Right, 0), Tconv(n.Right.Type, 0)) |
| goto out |
| } |
| |
| t1 = t.Type |
| t2 = nil |
| if count(n.List) == 2 { |
| toomany = 1 |
| } |
| |
| case TSTRING: |
| t1 = Types[TINT] |
| t2 = runetype |
| } |
| |
| if count(n.List) > 2 || toomany != 0 { |
| Yyerror("too many variables in range") |
| } |
| |
| v1 = nil |
| if n.List != nil { |
| v1 = n.List.N |
| } |
| v2 = nil |
| if n.List != nil && n.List.Next != nil { |
| v2 = n.List.Next.N |
| } |
| |
| // this is not only a optimization but also a requirement in the spec. |
| // "if the second iteration variable is the blank identifier, the range |
| // clause is equivalent to the same clause with only the first variable |
| // present." |
| if isblank(v2) { |
| if v1 != nil { |
| n.List = list1(v1) |
| } |
| v2 = nil |
| } |
| |
| if v1 != nil { |
| if v1.Defn == n { |
| v1.Type = t1 |
| } else if v1.Type != nil && assignop(t1, v1.Type, &why) == 0 { |
| Yyerror("cannot assign type %v to %v in range%s", Tconv(t1, 0), Nconv(v1, obj.FmtLong), why) |
| } |
| checkassign(n, v1) |
| } |
| |
| if v2 != nil { |
| if v2.Defn == n { |
| v2.Type = t2 |
| } else if v2.Type != nil && assignop(t2, v2.Type, &why) == 0 { |
| Yyerror("cannot assign type %v to %v in range%s", Tconv(t2, 0), Nconv(v2, obj.FmtLong), why) |
| } |
| checkassign(n, v2) |
| } |
| |
| // second half of dance |
| out: |
| n.Typecheck = 1 |
| |
| for ll := n.List; ll != nil; ll = ll.Next { |
| if ll.N.Typecheck == 0 { |
| typecheck(&ll.N, Erv|Easgn) |
| } |
| } |
| |
| decldepth++ |
| typechecklist(n.Nbody, Etop) |
| decldepth-- |
| } |
| |
| func walkrange(n *Node) { |
| t := n.Type |
| init := (*NodeList)(nil) |
| |
| a := n.Right |
| lno := int(setlineno(a)) |
| |
| v1 := (*Node)(nil) |
| if n.List != nil { |
| v1 = n.List.N |
| } |
| v2 := (*Node)(nil) |
| if n.List != nil && n.List.Next != nil && !isblank(n.List.Next.N) { |
| v2 = n.List.Next.N |
| } |
| |
| // n->list has no meaning anymore, clear it |
| // to avoid erroneous processing by racewalk. |
| n.List = nil |
| |
| hv2 := (*Node)(nil) |
| |
| var body *NodeList |
| switch t.Etype { |
| default: |
| Fatal("walkrange") |
| |
| // Lower n into runtime·memclr if possible, for |
| // fast zeroing of slices and arrays (issue 5373). |
| // Look for instances of |
| // |
| // for i := range a { |
| // a[i] = zero |
| // } |
| // |
| // in which the evaluation of a is side-effect-free. |
| case TARRAY: |
| if Debug['N'] == 0 { |
| if flag_race == 0 { |
| if v1 != nil { |
| if v2 == nil { |
| if n.Nbody != nil { |
| if n.Nbody.N != nil { // at least one statement in body |
| if n.Nbody.Next == nil { // at most one statement in body |
| tmp := n.Nbody.N // first statement of body |
| if tmp.Op == OAS { |
| if tmp.Left.Op == OINDEX { |
| if samesafeexpr(tmp.Left.Left, a) { |
| if samesafeexpr(tmp.Left.Right, v1) { |
| if t.Type.Width > 0 { |
| if iszero(tmp.Right) { |
| // Convert to |
| // if len(a) != 0 { |
| // hp = &a[0] |
| // hn = len(a)*sizeof(elem(a)) |
| // memclr(hp, hn) |
| // i = len(a) - 1 |
| // } |
| n.Op = OIF |
| |
| n.Nbody = nil |
| n.Ntest = Nod(ONE, Nod(OLEN, a, nil), Nodintconst(0)) |
| n.Nincr = nil |
| |
| // hp = &a[0] |
| hp := temp(Ptrto(Types[TUINT8])) |
| |
| tmp := Nod(OINDEX, a, Nodintconst(0)) |
| tmp.Bounded = true |
| tmp = Nod(OADDR, tmp, nil) |
| tmp = Nod(OCONVNOP, tmp, nil) |
| tmp.Type = Ptrto(Types[TUINT8]) |
| n.Nbody = list(n.Nbody, Nod(OAS, hp, tmp)) |
| |
| // hn = len(a) * sizeof(elem(a)) |
| hn := temp(Types[TUINTPTR]) |
| |
| tmp = Nod(OLEN, a, nil) |
| tmp = Nod(OMUL, tmp, Nodintconst(t.Type.Width)) |
| tmp = conv(tmp, Types[TUINTPTR]) |
| n.Nbody = list(n.Nbody, Nod(OAS, hn, tmp)) |
| |
| // memclr(hp, hn) |
| fn := mkcall("memclr", nil, nil, hp, hn) |
| |
| n.Nbody = list(n.Nbody, fn) |
| |
| // i = len(a) - 1 |
| v1 = Nod(OAS, v1, Nod(OSUB, Nod(OLEN, a, nil), Nodintconst(1))) |
| |
| n.Nbody = list(n.Nbody, v1) |
| |
| typecheck(&n.Ntest, Erv) |
| typechecklist(n.Nbody, Etop) |
| walkstmt(&n) |
| lineno = int32(lno) |
| return |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| // orderstmt arranged for a copy of the array/slice variable if needed. |
| ha := a |
| |
| hv1 := temp(Types[TINT]) |
| hn := temp(Types[TINT]) |
| hp := (*Node)(nil) |
| |
| init = list(init, Nod(OAS, hv1, nil)) |
| init = list(init, Nod(OAS, hn, Nod(OLEN, ha, nil))) |
| if v2 != nil { |
| hp = temp(Ptrto(n.Type.Type)) |
| tmp := Nod(OINDEX, ha, Nodintconst(0)) |
| tmp.Bounded = true |
| init = list(init, Nod(OAS, hp, Nod(OADDR, tmp, nil))) |
| } |
| |
| n.Ntest = Nod(OLT, hv1, hn) |
| n.Nincr = Nod(OAS, hv1, Nod(OADD, hv1, Nodintconst(1))) |
| if v1 == nil { |
| body = nil |
| } else if v2 == nil { |
| body = list1(Nod(OAS, v1, hv1)) |
| } else { |
| a := Nod(OAS2, nil, nil) |
| a.List = list(list1(v1), v2) |
| a.Rlist = list(list1(hv1), Nod(OIND, hp, nil)) |
| body = list1(a) |
| |
| // Advance pointer as part of increment. |
| // We used to advance the pointer before executing the loop body, |
| // but doing so would make the pointer point past the end of the |
| // array during the final iteration, possibly causing another unrelated |
| // piece of memory not to be garbage collected until the loop finished. |
| // Advancing during the increment ensures that the pointer p only points |
| // pass the end of the array during the final "p++; i++; if(i >= len(x)) break;", |
| // after which p is dead, so it cannot confuse the collector. |
| tmp := Nod(OADD, hp, Nodintconst(t.Type.Width)) |
| |
| tmp.Type = hp.Type |
| tmp.Typecheck = 1 |
| tmp.Right.Type = Types[Tptr] |
| tmp.Right.Typecheck = 1 |
| a = Nod(OAS, hp, tmp) |
| typecheck(&a, Etop) |
| n.Nincr.Ninit = list1(a) |
| } |
| |
| // orderstmt allocated the iterator for us. |
| // we only use a once, so no copy needed. |
| case TMAP: |
| ha := a |
| |
| th := hiter(t) |
| hit := n.Alloc |
| hit.Type = th |
| n.Left = nil |
| keyname := newname(th.Type.Sym) // depends on layout of iterator struct. See reflect.c:hiter |
| valname := newname(th.Type.Down.Sym) // ditto |
| |
| fn := syslook("mapiterinit", 1) |
| |
| argtype(fn, t.Down) |
| argtype(fn, t.Type) |
| argtype(fn, th) |
| init = list(init, mkcall1(fn, nil, nil, typename(t), ha, Nod(OADDR, hit, nil))) |
| n.Ntest = Nod(ONE, Nod(ODOT, hit, keyname), nodnil()) |
| |
| fn = syslook("mapiternext", 1) |
| argtype(fn, th) |
| n.Nincr = mkcall1(fn, nil, nil, Nod(OADDR, hit, nil)) |
| |
| key := Nod(ODOT, hit, keyname) |
| key = Nod(OIND, key, nil) |
| if v1 == nil { |
| body = nil |
| } else if v2 == nil { |
| body = list1(Nod(OAS, v1, key)) |
| } else { |
| val := Nod(ODOT, hit, valname) |
| val = Nod(OIND, val, nil) |
| a := Nod(OAS2, nil, nil) |
| a.List = list(list1(v1), v2) |
| a.Rlist = list(list1(key), val) |
| body = list1(a) |
| } |
| |
| // orderstmt arranged for a copy of the channel variable. |
| case TCHAN: |
| ha := a |
| |
| n.Ntest = nil |
| |
| hv1 := temp(t.Type) |
| hv1.Typecheck = 1 |
| if haspointers(t.Type) { |
| init = list(init, Nod(OAS, hv1, nil)) |
| } |
| hb := temp(Types[TBOOL]) |
| |
| n.Ntest = Nod(ONE, hb, Nodbool(false)) |
| a := Nod(OAS2RECV, nil, nil) |
| a.Typecheck = 1 |
| a.List = list(list1(hv1), hb) |
| a.Rlist = list1(Nod(ORECV, ha, nil)) |
| n.Ntest.Ninit = list1(a) |
| if v1 == nil { |
| body = nil |
| } else { |
| body = list1(Nod(OAS, v1, hv1)) |
| } |
| |
| // orderstmt arranged for a copy of the string variable. |
| case TSTRING: |
| ha := a |
| |
| ohv1 := temp(Types[TINT]) |
| |
| hv1 := temp(Types[TINT]) |
| init = list(init, Nod(OAS, hv1, nil)) |
| |
| var a *Node |
| if v2 == nil { |
| a = Nod(OAS, hv1, mkcall("stringiter", Types[TINT], nil, ha, hv1)) |
| } else { |
| hv2 = temp(runetype) |
| a = Nod(OAS2, nil, nil) |
| a.List = list(list1(hv1), hv2) |
| fn := syslook("stringiter2", 0) |
| a.Rlist = list1(mkcall1(fn, getoutargx(fn.Type), nil, ha, hv1)) |
| } |
| |
| n.Ntest = Nod(ONE, hv1, Nodintconst(0)) |
| n.Ntest.Ninit = list(list1(Nod(OAS, ohv1, hv1)), a) |
| |
| body = nil |
| if v1 != nil { |
| body = list1(Nod(OAS, v1, ohv1)) |
| } |
| if v2 != nil { |
| body = list(body, Nod(OAS, v2, hv2)) |
| } |
| } |
| |
| n.Op = OFOR |
| typechecklist(init, Etop) |
| n.Ninit = concat(n.Ninit, init) |
| typechecklist(n.Ntest.Ninit, Etop) |
| typecheck(&n.Ntest, Erv) |
| typecheck(&n.Nincr, Etop) |
| typechecklist(body, Etop) |
| n.Nbody = concat(body, n.Nbody) |
| walkstmt(&n) |
| |
| lineno = int32(lno) |
| } |