Skip to content

Commit

Permalink
cmd/gc: implement 'for range x {'
Browse files Browse the repository at this point in the history
Fixes #6102.

LGTM=gri
R=ken, r, gri
CC=golang-codereviews
https://golang.org/cl/113120043
  • Loading branch information
rsc committed Jul 16, 2014
1 parent 26d0f75 commit 8d504c4
Show file tree
Hide file tree
Showing 12 changed files with 1,416 additions and 1,294 deletions.
2 changes: 1 addition & 1 deletion src/cmd/gc/esc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
6 changes: 5 additions & 1 deletion src/cmd/gc/fmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
5 changes: 5 additions & 0 deletions src/cmd/gc/go.y
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
48 changes: 33 additions & 15 deletions src/cmd/gc/range.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,24 +67,29 @@ 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.
// "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)) {
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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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:
Expand All @@ -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]);
Expand All @@ -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:
Expand All @@ -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;
Expand Down
2,524 changes: 1,259 additions & 1,265 deletions src/cmd/gc/y.tab.c

Large diffs are not rendered by default.

22 changes: 11 additions & 11 deletions src/cmd/gc/yerr.h
Original file line number Diff line number Diff line change
Expand Up @@ -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, '{',
Expand All @@ -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"}
};
2 changes: 1 addition & 1 deletion test/bench/shootout/chameneosredux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
3 changes: 3 additions & 0 deletions test/chan/perm.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ func main() {
for _ = range cs {// ERROR "receive"
}

for range cs {// ERROR "receive"
}

close(c)
close(cs)
close(cr) // ERROR "receive"
Expand Down
2 changes: 2 additions & 0 deletions test/fixedbugs/bug173.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@ func main() {
}
for _ = range t {
}
for range t {
}
}
2 changes: 2 additions & 0 deletions test/fixedbugs/bug406.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ type matrix struct {
func (a matrix) equal() bool {
for _ = range a.e {
}
for range a.e {
}
return true
}

Expand Down
92 changes: 92 additions & 0 deletions test/range.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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}
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -298,13 +385,18 @@ func main() {
testchan()
testarray()
testarray1()
testarray2()
testarrayptr()
testarrayptr1()
testarrayptr2()
testslice()
testslice1()
testslice2()
teststring()
teststring1()
teststring2()
testmap()
testmap1()
testmap2()
testcalls()
}
Loading

0 comments on commit 8d504c4

Please sign in to comment.