// 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 #include #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) /*c2go Case *C; */ 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; inode; 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; ilink; 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; inode; 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; ilink; 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; }