2009-03-05 16:49:34 -07:00
|
|
|
// 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 "go.h"
|
|
|
|
|
2009-03-06 18:50:43 -07:00
|
|
|
enum
|
|
|
|
{
|
|
|
|
Snorm = 0,
|
|
|
|
Strue,
|
|
|
|
Sfalse,
|
|
|
|
Stype,
|
2009-03-17 14:58:38 -06:00
|
|
|
|
2009-03-22 21:54:21 -06:00
|
|
|
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
|
2009-03-17 14:58:38 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct Case Case;
|
|
|
|
struct Case
|
|
|
|
{
|
|
|
|
Node* node; // points at case statement
|
|
|
|
uint32 hash; // hash of a type switch
|
2009-03-22 21:54:21 -06:00
|
|
|
uint8 type; // type of case
|
2009-03-17 14:58:38 -06:00
|
|
|
uint8 diag; // suppress multiple diagnostics
|
2009-03-22 21:54:21 -06:00
|
|
|
uint16 ordinal; // position in switch
|
2009-03-17 14:58:38 -06:00
|
|
|
Case* link; // linked list to link
|
2009-03-06 18:50:43 -07:00
|
|
|
};
|
2009-03-17 14:58:38 -06:00
|
|
|
#define C ((Case*)nil)
|
2009-03-06 18:50:43 -07:00
|
|
|
|
2009-05-30 18:06:51 -06:00
|
|
|
Type*
|
|
|
|
notideal(Type *t)
|
|
|
|
{
|
|
|
|
if(t != T && t->etype == TIDEAL)
|
|
|
|
return T;
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
2009-03-22 21:54:21 -06:00
|
|
|
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;
|
|
|
|
|
|
|
|
ct = n1->val.ctype;
|
2009-07-06 17:05:48 -06:00
|
|
|
if(ct != n2->val.ctype) {
|
|
|
|
// invalid program, but return a sort
|
|
|
|
// order so that we can give a better
|
|
|
|
// error later.
|
|
|
|
return ct - n2->val.ctype;
|
|
|
|
}
|
2009-03-22 21:54:21 -06:00
|
|
|
|
|
|
|
// sort by constant value
|
|
|
|
n = 0;
|
|
|
|
switch(ct) {
|
|
|
|
case CTFLT:
|
|
|
|
n = mpcmpfltflt(n1->val.u.fval, n2->val.u.fval);
|
|
|
|
break;
|
|
|
|
case CTINT:
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-03-05 16:49:34 -07:00
|
|
|
Node*
|
2009-07-10 17:29:26 -06:00
|
|
|
newlabel(void)
|
2009-03-05 16:49:34 -07:00
|
|
|
{
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
void
|
2009-09-09 01:18:16 -06:00
|
|
|
casebody(Node *sw, Node *typeswvar)
|
2009-03-05 16:49:34 -07:00
|
|
|
{
|
2009-07-17 02:00:44 -06:00
|
|
|
Node *os, *oc, *n, *c, *last;
|
|
|
|
Node *def;
|
|
|
|
NodeList *cas, *stat, *l, *lc;
|
2009-03-05 16:49:34 -07:00
|
|
|
Node *go, *br;
|
2009-09-09 01:18:16 -06:00
|
|
|
int32 lno, needvar;
|
2009-03-05 16:49:34 -07:00
|
|
|
|
|
|
|
lno = setlineno(sw);
|
2009-07-17 02:00:44 -06:00
|
|
|
if(sw->list == nil)
|
2009-03-05 16:49:34 -07:00
|
|
|
return;
|
|
|
|
|
2009-07-17 02:00:44 -06:00
|
|
|
cas = nil; // cases
|
|
|
|
stat = nil; // statements
|
2009-03-05 16:49:34 -07:00
|
|
|
def = N; // defaults
|
|
|
|
os = N; // last statement
|
|
|
|
oc = N; // last case
|
|
|
|
br = nod(OBREAK, N, N);
|
|
|
|
|
2009-07-17 02:00:44 -06:00
|
|
|
for(l=sw->list; l; l=l->next) {
|
|
|
|
n = l->n;
|
2009-05-30 22:18:15 -06:00
|
|
|
lno = setlineno(n);
|
|
|
|
if(n->op != OXCASE)
|
|
|
|
fatal("casebody %O", n->op);
|
|
|
|
n->op = OCASE;
|
2009-09-09 01:18:16 -06:00
|
|
|
needvar = count(n->list) != 1 || n->list->n->op == OLITERAL;
|
2009-05-30 22:18:15 -06:00
|
|
|
|
|
|
|
go = nod(OGOTO, newlabel(), N);
|
2009-07-17 02:00:44 -06:00
|
|
|
if(n->list == nil) {
|
2009-05-30 22:18:15 -06:00
|
|
|
if(def != N)
|
|
|
|
yyerror("more than one default case");
|
|
|
|
// reuse original default case
|
|
|
|
n->right = go;
|
|
|
|
def = n;
|
|
|
|
}
|
2009-03-05 16:49:34 -07:00
|
|
|
|
2009-07-17 02:00:44 -06:00
|
|
|
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));
|
2009-05-30 22:18:15 -06:00
|
|
|
}
|
|
|
|
}
|
2009-03-05 16:49:34 -07:00
|
|
|
|
2009-05-30 22:18:15 -06:00
|
|
|
stat = list(stat, nod(OLABEL, go->left, N));
|
2009-09-09 01:18:16 -06:00
|
|
|
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);
|
|
|
|
}
|
2009-07-17 02:00:44 -06:00
|
|
|
stat = concat(stat, n->nbody);
|
2009-03-05 16:49:34 -07:00
|
|
|
|
2009-05-30 22:18:15 -06:00
|
|
|
// botch - shouldnt fall thru declaration
|
2009-07-17 02:00:44 -06:00
|
|
|
last = stat->end->n;
|
|
|
|
if(last->op == OXFALL)
|
|
|
|
last->op = OFALL;
|
2009-05-30 22:18:15 -06:00
|
|
|
else
|
|
|
|
stat = list(stat, br);
|
2009-03-22 21:54:21 -06:00
|
|
|
}
|
2009-03-05 16:49:34 -07:00
|
|
|
|
2009-05-30 22:18:15 -06:00
|
|
|
stat = list(stat, br);
|
2009-07-17 02:00:44 -06:00
|
|
|
if(def)
|
|
|
|
cas = list(cas, def);
|
2009-03-22 21:54:21 -06:00
|
|
|
|
2009-07-17 02:00:44 -06:00
|
|
|
sw->list = cas;
|
|
|
|
sw->nbody = stat;
|
2009-05-30 22:18:15 -06:00
|
|
|
lineno = lno;
|
2009-03-05 16:49:34 -07:00
|
|
|
}
|
|
|
|
|
2009-03-22 21:54:21 -06:00
|
|
|
Case*
|
|
|
|
mkcaselist(Node *sw, int arg)
|
2009-03-05 16:49:34 -07:00
|
|
|
{
|
2009-03-22 21:54:21 -06:00
|
|
|
Node *n;
|
|
|
|
Case *c, *c1;
|
2009-07-17 02:00:44 -06:00
|
|
|
NodeList *l;
|
2009-03-22 21:54:21 -06:00
|
|
|
int ord;
|
2009-03-06 18:50:43 -07:00
|
|
|
|
2009-03-22 21:54:21 -06:00
|
|
|
c = C;
|
|
|
|
ord = 0;
|
2009-03-05 16:49:34 -07:00
|
|
|
|
2009-07-17 02:00:44 -06:00
|
|
|
for(l=sw->list; l; l=l->next) {
|
|
|
|
n = l->n;
|
|
|
|
c1 = mal(sizeof(*c1));
|
|
|
|
c1->link = c;
|
|
|
|
c = c1;
|
2009-03-22 21:54:21 -06:00
|
|
|
|
2009-07-17 02:00:44 -06:00
|
|
|
ord++;
|
|
|
|
c->ordinal = ord;
|
|
|
|
c->node = n;
|
2009-03-05 16:49:34 -07:00
|
|
|
|
2009-07-17 02:00:44 -06:00
|
|
|
if(n->left == N) {
|
|
|
|
c->type = Tdefault;
|
|
|
|
continue;
|
2009-03-22 21:54:21 -06:00
|
|
|
}
|
2009-03-07 18:33:42 -07:00
|
|
|
|
2009-07-17 02:00:44 -06:00
|
|
|
switch(arg) {
|
|
|
|
case Stype:
|
|
|
|
c->hash = 0;
|
2009-09-09 01:18:16 -06:00
|
|
|
if(n->left->op == OLITERAL) {
|
2009-07-17 02:00:44 -06:00
|
|
|
c->type = Ttypenil;
|
|
|
|
continue;
|
|
|
|
}
|
2009-09-09 01:18:16 -06:00
|
|
|
if(istype(n->left->type, TINTER)) {
|
2009-07-17 02:00:44 -06:00
|
|
|
c->type = Ttypevar;
|
|
|
|
continue;
|
|
|
|
}
|
2009-03-05 16:49:34 -07:00
|
|
|
|
2009-09-09 01:18:16 -06:00
|
|
|
c->hash = typehash(n->left->type);
|
2009-07-17 02:00:44 -06:00
|
|
|
c->type = Ttypeconst;
|
|
|
|
continue;
|
|
|
|
|
|
|
|
case Snorm:
|
|
|
|
case Strue:
|
|
|
|
case Sfalse:
|
|
|
|
c->type = Texprvar;
|
|
|
|
switch(consttype(n->left)) {
|
|
|
|
case CTFLT:
|
|
|
|
case CTINT:
|
|
|
|
case CTSTR:
|
|
|
|
c->type = Texprconst;
|
|
|
|
}
|
|
|
|
continue;
|
2009-03-22 21:54:21 -06:00
|
|
|
}
|
|
|
|
}
|
2009-03-05 16:49:34 -07:00
|
|
|
|
2009-03-22 21:54:21 -06:00
|
|
|
if(c == C)
|
|
|
|
return C;
|
|
|
|
|
|
|
|
// sort by value and diagnose duplicate cases
|
|
|
|
switch(arg) {
|
|
|
|
case Stype:
|
|
|
|
c = csort(c, typecmp);
|
|
|
|
for(c1=c; c1->link!=C; c1=c1->link) {
|
|
|
|
if(typecmp(c1, c1->link) != 0)
|
|
|
|
continue;
|
|
|
|
setlineno(c1->link->node);
|
2009-10-12 12:03:48 -06:00
|
|
|
yyerror("duplicate case in switch\n\tprevious case at %L", c1->node->lineno);
|
2009-03-22 21:54:21 -06:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Snorm:
|
|
|
|
case Strue:
|
2009-03-06 18:50:43 -07:00
|
|
|
case Sfalse:
|
2009-03-22 21:54:21 -06:00
|
|
|
c = csort(c, exprcmp);
|
|
|
|
for(c1=c; c1->link!=C; c1=c1->link) {
|
|
|
|
if(exprcmp(c1, c1->link) != 0)
|
|
|
|
continue;
|
|
|
|
setlineno(c1->link->node);
|
2009-10-12 12:03:48 -06:00
|
|
|
yyerror("duplicate case in switch\n\tprevious case at %L", c1->node->lineno);
|
2009-03-22 21:54:21 -06:00
|
|
|
}
|
2009-03-05 16:49:34 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-03-22 21:54:21 -06:00
|
|
|
// put list back in processing order
|
|
|
|
c = csort(c, ordlcmp);
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Node* exprname;
|
|
|
|
|
|
|
|
Node*
|
|
|
|
exprbsw(Case *c0, int ncase, int arg)
|
|
|
|
{
|
2009-07-17 02:00:44 -06:00
|
|
|
NodeList *cas;
|
2009-03-22 21:54:21 -06:00
|
|
|
Node *a, *n;
|
|
|
|
Case *c;
|
2009-07-06 17:05:48 -06:00
|
|
|
int i, half, lno;
|
2009-03-22 21:54:21 -06:00
|
|
|
|
2009-07-17 02:00:44 -06:00
|
|
|
cas = nil;
|
2009-03-22 21:54:21 -06:00
|
|
|
if(ncase < Ncase) {
|
|
|
|
for(i=0; i<ncase; i++) {
|
|
|
|
n = c0->node;
|
2009-07-06 17:05:48 -06:00
|
|
|
lno = setlineno(n);
|
2009-03-22 21:54:21 -06:00
|
|
|
|
|
|
|
switch(arg) {
|
|
|
|
case Strue:
|
|
|
|
a = nod(OIF, N, N);
|
|
|
|
a->ntest = n->left; // if val
|
2009-07-17 02:00:44 -06:00
|
|
|
a->nbody = list1(n->right); // then goto l
|
2009-03-22 21:54:21 -06:00
|
|
|
break;
|
|
|
|
|
|
|
|
case Sfalse:
|
|
|
|
a = nod(OIF, N, N);
|
|
|
|
a->ntest = nod(ONOT, n->left, N); // if !val
|
2009-07-30 17:53:08 -06:00
|
|
|
typecheck(&a->ntest, Erv);
|
2009-07-17 02:00:44 -06:00
|
|
|
a->nbody = list1(n->right); // then goto l
|
2009-03-22 21:54:21 -06:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
a = nod(OIF, N, N);
|
|
|
|
a->ntest = nod(OEQ, exprname, n->left); // if name == val
|
2009-07-30 17:53:08 -06:00
|
|
|
typecheck(&a->ntest, Erv);
|
2009-07-17 02:00:44 -06:00
|
|
|
a->nbody = list1(n->right); // then goto l
|
2009-03-22 21:54:21 -06:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
cas = list(cas, a);
|
|
|
|
c0 = c0->link;
|
2009-07-06 17:05:48 -06:00
|
|
|
lineno = lno;
|
2009-03-22 21:54:21 -06:00
|
|
|
}
|
2009-07-17 02:00:44 -06:00
|
|
|
return liststmt(cas);
|
2009-03-22 21:54:21 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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);
|
2009-07-30 17:53:08 -06:00
|
|
|
typecheck(&a->ntest, Erv);
|
2009-07-17 02:00:44 -06:00
|
|
|
a->nbody = list1(exprbsw(c0, half, arg));
|
|
|
|
a->nelse = list1(exprbsw(c->link, ncase-half, arg));
|
2009-03-22 21:54:21 -06:00
|
|
|
return a;
|
2009-03-05 16:49:34 -07:00
|
|
|
}
|
|
|
|
|
2009-03-22 21:54:21 -06:00
|
|
|
/*
|
|
|
|
* normal (expression) switch.
|
|
|
|
* rebulid case statements into if .. goto
|
|
|
|
*/
|
2009-03-06 18:50:43 -07:00
|
|
|
void
|
2009-03-22 21:54:21 -06:00
|
|
|
exprswitch(Node *sw)
|
2009-03-06 18:50:43 -07:00
|
|
|
{
|
2009-07-17 02:00:44 -06:00
|
|
|
Node *def;
|
|
|
|
NodeList *cas;
|
2009-03-22 21:54:21 -06:00
|
|
|
Node *a;
|
|
|
|
Case *c0, *c, *c1;
|
2009-03-06 18:50:43 -07:00
|
|
|
Type *t;
|
2009-03-22 21:54:21 -06:00
|
|
|
int arg, ncase;
|
2009-03-06 18:50:43 -07:00
|
|
|
|
2009-09-09 01:18:16 -06:00
|
|
|
casebody(sw, N);
|
2009-03-06 18:50:43 -07:00
|
|
|
|
|
|
|
arg = Snorm;
|
2009-03-12 20:04:38 -06:00
|
|
|
if(isconst(sw->ntest, CTBOOL)) {
|
2009-03-06 18:50:43 -07:00
|
|
|
arg = Strue;
|
2009-03-17 20:10:32 -06:00
|
|
|
if(sw->ntest->val.u.bval == 0)
|
2009-03-06 18:50:43 -07:00
|
|
|
arg = Sfalse;
|
|
|
|
}
|
2009-07-31 10:29:28 -06:00
|
|
|
walkexpr(&sw->ntest, &sw->ninit);
|
2009-08-04 11:26:29 -06:00
|
|
|
t = sw->type;
|
2009-03-06 18:50:43 -07:00
|
|
|
if(t == T)
|
|
|
|
return;
|
2009-03-22 21:54:21 -06:00
|
|
|
|
2009-03-06 18:50:43 -07:00
|
|
|
/*
|
|
|
|
* convert the switch into OIF statements
|
|
|
|
*/
|
2009-03-22 21:54:21 -06:00
|
|
|
exprname = N;
|
2009-07-17 02:00:44 -06:00
|
|
|
cas = nil;
|
2009-03-22 21:54:21 -06:00
|
|
|
if(arg != Strue && arg != Sfalse) {
|
|
|
|
exprname = nod(OXXX, N, N);
|
|
|
|
tempname(exprname, sw->ntest->type);
|
2009-07-17 02:00:44 -06:00
|
|
|
cas = list1(nod(OAS, exprname, sw->ntest));
|
2009-07-30 17:53:08 -06:00
|
|
|
typechecklist(cas, Etop);
|
2009-03-07 18:33:42 -07:00
|
|
|
}
|
|
|
|
|
2009-03-22 21:54:21 -06:00
|
|
|
c0 = mkcaselist(sw, arg);
|
|
|
|
if(c0 != C && c0->type == Tdefault) {
|
|
|
|
def = c0->node->right;
|
|
|
|
c0 = c0->link;
|
|
|
|
} else {
|
|
|
|
def = nod(OBREAK, N, N);
|
2009-03-07 18:33:42 -07:00
|
|
|
}
|
|
|
|
|
2009-03-22 21:54:21 -06:00
|
|
|
loop:
|
|
|
|
if(c0 == C) {
|
|
|
|
cas = list(cas, def);
|
2009-07-17 02:00:44 -06:00
|
|
|
sw->nbody = concat(cas, sw->nbody);
|
|
|
|
sw->list = nil;
|
|
|
|
walkstmtlist(sw->nbody);
|
2009-03-22 21:54:21 -06:00
|
|
|
return;
|
2009-03-07 18:33:42 -07:00
|
|
|
}
|
|
|
|
|
2009-03-22 21:54:21 -06:00
|
|
|
// deal with the variables one-at-a-time
|
|
|
|
if(c0->type != Texprconst) {
|
|
|
|
a = exprbsw(c0, 1, arg);
|
|
|
|
cas = list(cas, a);
|
|
|
|
c0 = c0->link;
|
|
|
|
goto loop;
|
2009-03-07 18:33:42 -07:00
|
|
|
}
|
|
|
|
|
2009-03-22 21:54:21 -06:00
|
|
|
// do binary search on run of constants
|
|
|
|
ncase = 1;
|
|
|
|
for(c=c0; c->link!=C; c=c->link) {
|
|
|
|
if(c->link->type != Texprconst)
|
2009-03-07 18:33:42 -07:00
|
|
|
break;
|
2009-03-22 21:54:21 -06:00
|
|
|
ncase++;
|
2009-03-07 18:33:42 -07:00
|
|
|
}
|
|
|
|
|
2009-03-22 21:54:21 -06:00
|
|
|
// break the chain at the count
|
|
|
|
c1 = c->link;
|
|
|
|
c->link = C;
|
2009-03-07 18:33:42 -07:00
|
|
|
|
2009-03-22 21:54:21 -06:00
|
|
|
// sort and compile constants
|
|
|
|
c0 = csort(c0, exprcmp);
|
|
|
|
a = exprbsw(c0, ncase, arg);
|
|
|
|
cas = list(cas, a);
|
2009-03-07 18:33:42 -07:00
|
|
|
|
2009-03-22 21:54:21 -06:00
|
|
|
c0 = c1;
|
|
|
|
goto loop;
|
2009-03-07 18:33:42 -07:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2009-03-22 21:54:21 -06:00
|
|
|
static Node* hashname;
|
|
|
|
static Node* facename;
|
|
|
|
static Node* boolname;
|
2009-03-07 18:33:42 -07:00
|
|
|
|
|
|
|
Node*
|
2009-03-22 21:54:21 -06:00
|
|
|
typeone(Node *t)
|
2009-03-17 14:58:38 -06:00
|
|
|
{
|
2009-07-17 02:00:44 -06:00
|
|
|
NodeList *init;
|
|
|
|
Node *a, *b, *var;
|
2009-07-07 00:42:57 -06:00
|
|
|
|
2009-09-09 01:18:16 -06:00
|
|
|
var = t->nname;
|
|
|
|
init = nil;
|
|
|
|
if(var == N) {
|
|
|
|
typecheck(&nblank, Erv | Easgn);
|
|
|
|
var = nblank;
|
|
|
|
} else
|
|
|
|
init = list1(nod(ODCL, var, N));
|
2009-03-17 14:58:38 -06:00
|
|
|
|
2009-07-17 02:00:44 -06:00
|
|
|
a = nod(OAS2, N, N);
|
|
|
|
a->list = list(list1(var), boolname); // var,bool =
|
2009-03-22 21:54:21 -06:00
|
|
|
b = nod(ODOTTYPE, facename, N);
|
2009-09-09 01:18:16 -06:00
|
|
|
b->type = t->left->type; // interface.(type)
|
2009-07-17 02:00:44 -06:00
|
|
|
a->rlist = list1(b);
|
2009-07-30 17:53:08 -06:00
|
|
|
typecheck(&a, Etop);
|
2009-07-17 02:00:44 -06:00
|
|
|
init = list(init, a);
|
2009-03-17 14:58:38 -06:00
|
|
|
|
2009-03-22 21:54:21 -06:00
|
|
|
b = nod(OIF, N, N);
|
|
|
|
b->ntest = boolname;
|
2009-07-17 02:00:44 -06:00
|
|
|
b->nbody = list1(t->right); // if bool { goto l }
|
|
|
|
a = liststmt(list(init, b));
|
|
|
|
return a;
|
2009-03-17 14:58:38 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
Node*
|
|
|
|
typebsw(Case *c0, int ncase)
|
|
|
|
{
|
2009-07-17 02:00:44 -06:00
|
|
|
NodeList *cas;
|
2009-03-22 21:54:21 -06:00
|
|
|
Node *a, *n;
|
|
|
|
Case *c;
|
|
|
|
int i, half;
|
|
|
|
Val v;
|
2009-03-17 14:58:38 -06:00
|
|
|
|
2009-07-17 02:00:44 -06:00
|
|
|
cas = nil;
|
2009-03-17 14:58:38 -06:00
|
|
|
|
|
|
|
if(ncase < Ncase) {
|
|
|
|
for(i=0; i<ncase; i++) {
|
2009-03-22 21:54:21 -06:00
|
|
|
n = c0->node;
|
2009-03-17 14:58:38 -06:00
|
|
|
|
2009-03-22 21:54:21 -06:00
|
|
|
switch(c0->type) {
|
|
|
|
|
|
|
|
case Ttypenil:
|
|
|
|
v.ctype = CTNIL;
|
2009-03-17 14:58:38 -06:00
|
|
|
a = nod(OIF, N, N);
|
2009-03-22 21:54:21 -06:00
|
|
|
a->ntest = nod(OEQ, facename, nodlit(v));
|
2009-07-30 17:53:08 -06:00
|
|
|
typecheck(&a->ntest, Erv);
|
2009-07-17 02:00:44 -06:00
|
|
|
a->nbody = list1(n->right); // if i==nil { goto l }
|
2009-03-22 21:54:21 -06:00
|
|
|
cas = list(cas, a);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Ttypevar:
|
|
|
|
a = typeone(n);
|
|
|
|
cas = list(cas, a);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Ttypeconst:
|
|
|
|
a = nod(OIF, N, N);
|
|
|
|
a->ntest = nod(OEQ, hashname, nodintconst(c0->hash));
|
2009-07-30 17:53:08 -06:00
|
|
|
typecheck(&a->ntest, Erv);
|
2009-07-17 02:00:44 -06:00
|
|
|
a->nbody = list1(typeone(n));
|
2009-03-22 21:54:21 -06:00
|
|
|
cas = list(cas, a);
|
|
|
|
break;
|
2009-03-17 14:58:38 -06:00
|
|
|
}
|
2009-03-22 21:54:21 -06:00
|
|
|
c0 = c0->link;
|
2009-03-17 14:58:38 -06:00
|
|
|
}
|
2009-07-17 02:00:44 -06:00
|
|
|
return liststmt(cas);
|
2009-03-17 14:58:38 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// find the middle and recur
|
|
|
|
c = c0;
|
2009-03-22 21:54:21 -06:00
|
|
|
half = ncase>>1;
|
|
|
|
for(i=1; i<half; i++)
|
|
|
|
c = c->link;
|
2009-03-17 14:58:38 -06:00
|
|
|
a = nod(OIF, N, N);
|
|
|
|
a->ntest = nod(OLE, hashname, nodintconst(c->hash));
|
2009-07-30 17:53:08 -06:00
|
|
|
typecheck(&a->ntest, Erv);
|
2009-07-17 02:00:44 -06:00
|
|
|
a->nbody = list1(typebsw(c0, half));
|
|
|
|
a->nelse = list1(typebsw(c->link, ncase-half));
|
2009-03-17 14:58:38 -06:00
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* convert switch of the form
|
|
|
|
* switch v := i.(type) { case t1: ..; case t2: ..; }
|
|
|
|
* into if statements
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
typeswitch(Node *sw)
|
|
|
|
{
|
2009-07-17 02:00:44 -06:00
|
|
|
Node *def;
|
|
|
|
NodeList *cas;
|
2009-03-22 21:54:21 -06:00
|
|
|
Node *a;
|
|
|
|
Case *c, *c0, *c1;
|
2009-03-17 14:58:38 -06:00
|
|
|
int ncase;
|
2009-05-20 15:57:55 -06:00
|
|
|
Type *t;
|
2009-03-17 14:58:38 -06:00
|
|
|
|
2009-04-02 19:06:43 -06:00
|
|
|
if(sw->ntest == nil)
|
|
|
|
return;
|
|
|
|
if(sw->ntest->right == nil) {
|
|
|
|
setlineno(sw);
|
|
|
|
yyerror("type switch must have an assignment");
|
|
|
|
return;
|
|
|
|
}
|
2009-07-31 10:29:28 -06:00
|
|
|
walkexpr(&sw->ntest->right, &sw->ninit);
|
2009-03-17 14:58:38 -06:00
|
|
|
if(!istype(sw->ntest->right->type, TINTER)) {
|
|
|
|
yyerror("type switch must be on an interface");
|
|
|
|
return;
|
|
|
|
}
|
2009-07-17 02:00:44 -06:00
|
|
|
cas = nil;
|
2009-03-17 14:58:38 -06:00
|
|
|
|
|
|
|
/*
|
|
|
|
* predeclare temporary variables
|
|
|
|
* and the boolean var
|
|
|
|
*/
|
|
|
|
facename = nod(OXXX, N, N);
|
|
|
|
tempname(facename, sw->ntest->right->type);
|
|
|
|
a = nod(OAS, facename, sw->ntest->right);
|
2009-07-30 17:53:08 -06:00
|
|
|
typecheck(&a, Etop);
|
2009-03-17 14:58:38 -06:00
|
|
|
cas = list(cas, a);
|
|
|
|
|
2009-09-09 01:18:16 -06:00
|
|
|
casebody(sw, facename);
|
|
|
|
|
2009-03-17 14:58:38 -06:00
|
|
|
boolname = nod(OXXX, N, N);
|
|
|
|
tempname(boolname, types[TBOOL]);
|
2009-07-30 17:53:08 -06:00
|
|
|
typecheck(&boolname, Erv);
|
2009-03-17 14:58:38 -06:00
|
|
|
|
|
|
|
hashname = nod(OXXX, N, N);
|
|
|
|
tempname(hashname, types[TUINT32]);
|
2009-07-30 17:53:08 -06:00
|
|
|
typecheck(&hashname, Erv);
|
2009-03-17 14:58:38 -06:00
|
|
|
|
2009-05-20 15:57:55 -06:00
|
|
|
t = sw->ntest->right->type;
|
|
|
|
if(isnilinter(t))
|
|
|
|
a = syslook("efacethash", 1);
|
|
|
|
else
|
|
|
|
a = syslook("ifacethash", 1);
|
|
|
|
argtype(a, t);
|
2009-07-17 02:00:44 -06:00
|
|
|
a = nod(OCALL, a, N);
|
|
|
|
a->list = list1(facename);
|
2009-03-17 14:58:38 -06:00
|
|
|
a = nod(OAS, hashname, a);
|
2009-07-30 17:53:08 -06:00
|
|
|
typecheck(&a, Etop);
|
2009-03-17 14:58:38 -06:00
|
|
|
cas = list(cas, a);
|
|
|
|
|
2009-03-22 21:54:21 -06:00
|
|
|
c0 = mkcaselist(sw, Stype);
|
|
|
|
if(c0 != C && c0->type == Tdefault) {
|
|
|
|
def = c0->node->right;
|
|
|
|
c0 = c0->link;
|
|
|
|
} else {
|
|
|
|
def = nod(OBREAK, N, N);
|
|
|
|
}
|
2009-03-17 14:58:38 -06:00
|
|
|
|
|
|
|
loop:
|
2009-03-22 21:54:21 -06:00
|
|
|
if(c0 == C) {
|
|
|
|
cas = list(cas, def);
|
2009-07-17 02:00:44 -06:00
|
|
|
sw->nbody = concat(cas, sw->nbody);
|
|
|
|
sw->list = nil;
|
|
|
|
walkstmtlist(sw->nbody);
|
2009-03-17 14:58:38 -06:00
|
|
|
return;
|
|
|
|
}
|
2009-03-22 21:54:21 -06:00
|
|
|
|
|
|
|
// deal with the variables one-at-a-time
|
|
|
|
if(c0->type != Ttypeconst) {
|
|
|
|
a = typebsw(c0, 1);
|
|
|
|
cas = list(cas, a);
|
|
|
|
c0 = c0->link;
|
2009-03-17 14:58:38 -06:00
|
|
|
goto loop;
|
|
|
|
}
|
2009-03-22 21:54:21 -06:00
|
|
|
|
|
|
|
// do binary search on run of constants
|
|
|
|
ncase = 1;
|
|
|
|
for(c=c0; c->link!=C; c=c->link) {
|
|
|
|
if(c->link->type != Ttypeconst)
|
|
|
|
break;
|
|
|
|
ncase++;
|
2009-03-17 14:58:38 -06:00
|
|
|
}
|
|
|
|
|
2009-03-22 21:54:21 -06:00
|
|
|
// break the chain at the count
|
|
|
|
c1 = c->link;
|
|
|
|
c->link = C;
|
|
|
|
|
|
|
|
// sort and compile constants
|
|
|
|
c0 = csort(c0, typecmp);
|
|
|
|
a = typebsw(c0, ncase);
|
|
|
|
cas = list(cas, a);
|
2009-03-17 14:58:38 -06:00
|
|
|
|
2009-03-22 21:54:21 -06:00
|
|
|
c0 = c1;
|
2009-03-17 14:58:38 -06:00
|
|
|
goto loop;
|
|
|
|
}
|
2009-03-22 21:54:21 -06:00
|
|
|
|
|
|
|
void
|
|
|
|
walkswitch(Node *sw)
|
|
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
|
|
* reorder the body into (OLIST, cases, statements)
|
|
|
|
* cases have OGOTO into statements.
|
|
|
|
* both have inserted OBREAK statements
|
|
|
|
*/
|
2009-07-17 02:00:44 -06:00
|
|
|
walkstmtlist(sw->ninit);
|
2009-07-30 17:53:08 -06:00
|
|
|
if(sw->ntest == N) {
|
2009-03-22 21:54:21 -06:00
|
|
|
sw->ntest = nodbool(1);
|
2009-07-30 17:53:08 -06:00
|
|
|
typecheck(&sw->ntest, Erv);
|
|
|
|
}
|
2009-09-09 01:18:16 -06:00
|
|
|
|
2009-03-22 21:54:21 -06:00
|
|
|
if(sw->ntest->op == OTYPESW) {
|
|
|
|
typeswitch(sw);
|
2009-09-09 01:18:16 -06:00
|
|
|
//dump("sw", sw);
|
2009-03-22 21:54:21 -06:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
exprswitch(sw);
|
|
|
|
}
|
2009-08-04 11:26:29 -06:00
|
|
|
|
|
|
|
/*
|
|
|
|
* type check switch statement
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
typecheckswitch(Node *n)
|
|
|
|
{
|
|
|
|
int top, lno;
|
|
|
|
Type *t;
|
|
|
|
NodeList *l, *ll;
|
2009-09-09 01:18:16 -06:00
|
|
|
Node *ncase, *nvar;
|
2009-08-04 11:26:29 -06:00
|
|
|
Node *def;
|
|
|
|
|
|
|
|
lno = lineno;
|
|
|
|
typechecklist(n->ninit, Etop);
|
|
|
|
|
|
|
|
if(n->ntest != N && n->ntest->op == OTYPESW) {
|
|
|
|
// type switch
|
|
|
|
top = Etype;
|
2009-09-09 01:18:16 -06:00
|
|
|
typecheck(&n->ntest->right, Erv);
|
|
|
|
t = n->ntest->right->type;
|
2009-08-04 11:26:29 -06:00
|
|
|
if(t != T && t->etype != TINTER)
|
2009-09-09 01:18:16 -06:00
|
|
|
yyerror("cannot type switch on non-interface value %+N", n->ntest->right);
|
2009-08-04 11:26:29 -06:00
|
|
|
} else {
|
|
|
|
// value switch
|
|
|
|
top = Erv;
|
|
|
|
if(n->ntest) {
|
|
|
|
typecheck(&n->ntest, Erv);
|
|
|
|
defaultlit(&n->ntest, T);
|
|
|
|
t = n->ntest->type;
|
|
|
|
} else
|
|
|
|
t = types[TBOOL];
|
|
|
|
}
|
|
|
|
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);
|
2009-09-09 01:18:16 -06:00
|
|
|
typecheck(&ll->n, Erv | Etype);
|
|
|
|
if(ll->n->type == T || t == T)
|
2009-08-04 11:26:29 -06:00
|
|
|
continue;
|
2009-09-09 01:18:16 -06:00
|
|
|
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 && !eqtype(ll->n->type, t))
|
2009-09-21 16:45:55 -06:00
|
|
|
yyerror("case %+N in %T switch", ll->n, t);
|
2009-09-09 01:18:16 -06:00
|
|
|
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)
|
|
|
|
yyerror("%#N is not a type", ll->n);
|
|
|
|
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);
|
|
|
|
}
|
2009-08-04 11:26:29 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
typechecklist(ncase->nbody, Etop);
|
|
|
|
}
|
|
|
|
|
|
|
|
lineno = lno;
|
|
|
|
}
|