// 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)
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))
n.Right = nil
var v1 *Node
if n.List != nil {
v1 = n.List.N
}
var v2 *Node
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:
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.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(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.Left, 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])
var hp *Node
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.Left = Nod(OLT, hv1, hn)
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 := prealloc[n]
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)
substArgTypes(fn, th)
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
n.Left = nil
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))
n.Left.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
var hv2 *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)
}