root/src/cmd/gc/range.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. typecheckrange
  2. walkrange

// 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->list->n;
        v2 = N;
        if(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);
                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(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->list->n;
        v2 = N;
        if(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(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(v2 == N) {
                        a = 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);
                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);
                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 = 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;
}


/* [<][>][^][v][top][bottom][index][help] */