cmd/gc: implement 'for range x {'

Fixes #6102.

LGTM=gri
R=ken, r, gri
CC=golang-codereviews
https://golang.org/cl/113120043
This commit is contained in:
Russ Cox 2014-07-16 19:27:10 -04:00
parent 26d0f75f2b
commit 8d504c4e97
12 changed files with 1495 additions and 1373 deletions

View file

@ -495,7 +495,7 @@ esc(EscState *e, Node *n, Node *up)
case ORANGE:
// Everything but fixed array is a dereference.
if(isfixedarray(n->type) && n->list->next)
if(isfixedarray(n->type) && n->list && n->list->next)
escassign(e, n->list->next->n, n->right);
break;

View file

@ -880,7 +880,11 @@ stmtfmt(Fmt *f, Node *n)
fmtstrcpy(f, "for loop");
break;
}
if(n->list == nil) {
fmtprint(f, "for range %N { %H }", n->right, n->nbody);
break;
}
fmtprint(f, "for %,H = range %N { %H }", n->list, n->right, n->nbody);
break;

View file

@ -613,6 +613,11 @@ range_stmt:
$$->colas = 1;
colasdefn($1, $$);
}
| LRANGE expr
{
$$ = nod(ORANGE, N, $2);
$$->etype = 0; // := flag
}
for_header:
osimple_stmt ';' osimple_stmt ';' osimple_stmt

View file

@ -67,9 +67,11 @@ typecheckrange(Node *n)
yyerror("too many variables in range");
}
v1 = n->list->n;
v1 = N;
if(n->list)
v1 = n->list->n;
v2 = N;
if(n->list->next)
if(n->list && n->list->next)
v2 = n->list->next->n;
// this is not only a optimization but also a requirement in the spec.
@ -77,14 +79,17 @@ typecheckrange(Node *n)
// clause is equivalent to the same clause with only the first variable
// present."
if(isblank(v2)) {
n->list = list1(v1);
if(v1 != N)
n->list = list1(v1);
v2 = N;
}
if(v1->defn == n)
v1->type = t1;
else if(v1->type != T && assignop(t1, v1->type, &why) == 0)
yyerror("cannot assign type %T to %lN in range%s", t1, v1, why);
if(v1) {
if(v1->defn == n)
v1->type = t1;
else if(v1->type != T && assignop(t1, v1->type, &why) == 0)
yyerror("cannot assign type %T to %lN in range%s", t1, v1, why);
}
if(v2) {
if(v2->defn == n)
v2->type = t2;
@ -123,9 +128,11 @@ walkrange(Node *n)
a = n->right;
lno = setlineno(a);
v1 = n->list->n;
v1 = N;
if(n->list)
v1 = n->list->n;
v2 = N;
if(n->list->next && !isblank(n->list->next->n))
if(n->list && n->list->next && !isblank(n->list->next->n))
v2 = n->list->next->n;
// n->list has no meaning anymore, clear it
// to avoid erroneous processing by racewalk.
@ -154,7 +161,9 @@ walkrange(Node *n)
n->ntest = nod(OLT, hv1, hn);
n->nincr = nod(OAS, hv1, nod(OADD, hv1, nodintconst(1)));
if(v2 == N)
if(v1 == N)
body = nil;
else if(v2 == N)
body = list1(nod(OAS, v1, hv1));
else {
a = nod(OAS2, N, N);
@ -205,16 +214,18 @@ walkrange(Node *n)
key = nod(ODOT, hit, keyname);
key = nod(OIND, key, N);
if(v2 == N) {
a = nod(OAS, v1, key);
if(v1 == N)
body = nil;
else if(v2 == N) {
body = list1(nod(OAS, v1, key));
} else {
val = nod(ODOT, hit, valname);
val = nod(OIND, val, N);
a = nod(OAS2, N, N);
a->list = list(list1(v1), v2);
a->rlist = list(list1(key), val);
body = list1(a);
}
body = list1(a);
break;
case TCHAN:
@ -223,6 +234,7 @@ walkrange(Node *n)
n->ntest = N;
hv1 = temp(t->type);
hv1->typecheck = 1;
if(haspointers(t->type))
init = list(init, nod(OAS, hv1, N));
hb = temp(types[TBOOL]);
@ -233,7 +245,10 @@ walkrange(Node *n)
a->list = list(list1(hv1), hb);
a->rlist = list1(nod(ORECV, ha, N));
n->ntest->ninit = list1(a);
body = list1(nod(OAS, v1, hv1));
if(v1 == N)
body = nil;
else
body = list1(nod(OAS, v1, hv1));
break;
case TSTRING:
@ -257,7 +272,10 @@ walkrange(Node *n)
n->ntest = nod(ONE, hv1, nodintconst(0));
n->ntest->ninit = list(list1(nod(OAS, ohv1, hv1)), a);
body = list1(nod(OAS, v1, ohv1));
body = nil;
if(v1 != N)
body = list1(nod(OAS, v1, ohv1));
if(v2 != N)
body = list(body, nod(OAS, v2, hv2));
break;

File diff suppressed because it is too large Load diff

View file

@ -20,16 +20,16 @@ static struct {
{32, ';',
"missing import path; require quoted string"},
{378, ';',
{380, ';',
"missing { after if clause"},
{399, ';',
{401, ';',
"missing { after switch clause"},
{238, ';',
{239, ';',
"missing { after for clause"},
{476, LBODY,
{478, LBODY,
"missing { after for clause"},
{22, '{',
@ -47,33 +47,33 @@ static struct {
{37, ',',
"unexpected comma in channel type"},
{439, LELSE,
{441, LELSE,
"unexpected semicolon or newline before else"},
{258, ',',
{259, ',',
"name list not allowed in interface type"},
{238, LVAR,
{239, LVAR,
"var declaration not allowed in for initializer"},
{65, '{',
"unexpected { at end of statement"},
{377, '{',
{379, '{',
"unexpected { at end of statement"},
{126, ';',
"argument to go/defer must be function call"},
{426, ';',
{428, ';',
"need trailing comma before newline in composite literal"},
{437, ';',
{439, ';',
"need trailing comma before newline in composite literal"},
{113, LNAME,
"nested func not allowed"},
{645, ';',
{647, ';',
"else must be followed by if or statement block"}
};

View file

@ -123,7 +123,7 @@ func pallmall(cols []int) {
fmt.Println(msg)
tot := 0
// wait for all results
for _ = range cols {
for range cols {
result := <-ended
tot += result.met
fmt.Printf("%v%v\n", result.met, spell(result.same, true))

View file

@ -56,6 +56,9 @@ func main() {
for _ = range cs {// ERROR "receive"
}
for range cs {// ERROR "receive"
}
close(c)
close(cs)
close(cr) // ERROR "receive"

View file

@ -18,4 +18,6 @@ func main() {
}
for _ = range t {
}
for range t {
}
}

View file

@ -14,6 +14,8 @@ type matrix struct {
func (a matrix) equal() bool {
for _ = range a.e {
}
for range a.e {
}
return true
}

View file

@ -32,6 +32,13 @@ func testchan() {
println("Wanted lowercase alphabet; got", s)
panic("fail")
}
n := 0
for range seq('a', 'z') {
n++
}
if n != 26 {
println("testchan wrong count", n, "want 26")
}
}
// test that range over slice only evaluates
@ -87,6 +94,22 @@ func testslice1() {
}
}
func testslice2() {
n := 0
nmake = 0
for range makeslice() {
n++
}
if nmake != 1 {
println("range called makeslice", nmake, "times")
panic("fail")
}
if n != 5 {
println("wrong count ranging over makeslice", n)
panic("fail")
}
}
// test that range over array only evaluates
// the expression after "range" once.
@ -127,6 +150,22 @@ func testarray1() {
}
}
func testarray2() {
n := 0
nmake = 0
for range makearray() {
n++
}
if nmake != 1 {
println("range called makearray", nmake, "times")
panic("fail")
}
if n != 5 {
println("wrong count ranging over makearray", n)
panic("fail")
}
}
func makearrayptr() *[5]int {
nmake++
return &[5]int{1, 2, 3, 4, 5}
@ -176,6 +215,22 @@ func testarrayptr1() {
}
}
func testarrayptr2() {
n := 0
nmake = 0
for range makearrayptr() {
n++
}
if nmake != 1 {
println("range called makearrayptr", nmake, "times")
panic("fail")
}
if n != 5 {
println("wrong count ranging over makearrayptr", n)
panic("fail")
}
}
// test that range over string only evaluates
// the expression after "range" once.
@ -216,6 +271,22 @@ func teststring1() {
}
}
func teststring2() {
n := 0
nmake = 0
for range makestring() {
n++
}
if nmake != 1 {
println("range called makestring", nmake, "times")
panic("fail")
}
if n != 5 {
println("wrong count ranging over makestring", n)
panic("fail")
}
}
// test that range over map only evaluates
// the expression after "range" once.
@ -256,6 +327,22 @@ func testmap1() {
}
}
func testmap2() {
n := 0
nmake = 0
for range makemap() {
n++
}
if nmake != 1 {
println("range called makemap", nmake, "times")
panic("fail")
}
if n != 5 {
println("wrong count ranging over makemap", n)
panic("fail")
}
}
// test that range evaluates the index and value expressions
// exactly once per iteration.
@ -298,13 +385,18 @@ func main() {
testchan()
testarray()
testarray1()
testarray2()
testarrayptr()
testarrayptr1()
testarrayptr2()
testslice()
testslice1()
testslice2()
teststring()
teststring1()
teststring2()
testmap()
testmap1()
testmap2()
testcalls()
}

View file

@ -97,6 +97,8 @@ func (m intMap) Len() int { return len(m) }
func (m intMap) RangeAll() {
for _ = range m {
}
for range m {
}
}
func stressMaps() {