// 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 #include #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; }