blob: dfb2b8efd68430748bbda182565d21c4ad150e5e [file] [log] [blame]
// 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.
/*
* range
*/
#include "go.h"
void
typecheckrange(Node *n)
{
char *why;
Type *t, *t1, *t2;
Node *v1, *v2;
NodeList *ll;
// delicate little dance. see typecheckas2
for(ll=n->list; ll; ll=ll->next)
if(ll->n->defn != n)
typecheck(&ll->n, Erv | Easgn);
typecheck(&n->right, Erv);
if((t = n->right->type) == T)
goto out;
if(isptr[t->etype] && isfixedarray(t->type))
t = t->type;
n->type = t;
switch(t->etype) {
default:
yyerror("cannot range over %+N", n->right);
goto out;
case TARRAY:
t1 = types[TINT];
t2 = t->type;
break;
case TMAP:
t1 = t->down;
t2 = t->type;
break;
case TCHAN:
t1 = t->type;
t2 = nil;
if(count(n->list) == 2)
goto toomany;
break;
case TSTRING:
t1 = types[TINT];
t2 = types[TINT];
break;
}
if(count(n->list) > 2) {
toomany:
yyerror("too many variables in range");
}
v1 = n->list->n;
v2 = N;
if(n->list->next)
v2 = n->list->next->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 %+N in range%s", t1, v1, why);
if(v2) {
if(v2->defn == n)
v2->type = t2;
else if(v2->type != T && assignop(t2, v2->type, &why) == 0)
yyerror("cannot assign type %T to %+N in range%s", t2, v2, why);
}
out:
typechecklist(n->nbody, Etop);
// second half of dance
n->typecheck = 1;
for(ll=n->list; ll; ll=ll->next)
if(ll->n->typecheck == 0)
typecheck(&ll->n, Erv | Easgn);
}
void
walkrange(Node *n)
{
Node *ohv1, *hv1, *hv2; // hidden (old) val 1, 2
Node *ha, *hit; // hidden aggregate, iterator
Node *hn, *hp; // hidden len, pointer
Node *hb; // hidden bool
Node *a, *v1, *v2; // not hidden aggregate, val 1, 2
Node *fn, *tmp;
NodeList *body, *init;
Type *th, *t;
t = n->type;
init = nil;
a = n->right;
if(t->etype == TSTRING && !eqtype(t, types[TSTRING])) {
a = nod(OCONV, n->right, N);
a->type = types[TSTRING];
}
v1 = n->list->n;
hv1 = N;
v2 = N;
if(n->list->next)
v2 = n->list->next->n;
hv2 = N;
if(v2 == N && t->etype == TARRAY) {
// will have just one reference to argument.
// no need to make a potentially expensive copy.
ha = a;
} else {
ha = nod(OXXX, N, N);
tempname(ha, a->type);
init = list(init, nod(OAS, ha, a));
}
switch(t->etype) {
default:
fatal("walkrange");
case TARRAY:
hv1 = nod(OXXX, N, n);
tempname(hv1, types[TINT]);
hn = nod(OXXX, N, N);
tempname(hn, types[TINT]);
hp = nil;
init = list(init, nod(OAS, hv1, N));
init = list(init, nod(OAS, hn, nod(OLEN, ha, N)));
if(v2) {
hp = nod(OXXX, N, N);
tempname(hp, ptrto(n->type->type));
tmp = nod(OINDEX, ha, nodintconst(0));
tmp->etype = 1; // no bounds check
init = list(init, nod(OAS, hp, nod(OADDR, tmp, N)));
}
n->ntest = nod(OLT, hv1, hn);
n->nincr = nod(OASOP, hv1, nodintconst(1));
n->nincr->etype = OADD;
body = list1(nod(OAS, v1, hv1));
if(v2) {
body = list(body, nod(OAS, v2, nod(OIND, hp, N)));
tmp = nod(OADD, hp, nodintconst(t->type->width));
tmp->type = hp->type;
tmp->typecheck = 1;
tmp->right->type = types[tptr];
tmp->right->typecheck = 1;
body = list(body, nod(OAS, hp, tmp));
}
break;
case TMAP:
th = typ(TARRAY);
th->type = ptrto(types[TUINT8]);
th->bound = (sizeof(struct Hiter) + widthptr - 1) / widthptr;
hit = nod(OXXX, N, N);
tempname(hit, th);
fn = syslook("mapiterinit", 1);
argtype(fn, t->down);
argtype(fn, t->type);
argtype(fn, th);
init = list(init, mkcall1(fn, T, nil, ha, nod(OADDR, hit, N)));
n->ntest = nod(ONE, nod(OINDEX, hit, nodintconst(0)), nodnil());
fn = syslook("mapiternext", 1);
argtype(fn, th);
n->nincr = mkcall1(fn, T, nil, nod(OADDR, hit, N));
if(v2 == N) {
fn = syslook("mapiter1", 1);
argtype(fn, th);
argtype(fn, t->down);
a = nod(OAS, v1, mkcall1(fn, t->down, nil, nod(OADDR, hit, N)));
} else {
fn = syslook("mapiter2", 1);
argtype(fn, th);
argtype(fn, t->down);
argtype(fn, t->type);
a = nod(OAS2, N, N);
a->list = list(list1(v1), v2);
a->rlist = list1(mkcall1(fn, getoutargx(fn->type), nil, nod(OADDR, hit, N)));
}
body = list1(a);
break;
case TCHAN:
hv1 = nod(OXXX, N, n);
tempname(hv1, t->type);
hb = nod(OXXX, N, N);
tempname(hb, types[TBOOL]);
n->ntest = nod(ONE, hb, nodbool(0));
a = nod(OAS2RECV, N, N);
a->typecheck = 1;
a->list = list(list1(hv1), hb);
a->rlist = list1(nod(ORECV, ha, N));
n->ntest->ninit = list1(a);
body = list1(nod(OAS, v1, hv1));
break;
case TSTRING:
ohv1 = nod(OXXX, N, N);
tempname(ohv1, types[TINT]);
hv1 = nod(OXXX, N, N);
tempname(hv1, types[TINT]);
init = list(init, nod(OAS, hv1, N));
if(v2 == N)
a = nod(OAS, hv1, mkcall("stringiter", types[TINT], nil, ha, hv1));
else {
hv2 = nod(OXXX, N, N);
tempname(hv2, types[TINT]);
a = nod(OAS2, N, N);
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 = list1(nod(OAS, v1, ohv1));
if(v2 != N)
body = list(body, nod(OAS, v2, hv2));
break;
}
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);
}