mirror of
https://github.com/golang/go.git
synced 2025-11-03 10:10:55 +00:00
Reconvert using rsc.io/c2go rev 27b3f59. Changes to converter: - fatal does not return, so no fallthrough after fatal in switch - many more function results and variables identified as bool - simplification of negated boolean expressions Change-Id: I3bc67da5e46cb7ee613e230cf7e9533036cc870b Reviewed-on: https://go-review.googlesource.com/5171 Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
425 lines
9.9 KiB
Go
425 lines
9.9 KiB
Go
// 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 t *Type
|
|
var t1 *Type
|
|
var t2 *Type
|
|
var v1 *Node
|
|
var v2 *Node
|
|
var ll *NodeList
|
|
|
|
// 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) {
|
|
var ohv1 *Node
|
|
var hv1 *Node // hidden (old) val 1, 2 // hidden aggregate, iterator // hidden len, pointer // hidden bool // not hidden aggregate, val 1, 2
|
|
var hv2 *Node
|
|
var ha *Node
|
|
var hit *Node
|
|
var hn *Node
|
|
var hp *Node
|
|
var hb *Node
|
|
var a *Node
|
|
var v1 *Node
|
|
var v2 *Node
|
|
var fn *Node
|
|
var tmp *Node
|
|
var keyname *Node
|
|
var valname *Node
|
|
var key *Node
|
|
var val *Node
|
|
var body *NodeList
|
|
var init *NodeList
|
|
var th *Type
|
|
var t *Type
|
|
var lno int
|
|
|
|
t = n.Type
|
|
init = nil
|
|
|
|
a = n.Right
|
|
lno = int(setlineno(a))
|
|
|
|
v1 = nil
|
|
if n.List != nil {
|
|
v1 = n.List.N
|
|
}
|
|
v2 = 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 = nil
|
|
|
|
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 = 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))
|
|
|
|
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)
|
|
}
|