root/src/cmd/gc/swt.c

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

DEFINITIONS

This source file includes following definitions.
  1. dumpcase
  2. ordlcmp
  3. exprcmp
  4. typecmp
  5. csort
  6. newlabel
  7. casebody
  8. mkcaselist
  9. exprbsw
  10. exprswitch
  11. typeone
  12. typebsw
  13. typeswitch
  14. walkswitch
  15. typecheckswitch

// 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.

#include        <u.h>
#include        <libc.h>
#include        "go.h"

enum
{
        Snorm           = 0,
        Strue,
        Sfalse,
        Stype,

        Tdefault,       // default case
        Texprconst,     // normal constant case
        Texprvar,       // normal variable case
        Ttypenil,       // case nil
        Ttypeconst,     // type hashes
        Ttypevar,       // interface type

        Ncase   = 4,    // count needed to split
};

typedef struct  Case    Case;
struct  Case
{
        Node*   node;           // points at case statement
        uint32  hash;           // hash of a type switch
        uint8   type;           // type of case
        uint8   diag;           // suppress multiple diagnostics
        uint16  ordinal;        // position in switch
        Case*   link;           // linked list to link
};
#define C       ((Case*)nil)

void
dumpcase(Case *c0)
{
        Case *c;

        for(c=c0; c!=C; c=c->link) {
                switch(c->type) {
                case Tdefault:
                        print("case-default\n");
                        print(" ord=%d\n", c->ordinal);
                        break;
                case Texprconst:
                        print("case-exprconst\n");
                        print(" ord=%d\n", c->ordinal);
                        break;
                case Texprvar:
                        print("case-exprvar\n");
                        print(" ord=%d\n", c->ordinal);
                        print(" op=%O\n", c->node->left->op);
                        break;
                case Ttypenil:
                        print("case-typenil\n");
                        print(" ord=%d\n", c->ordinal);
                        break;
                case Ttypeconst:
                        print("case-typeconst\n");
                        print(" ord=%d\n", c->ordinal);
                        print(" hash=%ux\n", c->hash);
                        break;
                case Ttypevar:
                        print("case-typevar\n");
                        print(" ord=%d\n", c->ordinal);
                        break;
                default:
                        print("case-???\n");
                        print(" ord=%d\n", c->ordinal);
                        print(" op=%O\n", c->node->left->op);
                        print(" hash=%ux\n", c->hash);
                        break;
                }
        }
        print("\n");
}

static int
ordlcmp(Case *c1, Case *c2)
{
        // sort default first
        if(c1->type == Tdefault)
                return -1;
        if(c2->type == Tdefault)
                return +1;

        // sort nil second
        if(c1->type == Ttypenil)
                return -1;
        if(c2->type == Ttypenil)
                return +1;

        // sort by ordinal
        if(c1->ordinal > c2->ordinal)
                return +1;
        if(c1->ordinal < c2->ordinal)
                return -1;
        return 0;
}

static int
exprcmp(Case *c1, Case *c2)
{
        int ct, n;
        Node *n1, *n2;

        // sort non-constants last
        if(c1->type != Texprconst)
                return +1;
        if(c2->type != Texprconst)
                return -1;

        n1 = c1->node->left;
        n2 = c2->node->left;

        // sort by type (for switches on interface)
        ct = n1->val.ctype;
        if(ct != n2->val.ctype)
                return ct - n2->val.ctype;
        if(!eqtype(n1->type, n2->type)) {
                if(n1->type->vargen > n2->type->vargen)
                        return +1;
                else
                        return -1;
        }

        // sort by constant value
        n = 0;
        switch(ct) {
        case CTFLT:
                n = mpcmpfltflt(n1->val.u.fval, n2->val.u.fval);
                break;
        case CTINT:
        case CTRUNE:
                n = mpcmpfixfix(n1->val.u.xval, n2->val.u.xval);
                break;
        case CTSTR:
                n = cmpslit(n1, n2);
                break;
        }

        return n;
}

static int
typecmp(Case *c1, Case *c2)
{

        // sort non-constants last
        if(c1->type != Ttypeconst)
                return +1;
        if(c2->type != Ttypeconst)
                return -1;

        // sort by hash code
        if(c1->hash > c2->hash)
                return +1;
        if(c1->hash < c2->hash)
                return -1;

        // sort by ordinal so duplicate error
        // happens on later case.
        if(c1->ordinal > c2->ordinal)
                return +1;
        if(c1->ordinal < c2->ordinal)
                return -1;
        return 0;
}

static Case*
csort(Case *l, int(*f)(Case*, Case*))
{
        Case *l1, *l2, *le;

        if(l == C || l->link == C)
                return l;

        l1 = l;
        l2 = l;
        for(;;) {
                l2 = l2->link;
                if(l2 == C)
                        break;
                l2 = l2->link;
                if(l2 == C)
                        break;
                l1 = l1->link;
        }

        l2 = l1->link;
        l1->link = C;
        l1 = csort(l, f);
        l2 = csort(l2, f);

        /* set up lead element */
        if((*f)(l1, l2) < 0) {
                l = l1;
                l1 = l1->link;
        } else {
                l = l2;
                l2 = l2->link;
        }
        le = l;

        for(;;) {
                if(l1 == C) {
                        while(l2) {
                                le->link = l2;
                                le = l2;
                                l2 = l2->link;
                        }
                        le->link = C;
                        break;
                }
                if(l2 == C) {
                        while(l1) {
                                le->link = l1;
                                le = l1;
                                l1 = l1->link;
                        }
                        break;
                }
                if((*f)(l1, l2) < 0) {
                        le->link = l1;
                        le = l1;
                        l1 = l1->link;
                } else {
                        le->link = l2;
                        le = l2;
                        l2 = l2->link;
                }
        }
        le->link = C;
        return l;
}

static Node*
newlabel(void)
{
        static int label;

        label++;
        snprint(namebuf, sizeof(namebuf), "%.6d", label);
        return newname(lookup(namebuf));
}

/*
 * build separate list of statements and cases
 * make labels between cases and statements
 * deal with fallthrough, break, unreachable statements
 */
static void
casebody(Node *sw, Node *typeswvar)
{
        Node *n, *c, *last;
        Node *def;
        NodeList *cas, *stat, *l, *lc;
        Node *go, *br;
        int32 lno, needvar;

        if(sw->list == nil)
                return;

        lno = setlineno(sw);

        cas = nil;      // cases
        stat = nil;     // statements
        def = N;        // defaults
        br = nod(OBREAK, N, N);

        for(l=sw->list; l; l=l->next) {
                n = l->n;
                setlineno(n);
                if(n->op != OXCASE)
                        fatal("casebody %O", n->op);
                n->op = OCASE;
                needvar = count(n->list) != 1 || n->list->n->op == OLITERAL;

                go = nod(OGOTO, newlabel(), N);
                if(n->list == nil) {
                        if(def != N)
                                yyerror("more than one default case");
                        // reuse original default case
                        n->right = go;
                        def = n;
                }

                if(n->list != nil && n->list->next == nil) {
                        // one case - reuse OCASE node.
                        c = n->list->n;
                        n->left = c;
                        n->right = go;
                        n->list = nil;
                        cas = list(cas, n);
                } else {
                        // expand multi-valued cases
                        for(lc=n->list; lc; lc=lc->next) {
                                c = lc->n;
                                cas = list(cas, nod(OCASE, c, go));
                        }
                }

                stat = list(stat, nod(OLABEL, go->left, N));
                if(typeswvar && needvar && n->nname != N) {
                        NodeList *l;

                        l = list1(nod(ODCL, n->nname, N));
                        l = list(l, nod(OAS, n->nname, typeswvar));
                        typechecklist(l, Etop);
                        stat = concat(stat, l);
                }
                stat = concat(stat, n->nbody);

                // botch - shouldn't fall thru declaration
                last = stat->end->n;
                if(last->xoffset == n->xoffset && last->op == OXFALL) {
                        if(typeswvar) {
                                setlineno(last);
                                yyerror("cannot fallthrough in type switch");
                        }
                        if(l->next == nil) {
                                setlineno(last);
                                yyerror("cannot fallthrough final case in switch");
                        }
                        last->op = OFALL;
                } else
                        stat = list(stat, br);
        }

        stat = list(stat, br);
        if(def)
                cas = list(cas, def);

        sw->list = cas;
        sw->nbody = stat;
        lineno = lno;
}

static Case*
mkcaselist(Node *sw, int arg)
{
        Node *n;
        Case *c, *c1, *c2;
        NodeList *l;
        int ord;

        c = C;
        ord = 0;

        for(l=sw->list; l; l=l->next) {
                n = l->n;
                c1 = mal(sizeof(*c1));
                c1->link = c;
                c = c1;

                ord++;
                if((uint16)ord != ord)
                        fatal("too many cases in switch");
                c->ordinal = ord;
                c->node = n;

                if(n->left == N) {
                        c->type = Tdefault;
                        continue;
                }

                switch(arg) {
                case Stype:
                        c->hash = 0;
                        if(n->left->op == OLITERAL) {
                                c->type = Ttypenil;
                                continue;
                        }
                        if(istype(n->left->type, TINTER)) {
                                c->type = Ttypevar;
                                continue;
                        }

                        c->hash = typehash(n->left->type);
                        c->type = Ttypeconst;
                        continue;

                case Snorm:
                case Strue:
                case Sfalse:
                        c->type = Texprvar;
                        c->hash = typehash(n->left->type);
                        switch(consttype(n->left)) {
                        case CTFLT:
                        case CTINT:
                        case CTRUNE:
                        case CTSTR:
                                c->type = Texprconst;
                        }
                        continue;
                }
        }

        if(c == C)
                return C;

        // sort by value and diagnose duplicate cases
        switch(arg) {
        case Stype:
                c = csort(c, typecmp);
                for(c1=c; c1!=C; c1=c1->link) {
                        for(c2=c1->link; c2!=C && c2->hash==c1->hash; c2=c2->link) {
                                if(c1->type == Ttypenil || c1->type == Tdefault)
                                        break;
                                if(c2->type == Ttypenil || c2->type == Tdefault)
                                        break;
                                if(!eqtype(c1->node->left->type, c2->node->left->type))
                                        continue;
                                yyerrorl(c2->node->lineno, "duplicate case %T in type switch\n\tprevious case at %L", c2->node->left->type, c1->node->lineno);
                        }
                }
                break;
        case Snorm:
        case Strue:
        case Sfalse:
                c = csort(c, exprcmp);
                for(c1=c; c1->link!=C; c1=c1->link) {
                        if(exprcmp(c1, c1->link) != 0)
                                continue;
                        setlineno(c1->link->node);
                        yyerror("duplicate case %N in switch\n\tprevious case at %L", c1->node->left, c1->node->lineno);
                }
                break;
        }

        // put list back in processing order
        c = csort(c, ordlcmp);
        return c;
}

static  Node*   exprname;

static Node*
exprbsw(Case *c0, int ncase, int arg)
{
        NodeList *cas;
        Node *a, *n;
        Case *c;
        int i, half, lno;

        cas = nil;
        if(ncase < Ncase) {
                for(i=0; i<ncase; i++) {
                        n = c0->node;
                        lno = setlineno(n);

                        if(assignop(n->left->type, exprname->type, nil) == OCONVIFACE ||
                           assignop(exprname->type, n->left->type, nil) == OCONVIFACE)
                                goto snorm;

                        switch(arg) {
                        case Strue:
                                a = nod(OIF, N, N);
                                a->ntest = n->left;                     // if val
                                a->nbody = list1(n->right);                     // then goto l
                                break;

                        case Sfalse:
                                a = nod(OIF, N, N);
                                a->ntest = nod(ONOT, n->left, N);       // if !val
                                typecheck(&a->ntest, Erv);
                                a->nbody = list1(n->right);                     // then goto l
                                break;

                        default:
                        snorm:
                                a = nod(OIF, N, N);
                                a->ntest = nod(OEQ, exprname, n->left); // if name == val
                                typecheck(&a->ntest, Erv);
                                a->nbody = list1(n->right);                     // then goto l
                                break;
                        }

                        cas = list(cas, a);
                        c0 = c0->link;
                        lineno = lno;
                }
                return liststmt(cas);
        }

        // find the middle and recur
        c = c0;
        half = ncase>>1;
        for(i=1; i<half; i++)
                c = c->link;
        a = nod(OIF, N, N);
        a->ntest = nod(OLE, exprname, c->node->left);
        typecheck(&a->ntest, Erv);
        a->nbody = list1(exprbsw(c0, half, arg));
        a->nelse = list1(exprbsw(c->link, ncase-half, arg));
        return a;
}

/*
 * normal (expression) switch.
 * rebulid case statements into if .. goto
 */
static void
exprswitch(Node *sw)
{
        Node *def;
        NodeList *cas;
        Node *a;
        Case *c0, *c, *c1;
        Type *t;
        int arg, ncase;

        casebody(sw, N);

        arg = Snorm;
        if(isconst(sw->ntest, CTBOOL)) {
                arg = Strue;
                if(sw->ntest->val.u.bval == 0)
                        arg = Sfalse;
        }
        walkexpr(&sw->ntest, &sw->ninit);
        t = sw->type;
        if(t == T)
                return;

        /*
         * convert the switch into OIF statements
         */
        exprname = N;
        cas = nil;
        if(arg != Strue && arg != Sfalse) {
                exprname = temp(sw->ntest->type);
                cas = list1(nod(OAS, exprname, sw->ntest));
                typechecklist(cas, Etop);
        } else {
                exprname = nodbool(arg == Strue);
        }

        c0 = mkcaselist(sw, arg);
        if(c0 != C && c0->type == Tdefault) {
                def = c0->node->right;
                c0 = c0->link;
        } else {
                def = nod(OBREAK, N, N);
        }

loop:
        if(c0 == C) {
                cas = list(cas, def);
                sw->nbody = concat(cas, sw->nbody);
                sw->list = nil;
                walkstmtlist(sw->nbody);
                return;
        }

        // deal with the variables one-at-a-time
        if(!okforcmp[t->etype] || c0->type != Texprconst) {
                a = exprbsw(c0, 1, arg);
                cas = list(cas, a);
                c0 = c0->link;
                goto loop;
        }

        // do binary search on run of constants
        ncase = 1;
        for(c=c0; c->link!=C; c=c->link) {
                if(c->link->type != Texprconst)
                        break;
                ncase++;
        }

        // break the chain at the count
        c1 = c->link;
        c->link = C;

        // sort and compile constants
        c0 = csort(c0, exprcmp);
        a = exprbsw(c0, ncase, arg);
        cas = list(cas, a);

        c0 = c1;
        goto loop;

}

static  Node*   hashname;
static  Node*   facename;
static  Node*   boolname;

static Node*
typeone(Node *t)
{
        NodeList *init;
        Node *a, *b, *var;

        var = t->nname;
        init = nil;
        if(var == N) {
                typecheck(&nblank, Erv | Easgn);
                var = nblank;
        } else
                init = list1(nod(ODCL, var, N));

        a = nod(OAS2, N, N);
        a->list = list(list1(var), boolname);   // var,bool =
        b = nod(ODOTTYPE, facename, N);
        b->type = t->left->type;                // interface.(type)
        a->rlist = list1(b);
        typecheck(&a, Etop);
        init = list(init, a);

        b = nod(OIF, N, N);
        b->ntest = boolname;
        b->nbody = list1(t->right);             // if bool { goto l }
        a = liststmt(list(init, b));
        return a;
}

static Node*
typebsw(Case *c0, int ncase)
{
        NodeList *cas;
        Node *a, *n;
        Case *c;
        int i, half;

        cas = nil;

        if(ncase < Ncase) {
                for(i=0; i<ncase; i++) {
                        n = c0->node;
                        if(c0->type != Ttypeconst)
                                fatal("typebsw");
                        a = nod(OIF, N, N);
                        a->ntest = nod(OEQ, hashname, nodintconst(c0->hash));
                        typecheck(&a->ntest, Erv);
                        a->nbody = list1(n->right);
                        cas = list(cas, a);
                        c0 = c0->link;
                }
                return liststmt(cas);
        }

        // find the middle and recur
        c = c0;
        half = ncase>>1;
        for(i=1; i<half; i++)
                c = c->link;
        a = nod(OIF, N, N);
        a->ntest = nod(OLE, hashname, nodintconst(c->hash));
        typecheck(&a->ntest, Erv);
        a->nbody = list1(typebsw(c0, half));
        a->nelse = list1(typebsw(c->link, ncase-half));
        return a;
}

/*
 * convert switch of the form
 *      switch v := i.(type) { case t1: ..; case t2: ..; }
 * into if statements
 */
static void
typeswitch(Node *sw)
{
        Node *def;
        NodeList *cas, *hash;
        Node *a, *n;
        Case *c, *c0, *c1;
        int ncase;
        Type *t;
        Val v;

        if(sw->ntest == nil)
                return;
        if(sw->ntest->right == nil) {
                setlineno(sw);
                yyerror("type switch must have an assignment");
                return;
        }
        walkexpr(&sw->ntest->right, &sw->ninit);
        if(!istype(sw->ntest->right->type, TINTER)) {
                yyerror("type switch must be on an interface");
                return;
        }
        cas = nil;

        /*
         * predeclare temporary variables
         * and the boolean var
         */
        facename = temp(sw->ntest->right->type);
        a = nod(OAS, facename, sw->ntest->right);
        typecheck(&a, Etop);
        cas = list(cas, a);

        casebody(sw, facename);

        boolname = temp(types[TBOOL]);
        typecheck(&boolname, Erv);

        hashname = temp(types[TUINT32]);
        typecheck(&hashname, Erv);

        t = sw->ntest->right->type;
        if(isnilinter(t))
                a = syslook("efacethash", 1);
        else
                a = syslook("ifacethash", 1);
        argtype(a, t);
        a = nod(OCALL, a, N);
        a->list = list1(facename);
        a = nod(OAS, hashname, a);
        typecheck(&a, Etop);
        cas = list(cas, a);

        c0 = mkcaselist(sw, Stype);
        if(c0 != C && c0->type == Tdefault) {
                def = c0->node->right;
                c0 = c0->link;
        } else {
                def = nod(OBREAK, N, N);
        }
        
        /*
         * insert if statement into each case block
         */
        for(c=c0; c!=C; c=c->link) {
                n = c->node;
                switch(c->type) {

                case Ttypenil:
                        v.ctype = CTNIL;
                        a = nod(OIF, N, N);
                        a->ntest = nod(OEQ, facename, nodlit(v));
                        typecheck(&a->ntest, Erv);
                        a->nbody = list1(n->right);             // if i==nil { goto l }
                        n->right = a;
                        break;
                
                case Ttypevar:
                case Ttypeconst:
                        n->right = typeone(n);
                        break;
                }
        }

        /*
         * generate list of if statements, binary search for constant sequences
         */
        while(c0 != C) {
                if(c0->type != Ttypeconst) {
                        n = c0->node;
                        cas = list(cas, n->right);
                        c0=c0->link;
                        continue;
                }
                
                // identify run of constants
                c1 = c = c0;
                while(c->link!=C && c->link->type==Ttypeconst)
                        c = c->link;
                c0 = c->link;
                c->link = nil;

                // sort by hash
                c1 = csort(c1, typecmp);
                
                // for debugging: linear search
                if(0) {
                        for(c=c1; c!=C; c=c->link) {
                                n = c->node;
                                cas = list(cas, n->right);
                        }
                        continue;
                }

                // combine adjacent cases with the same hash
                ncase = 0;
                for(c=c1; c!=C; c=c->link) {
                        ncase++;
                        hash = list1(c->node->right);
                        while(c->link != C && c->link->hash == c->hash) {
                                hash = list(hash, c->link->node->right);
                                c->link = c->link->link;
                        }
                        c->node->right = liststmt(hash);
                }
                
                // binary search among cases to narrow by hash
                cas = list(cas, typebsw(c1, ncase));
        }
        if(nerrors == 0) {
                cas = list(cas, def);
                sw->nbody = concat(cas, sw->nbody);
                sw->list = nil;
                walkstmtlist(sw->nbody);
        }
}

void
walkswitch(Node *sw)
{
        /*
         * reorder the body into (OLIST, cases, statements)
         * cases have OGOTO into statements.
         * both have inserted OBREAK statements
         */
        if(sw->ntest == N) {
                sw->ntest = nodbool(1);
                typecheck(&sw->ntest, Erv);
        }

        if(sw->ntest->op == OTYPESW) {
                typeswitch(sw);
//dump("sw", sw);
                return;
        }
        exprswitch(sw);
        // Discard old AST elements after a walk. They can confuse racewealk.
        sw->ntest = nil;
        sw->list = nil;
}

/*
 * type check switch statement
 */
void
typecheckswitch(Node *n)
{
        int top, lno, ptr;
        char *nilonly;
        Type *t, *badtype, *missing, *have;
        NodeList *l, *ll;
        Node *ncase, *nvar;
        Node *def;

        lno = lineno;
        typechecklist(n->ninit, Etop);
        nilonly = nil;

        if(n->ntest != N && n->ntest->op == OTYPESW) {
                // type switch
                top = Etype;
                typecheck(&n->ntest->right, Erv);
                t = n->ntest->right->type;
                if(t != T && t->etype != TINTER)
                        yyerror("cannot type switch on non-interface value %lN", n->ntest->right);
        } else {
                // value switch
                top = Erv;
                if(n->ntest) {
                        typecheck(&n->ntest, Erv);
                        defaultlit(&n->ntest, T);
                        t = n->ntest->type;
                } else
                        t = types[TBOOL];
                if(t) {
                        if(!okforeq[t->etype])
                                yyerror("cannot switch on %lN", n->ntest);
                        else if(t->etype == TARRAY && !isfixedarray(t))
                                nilonly = "slice";
                        else if(t->etype == TARRAY && isfixedarray(t) && algtype1(t, nil) == ANOEQ)
                                yyerror("cannot switch on %lN", n->ntest);
                        else if(t->etype == TSTRUCT && algtype1(t, &badtype) == ANOEQ)
                                yyerror("cannot switch on %lN (struct containing %T cannot be compared)", n->ntest, badtype);
                        else if(t->etype == TFUNC)
                                nilonly = "func";
                        else if(t->etype == TMAP)
                                nilonly = "map";
                }
        }
        n->type = t;

        def = N;
        for(l=n->list; l; l=l->next) {
                ncase = l->n;
                setlineno(n);
                if(ncase->list == nil) {
                        // default
                        if(def != N)
                                yyerror("multiple defaults in switch (first at %L)", def->lineno);
                        else
                                def = ncase;
                } else {
                        for(ll=ncase->list; ll; ll=ll->next) {
                                setlineno(ll->n);
                                typecheck(&ll->n, Erv | Etype);
                                if(ll->n->type == T || t == T)
                                        continue;
                                setlineno(ncase);
                                switch(top) {
                                case Erv:       // expression switch
                                        defaultlit(&ll->n, t);
                                        if(ll->n->op == OTYPE)
                                                yyerror("type %T is not an expression", ll->n->type);
                                        else if(ll->n->type != T && !assignop(ll->n->type, t, nil) && !assignop(t, ll->n->type, nil)) {
                                                if(n->ntest)
                                                        yyerror("invalid case %N in switch on %N (mismatched types %T and %T)", ll->n, n->ntest, ll->n->type, t);
                                                else
                                                        yyerror("invalid case %N in switch (mismatched types %T and bool)", ll->n, ll->n->type);
                                        } else if(nilonly && !isconst(ll->n, CTNIL)) {
                                                yyerror("invalid case %N in switch (can only compare %s %N to nil)", ll->n, nilonly, n->ntest);
                                        }
                                        break;
                                case Etype:     // type switch
                                        if(ll->n->op == OLITERAL && istype(ll->n->type, TNIL)) {
                                                ;
                                        } else if(ll->n->op != OTYPE && ll->n->type != T) {  // should this be ||?
                                                yyerror("%lN is not a type", ll->n);
                                                // reset to original type
                                                ll->n = n->ntest->right;
                                        } else if(ll->n->type->etype != TINTER && t->etype == TINTER && !implements(ll->n->type, t, &missing, &have, &ptr)) {
                                                if(have && !missing->broke && !have->broke)
                                                        yyerror("impossible type switch case: %lN cannot have dynamic type %T"
                                                                " (wrong type for %S method)\n\thave %S%hT\n\twant %S%hT",
                                                                n->ntest->right, ll->n->type, missing->sym, have->sym, have->type,
                                                                missing->sym, missing->type);
                                                else if(!missing->broke)
                                                        yyerror("impossible type switch case: %lN cannot have dynamic type %T"
                                                                " (missing %S method)", n->ntest->right, ll->n->type, missing->sym);
                                        }
                                        break;
                                }
                        }
                }
                if(top == Etype && n->type != T) {
                        ll = ncase->list;
                        nvar = ncase->nname;
                        if(nvar != N) {
                                if(ll && ll->next == nil && ll->n->type != T && !istype(ll->n->type, TNIL)) {
                                        // single entry type switch
                                        nvar->ntype = typenod(ll->n->type);
                                } else {
                                        // multiple entry type switch or default
                                        nvar->ntype = typenod(n->type);
                                }
                        }
                }
                typechecklist(ncase->nbody, Etop);
        }

        lineno = lno;
}

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