diff options
Diffstat (limited to 'src/cmd/gc/range.c')
-rw-r--r-- | src/cmd/gc/range.c | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/src/cmd/gc/range.c b/src/cmd/gc/range.c new file mode 100644 index 000000000..bc51ee6ac --- /dev/null +++ b/src/cmd/gc/range.c @@ -0,0 +1,216 @@ +// 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) +{ + int op, et; + 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); + + typecheck(&n->right, Erv); + if((t = n->right->type) == T) + goto out; + 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 && checkconv(t1, v1->type, 0, &op, &et) < 0) + yyerror("cannot assign type %T to %+N", t1, v1); + if(v2) { + if(v2->defn == n) + v2->type = t2; + else if(v2->type != T && checkconv(t2, v2->type, 0, &op, &et) < 0) + yyerror("cannot assign type %T to %+N", t1, v1); + } + +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); +} + +void +walkrange(Node *n) +{ + Node *ohv1, *hv1, *hv2; // hidden (old) val 1, 2 + Node *ha, *hit; // hidden aggregate, iterator + Node *a, *v1, *v2; // not hidden aggregate, val 1, 2 + Node *fn; + 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]; + } + ha = nod(OXXX, N, N); + tempname(ha, a->type); + init = list(init, nod(OAS, ha, a)); + + v1 = n->list->n; + hv1 = N; + + v2 = N; + if(n->list->next) + v2 = n->list->next->n; + hv2 = N; + + switch(t->etype) { + default: + fatal("walkrange"); + + case TARRAY: + hv1 = nod(OXXX, N, n); + tempname(hv1, v1->type); + + init = list(init, nod(OAS, hv1, N)); + n->ntest = nod(OLT, hv1, nod(OLEN, ha, N)); + 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(OINDEX, ha, hv1))); + 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, v1->type); + + n->ntest = nod(ONOT, nod(OCLOSED, ha, N), N); + n->ntest->ninit = list1(nod(OAS, hv1, nod(ORECV, ha, N))); + 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); +} + |