| // 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 <u.h> |
| #include <libc.h> |
| #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 %lN", n->right); |
| goto out; |
| |
| case TARRAY: |
| t1 = types[TINT]; |
| t2 = t->type; |
| break; |
| |
| case TMAP: |
| t1 = t->down; |
| t2 = t->type; |
| break; |
| |
| case TCHAN: |
| if(!(t->chan & Crecv)) { |
| yyerror("invalid operation: range %N (receive from send-only type %T)", n->right, n->right->type); |
| goto out; |
| } |
| t1 = t->type; |
| t2 = nil; |
| if(count(n->list) == 2) |
| goto toomany; |
| break; |
| |
| case TSTRING: |
| t1 = types[TINT]; |
| t2 = runetype; |
| break; |
| } |
| |
| if(count(n->list) > 2) { |
| toomany: |
| yyerror("too many variables in range"); |
| } |
| |
| v1 = N; |
| if(n->list) |
| v1 = n->list->n; |
| v2 = N; |
| 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)) { |
| if(v1 != N) |
| n->list = list1(v1); |
| v2 = N; |
| } |
| |
| 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; |
| else if(v2->type != T && assignop(t2, v2->type, &why) == 0) |
| yyerror("cannot assign type %T to %lN 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; |
| Node *keyname, *valname; |
| Node *key, *val; |
| NodeList *body, *init; |
| Type *th, *t; |
| int lno; |
| |
| t = n->type; |
| init = nil; |
| |
| a = n->right; |
| lno = setlineno(a); |
| |
| v1 = N; |
| if(n->list) |
| v1 = n->list->n; |
| v2 = 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. |
| n->list = nil; |
| hv2 = N; |
| |
| switch(t->etype) { |
| default: |
| fatal("walkrange"); |
| |
| case TARRAY: |
| // 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, N)); |
| init = list(init, nod(OAS, hn, nod(OLEN, ha, N))); |
| if(v2) { |
| hp = temp(ptrto(n->type->type)); |
| tmp = nod(OINDEX, ha, nodintconst(0)); |
| tmp->bounded = 1; |
| init = list(init, nod(OAS, hp, nod(OADDR, tmp, N))); |
| } |
| |
| n->ntest = nod(OLT, hv1, hn); |
| n->nincr = nod(OAS, hv1, nod(OADD, hv1, nodintconst(1))); |
| if(v1 == N) |
| body = nil; |
| else if(v2 == N) |
| body = list1(nod(OAS, v1, hv1)); |
| else { |
| a = nod(OAS2, N, N); |
| a->list = list(list1(v1), v2); |
| a->rlist = list(list1(hv1), nod(OIND, hp, N)); |
| 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); |
| } |
| break; |
| |
| case TMAP: |
| // orderstmt allocated the iterator for us. |
| // we only use a once, so no copy needed. |
| ha = a; |
| th = hiter(t); |
| hit = n->alloc; |
| hit->type = th; |
| n->left = N; |
| 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, T, nil, typename(t), ha, nod(OADDR, hit, N))); |
| n->ntest = nod(ONE, nod(ODOT, hit, keyname), nodnil()); |
| |
| fn = syslook("mapiternext", 1); |
| argtype(fn, th); |
| n->nincr = mkcall1(fn, T, nil, nod(OADDR, hit, N)); |
| |
| key = nod(ODOT, hit, keyname); |
| key = nod(OIND, key, N); |
| 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); |
| } |
| break; |
| |
| case TCHAN: |
| // orderstmt arranged for a copy of the channel variable. |
| ha = a; |
| 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]); |
| |
| 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); |
| if(v1 == N) |
| body = nil; |
| else |
| body = list1(nod(OAS, v1, hv1)); |
| break; |
| |
| case TSTRING: |
| // orderstmt arranged for a copy of the string variable. |
| ha = a; |
| |
| ohv1 = temp(types[TINT]); |
| |
| hv1 = temp(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 = temp(runetype); |
| 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 = nil; |
| if(v1 != N) |
| 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); |
| |
| lineno = lno; |
| } |
| |