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