new switch implementation
in preparation of type switch.
no functional change (yet).
R=r
OCL=25784
CL=25788
diff --git a/src/cmd/gc/Makefile b/src/cmd/gc/Makefile
index 1ab4497..4237e97 100644
--- a/src/cmd/gc/Makefile
+++ b/src/cmd/gc/Makefile
@@ -21,6 +21,7 @@
dcl.$O\
export.$O\
walk.$O\
+ swt.$O\
const.$O\
mparith1.$O\
mparith2.$O\
diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h
index 76e440d..dfd975f 100644
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -812,8 +812,7 @@
void walkconv(Node*);
void walkas(Node*);
void walkbool(Node*);
-Type* walkswitch(Node*, Type*(*)(Node*, Type*));
-int casebody(Node*);
+void walkswitch(Node*);
void walkselect(Node*);
int whatis(Node*);
void walkdot(Node*);
diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c
index bb2c31e..aa8b01a 100644
--- a/src/cmd/gc/subr.c
+++ b/src/cmd/gc/subr.c
@@ -547,8 +547,11 @@
return;
case OCASE:
- // the right side points to the next case
- print("%O%J\n", n->op, n);
+ // the right side points to label of the body
+ if(n->right != N && n->right->op == OGOTO && n->right->left->op == ONAME)
+ print("%O%J GOTO %N\n", n->op, n, n->right->left);
+ else
+ print("%O%J\n", n->op, n);
dodump(n->left, dep+1);
return;
}
@@ -654,8 +657,8 @@
[OCMP] = "CMP",
[OFALL] = "FALL",
[OCOMPOS] = "COMPOS",
- [ODOTTYPE] = "DOTTYPE",
- [OCONV] = "CONV",
+ [ODOTTYPE] = "DOTTYPE",
+ [OCONV] = "CONV",
[OCOM] = "COM",
[OCONST] = "CONST",
[OCONTINUE] = "CONTINUE",
@@ -724,7 +727,7 @@
[OPRINT] = "PRINT",
[OPRINTN] = "PRINTN",
[OPARAM] = "PARAM",
- [ODCL] = "DCL",
+ [ODCL] = "DCL",
[OXXX] = "XXX",
};
diff --git a/src/cmd/gc/swt.c b/src/cmd/gc/swt.c
new file mode 100644
index 0000000..dc32665
--- /dev/null
+++ b/src/cmd/gc/swt.c
@@ -0,0 +1,333 @@
+// 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"
+
+/*
+ * walktype
+ */
+Type*
+sw0(Node *c, Type *place)
+{
+ Node *r;
+
+ if(c == N)
+ return T;
+ if(c->op != OAS) {
+ walktype(c, Erv);
+ return T;
+ }
+ walktype(c->left, Elv);
+
+ r = c->right;
+ if(c == N)
+ return T;
+
+ switch(r->op) {
+ default:
+ goto bad;
+ case ORECV:
+ // <-chan
+ walktype(r->left, Erv);
+ if(!istype(r->left->type, TCHAN))
+ goto bad;
+ break;
+ case OINDEX:
+ // map[e]
+ walktype(r->left, Erv);
+ if(!istype(r->left->type, TMAP))
+ goto bad;
+ break;
+ case ODOTTYPE:
+ // interface.(type)
+ walktype(r->left, Erv);
+ if(!istype(r->left->type, TINTER))
+ goto bad;
+ break;
+ }
+ c->type = types[TBOOL];
+ return T;
+
+bad:
+ yyerror("inappropriate assignment in a case statement");
+ return T;
+}
+
+/*
+ * return the first type
+ */
+Type*
+sw1(Node *c, Type *place)
+{
+ if(place == T)
+ return c->type;
+ return place;
+}
+
+/*
+ * return a suitable type
+ */
+Type*
+sw2(Node *c, Type *place)
+{
+ return types[TINT]; // botch
+}
+
+/*
+ * check that switch type
+ * is compat with all the cases
+ */
+Type*
+sw3(Node *c, Type *place)
+{
+ if(place == T)
+ return c->type;
+ if(c->type == T)
+ c->type = place;
+ convlit(c, place);
+ if(!ascompat(place, c->type))
+ badtype(OSWITCH, place, c->type);
+ return place;
+}
+
+/*
+ * over all cases, call paramenter function.
+ * four passes of these are used to allocate
+ * types to cases and switch
+ */
+Type*
+walkcases(Node *sw, Type*(*call)(Node*, Type*))
+{
+ Iter save;
+ Node *n;
+ Type *place;
+ int32 lno;
+
+ lno = setlineno(sw);
+ place = call(sw->ntest, T);
+
+ n = listfirst(&save, &sw->nbody->left);
+ if(n->op == OEMPTY)
+ return T;
+
+loop:
+ if(n == N) {
+ lineno = lno;
+ return place;
+ }
+
+ if(n->op != OCASE)
+ fatal("walkcases: not case %O\n", n->op);
+
+ if(n->left != N) {
+ setlineno(n->left);
+ place = call(n->left, place);
+ }
+ n = listnext(&save);
+ goto loop;
+}
+
+Node*
+newlabel()
+{
+ 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
+casebody(Node *sw)
+{
+ Iter save;
+ Node *os, *oc, *t, *c;
+ Node *cas, *stat, *def;
+ Node *go, *br;
+ int32 lno;
+
+ lno = setlineno(sw);
+ t = listfirst(&save, &sw->nbody);
+ if(t == N || t->op == OEMPTY) {
+ sw->nbody = nod(OLIST, N, N);
+ return;
+ }
+
+ cas = N; // cases
+ stat = N; // statements
+ def = N; // defaults
+ os = N; // last statement
+ oc = N; // last case
+ br = nod(OBREAK, N, N);
+
+loop:
+
+ if(t == N) {
+ if(oc == N && os != N)
+ yyerror("first switch statement must be a case");
+
+ stat = list(stat, br);
+ cas = list(cas, def);
+
+ sw->nbody = nod(OLIST, rev(cas), rev(stat));
+//dump("case", sw->nbody->left);
+//dump("stat", sw->nbody->right);
+ lineno = lno;
+ return;
+ }
+
+ lno = setlineno(t);
+
+ switch(t->op) {
+ case OXCASE:
+ t->op = OCASE;
+ if(oc == N && os != N)
+ yyerror("first switch statement must be a case");
+
+ if(os != N && os->op == OXFALL)
+ os->op = OFALL;
+ else
+ stat = list(stat, br);
+
+ go = nod(OGOTO, newlabel(), N);
+
+ c = t->left;
+ if(c == N) {
+ if(def != N)
+ yyerror("more than one default case");
+
+ // reuse original default case
+ t->right = go;
+ def = t;
+ }
+
+ // expand multi-valued cases
+ for(; c!=N; c=c->right) {
+ if(c->op != OLIST) {
+ // reuse original case
+ t->left = c;
+ t->right = go;
+ cas = list(cas, t);
+ break;
+ }
+ cas = list(cas, nod(OCASE, c->left, go));
+ }
+ stat = list(stat, nod(OLABEL, go->left, N));
+ oc = t;
+ os = N;
+ break;
+
+ default:
+ stat = list(stat, t);
+ os = t;
+ break;
+ }
+ t = listnext(&save);
+ goto loop;
+}
+
+/*
+ * rebulid case statements into if .. goto
+ */
+void
+prepsw(Node *sw)
+{
+ Iter save;
+ Node *name, *cas;
+ Node *t, *a;
+ int bool;
+
+ bool = 0;
+ if(whatis(sw->ntest) == Wlitbool) {
+ bool = 1; // true
+ if(sw->ntest->val.u.xval == 0)
+ bool = 2; // false
+ }
+
+ cas = N;
+ name = N;
+ if(bool == 0) {
+ name = nod(OXXX, N, N);
+ tempname(name, sw->ntest->type);
+ cas = nod(OAS, name, sw->ntest);
+ }
+
+ t = listfirst(&save, &sw->nbody->left);
+
+loop:
+ if(t == N) {
+ sw->nbody->left = rev(cas);
+ walkstate(sw->nbody->left);
+//dump("case", sw->nbody->left);
+ return;
+ }
+
+ if(t->left == N) {
+ cas = list(cas, t->right); // goto default
+ t = listnext(&save);
+ goto loop;
+ }
+
+ a = nod(OIF, N, N);
+ a->nbody = t->right; // then goto l
+
+ switch(bool) {
+ default:
+ // not bool const
+ a->ntest = nod(OEQ, name, t->left); // if name == val
+ break;
+
+ case 1:
+ // bool true
+ a->ntest = t->left; // if val
+ break;
+
+ case 2:
+ // bool false
+ a->ntest = nod(ONOT, t->left, N); // if !val
+ break;
+ }
+ cas = list(cas, a);
+
+ t = listnext(&save);
+ goto loop;
+}
+
+void
+walkswitch(Node *n)
+{
+ Type *t;
+
+ casebody(n);
+ if(n->ntest == N)
+ n->ntest = booltrue;
+
+ walkstate(n->ninit);
+ walktype(n->ntest, Erv);
+ walkstate(n->nbody);
+
+ // walktype
+ walkcases(n, sw0);
+
+ // find common type
+ t = n->ntest->type;
+ if(t == T)
+ t = walkcases(n, sw1);
+
+ // if that fails pick a type
+ if(t == T)
+ t = walkcases(n, sw2);
+
+ // set the type on all literals
+ if(t != T) {
+ walkcases(n, sw3);
+ convlit(n->ntest, t);
+ prepsw(n);
+ }
+}
diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c
index 3ae0f52..d82dfd4 100644
--- a/src/cmd/gc/walk.c
+++ b/src/cmd/gc/walk.c
@@ -4,9 +4,6 @@
#include "go.h"
-static Type* sw1(Node*, Type*);
-static Type* sw2(Node*, Type*);
-static Type* sw3(Node*, Type*);
static Node* curfn;
enum
@@ -318,26 +315,7 @@
if(top != Etop)
goto nottop;
- casebody(n->nbody);
- if(n->ntest == N)
- n->ntest = booltrue;
- walkstate(n->ninit);
- walktype(n->ntest, Erv);
- walkstate(n->nbody);
-
- // find common type
- if(n->ntest->type == T)
- n->ntest->type = walkswitch(n, sw1);
-
- // if that fails pick a type
- if(n->ntest->type == T)
- n->ntest->type = walkswitch(n, sw2);
-
- // set the type on all literals
- if(n->ntest->type != T)
- walkswitch(n, sw3);
- walktype(n->ntest, Erv); // BOTCH is this right
- walktype(n->nincr, Erv);
+ walkswitch(n);
goto ret;
case OSELECT:
@@ -577,7 +555,6 @@
case OCASE:
if(top != Etop)
goto nottop;
- walktype(n->left, Erv);
walkstate(n->right);
goto ret;
@@ -1251,124 +1228,11 @@
bad:
if(l->type != T)
yyerror("invalid conversion: %T to %T", l->type, t);
- else if(n->left->op == OLIST)
+ else
+ if(n->left->op == OLIST)
yyerror("invalid type for composite literal: %T", t);
}
-
-/*
- * return the first type
- */
-Type*
-sw1(Node *c, Type *place)
-{
- if(place == T)
- return c->type;
- return place;
-}
-
-/*
- * return a suitable type
- */
-Type*
-sw2(Node *c, Type *place)
-{
- return types[TINT]; // botch
-}
-
-/*
- * check that switch type
- * is compat with all the cases
- */
-Type*
-sw3(Node *c, Type *place)
-{
- if(place == T)
- return c->type;
- if(c->type == T)
- c->type = place;
- convlit(c, place);
- if(!ascompat(place, c->type))
- badtype(OSWITCH, place, c->type);
- return place;
-}
-
-Type*
-walkswitch(Node *sw, Type*(*call)(Node*, Type*))
-{
- Node *n, *c;
- Type *place;
- place = call(sw->ntest, T);
-
- setlineno(sw);
-
- n = sw->nbody;
- if(n->op == OLIST)
- n = n->left;
- if(n->op == OEMPTY)
- return T;
-
- for(; n!=N; n=n->right) {
- if(n->op != OCASE)
- fatal("walkswitch: not case %O\n", n->op);
- for(c=n->left; c!=N; c=c->right) {
- if(c->op != OLIST) {
- setlineno(c);
- place = call(c, place);
- break;
- }
- setlineno(c);
- place = call(c->left, place);
- }
- }
- return place;
-}
-
-int
-casebody(Node *n)
-{
- Node *oc, *ot, *t;
- Iter save;
-
- /*
- * look to see if statements at top level have
- * case labels attached to them. convert the illegal
- * ops XFALL and XCASE into legal ops FALL and CASE.
- * all unconverted ops will thus be caught as illegal
- */
-
- oc = N; // last case statement
- ot = N; // last statement (look for XFALL)
- t = listfirst(&save, &n);
-
-loop:
- if(t == N) {
- /* empty switch */
- if(oc == N)
- return 0;
- return 1;
- }
- if(t->op == OXCASE) {
- /* rewrite and link top level cases */
- t->op = OCASE;
- if(oc != N)
- oc->right = t;
- oc = t;
-
- /* rewrite top fall that preceed case */
- if(ot != N && ot->op == OXFALL)
- ot->op = OFALL;
- }
-
- /* if first statement is not case */
- if(oc == N)
- return 0;
-
- ot = t;
- t = listnext(&save);
- goto loop;
-}
-
Node*
selcase(Node *n, Node *var)
{
@@ -1477,21 +1341,58 @@
return r;
}
+/*
+ * enumerate the special cases
+ * of the case statement:
+ * case v := <-chan // select and switch
+ * case v := map[] // switch
+ * case v := interface.(TYPE) // switch
+ */
Node*
selectas(Node *name, Node *expr)
{
Node *a;
Type *t;
- if(expr == N || expr->op != ORECV)
+ if(expr == N)
goto bad;
- walktype(expr->left, Erv);
- t = expr->left->type;
- if(t == T)
+ switch(expr->op) {
+ default:
+//dump("case", expr);
goto bad;
- if(t->etype != TCHAN)
- goto bad;
- a = old2new(name, t->type);
+
+ case ORECV:
+ walktype(expr->left, Erv);
+ t = expr->left->type;
+ if(t == T)
+ goto bad;
+ if(t->etype != TCHAN)
+ goto bad;
+ t = t->type;
+ break;
+
+ case OINDEX:
+ walktype(expr->left, Erv);
+ walktype(expr->right, Erv);
+ t = expr->left->type;
+ if(t == T)
+ goto bad;
+ if(t->etype != TMAP)
+ goto bad;
+ t = t->type;
+ break;
+
+ case ODOTTYPE:
+ walktype(expr->left, Erv);
+ t = expr->left->type;
+ if(t == T)
+ goto bad;
+ if(t->etype != TINTER)
+ goto bad;
+ t = expr->type;
+ break;
+ }
+ a = old2new(name, t);
return a;
bad:
@@ -1523,7 +1424,7 @@
oc = N; // last case
def = N; // default case
- for(count=0; n!=N; n=listnext(&iter)) {
+ for(; n!=N; n=listnext(&iter)) {
setlineno(n);
switch(n->op) {