Newer
Older
// 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.Name == nil || ll.N.Name.Defn != n {
typecheck(&ll.N, Erv|Easgn)
}
}
if Isptr[t.Etype] && 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)", n.Right, n.Right.Type)
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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.Name != nil && v1.Name.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", t1, Nconv(v1, obj.FmtLong), why)
}
checkassign(n, v1)
}
if v2 != nil {
if v2.Name != nil && v2.Name.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", t2, 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) {
// variable name conventions:
// ohv1, hv1, hv2: hidden (old) val 1, 2
// ha, hit: hidden aggregate, iterator
// hn, hp: hidden len, pointer
// hb: hidden bool
// a, v1, v2: not hidden aggregate, val 1, 2
t := n.Type
a := n.Right
lno := int(setlineno(a))
if n.List != nil {
v1 = n.List.N
}
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
var body *NodeList
var init *NodeList
switch t.Etype {
default:
case TARRAY:
if memclrrange(n, v1, v2, a) {
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])
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.Right = 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.Right.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.Type = th
n.Left = nil
keyname := newname(th.Type.Sym) // depends on layout of iterator struct. See reflect.go:hiter
valname := newname(th.Type.Down.Sym) // ditto
fn := syslook("mapiterinit", 1)
substArgTypes(fn, t.Down, t.Type, th)
init = list(init, mkcall1(fn, nil, nil, typename(t), ha, Nod(OADDR, hit, nil)))
n.Left = Nod(ONE, Nod(ODOT, hit, keyname), nodnil())
fn = syslook("mapiternext", 1)
n.Right = 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
hv1 := temp(t.Type)
hv1.Typecheck = 1
if haspointers(t.Type) {
init = list(init, Nod(OAS, hv1, nil))
}
hb := temp(Types[TBOOL])
n.Left = 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))
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.Left = Nod(ONE, hv1, Nodintconst(0))
n.Left.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.Left.Ninit, Etop)
typecheck(&n.Left, Erv)
typecheck(&n.Right, Etop)
typechecklist(body, Etop)
n.Nbody = concat(body, n.Nbody)
walkstmt(&n)
lineno = int32(lno)
}
// 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.
//
// Parameters are as in walkrange: "for v1, v2 = range a".
func memclrrange(n, v1, v2, a *Node) bool {
if Debug['N'] != 0 || instrumenting {
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
return false
}
if v1 == nil || v2 != nil {
return false
}
if n.Nbody == nil || n.Nbody.N == nil || n.Nbody.Next != nil {
return false
}
stmt := n.Nbody.N // only stmt in body
if stmt.Op != OAS || stmt.Left.Op != OINDEX {
return false
}
if !samesafeexpr(stmt.Left.Left, a) || !samesafeexpr(stmt.Left.Right, v1) {
return false
}
elemsize := n.Type.Type.Width
if elemsize <= 0 || !iszero(stmt.Right) {
return false
}
// 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.Left = Nod(ONE, Nod(OLEN, a, nil), Nodintconst(0))
// 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(elemsize))
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.Left, Erv)
typechecklist(n.Nbody, Etop)
walkstmt(&n)
return true
}