1
0
mirror of https://github.com/golang/go synced 2024-11-25 06:57:58 -07:00

update type switch to match spec.

R=ken
OCL=34471
CL=34471
This commit is contained in:
Russ Cox 2009-09-09 00:18:16 -07:00
parent 5438be4541
commit 5d16d23362
8 changed files with 178 additions and 66 deletions

View File

@ -4367,7 +4367,6 @@ The following minimal alignment properties are guaranteed:
<h2 id="Implementation_differences"><font color=red>Implementation differences - TODO</font></h2> <h2 id="Implementation_differences"><font color=red>Implementation differences - TODO</font></h2>
<ul> <ul>
<li><font color=red>Implementation does not honor the restriction on goto statements and targets (no intervening declarations).</font></li> <li><font color=red>Implementation does not honor the restriction on goto statements and targets (no intervening declarations).</font></li>
<li><font color=red>A type switch must have an assignment in the guard expression and does not support multiple types per case.</font></li>
</ul> </ul>
</div> </div>

View File

@ -375,7 +375,7 @@ enum
OSELECT, OSELECT,
OSWITCH, OSWITCH,
OTYPECASE, OTYPECASE,
OTYPESW, OTYPESW, // l = r.(type)
// types // types
OTCHAN, OTCHAN,
@ -676,7 +676,8 @@ EXTERN Node* curfn;
EXTERN int maxround; EXTERN int maxround;
EXTERN int widthptr; EXTERN int widthptr;
EXTERN Node* typeswvar; EXTERN Node* typesw;
EXTERN Node* nblank;
EXTERN char* structpkg; EXTERN char* structpkg;
extern int thechar; extern int thechar;

View File

@ -424,7 +424,7 @@ simple_stmt:
yyerror("expr.(type) must be alone in list"); yyerror("expr.(type) must be alone in list");
else if($1->next != nil) else if($1->next != nil)
yyerror("argument count mismatch: %d = %d", count($1), 1); yyerror("argument count mismatch: %d = %d", count($1), 1);
$$ = nod(OTYPESW, $1->n, $3->n->left); $$ = nod(OTYPESW, $1->n, $3->n->right);
break; break;
} }
$$ = colas($1, $3); $$ = colas($1, $3);
@ -443,30 +443,19 @@ simple_stmt:
case: case:
LCASE expr_or_type_list ':' LCASE expr_or_type_list ':'
{ {
Node *n, *ntype; Node *n;
// will be converted to OCASE // will be converted to OCASE
// right will point to next case // right will point to next case
// done in casebody() // done in casebody()
poptodcl(); poptodcl();
$$ = nod(OXCASE, N, N); $$ = nod(OXCASE, N, N);
if(typeswvar != N && typeswvar->right != N) { $$->list = $2;
// type switch if(typesw != N && typesw->right != N && (n=typesw->right->left) != N) {
ntype = $2->n; // type switch - declare variable
if($2->next != nil) n = newname(n->sym);
yyerror("type switch case cannot be list");
if(ntype->op == OLITERAL && ntype->val.ctype == CTNIL) {
// case nil
$$->list = list1(nod(OTYPECASE, N, N));
break;
}
n = newname(typeswvar->right->sym);
declare(n, dclcontext); declare(n, dclcontext);
n->ntype = ntype; $$->nname = n;
$$->list = list1(nod(OTYPECASE, n, N));
} else {
// expr switch
$$->list = $2;
} }
break; break;
} }
@ -490,8 +479,16 @@ case:
} }
| LDEFAULT ':' | LDEFAULT ':'
{ {
Node *n;
poptodcl(); poptodcl();
$$ = nod(OXCASE, N, N); $$ = nod(OXCASE, N, N);
if(typesw != N && typesw->right != N && (n=typesw->right->left) != N) {
// type switch - declare variable
n = newname(n->sym);
declare(n, dclcontext);
$$->nname = n;
}
} }
compound_stmt: compound_stmt:
@ -637,18 +634,16 @@ switch_stmt:
{ {
Node *n; Node *n;
n = $3->ntest; n = $3->ntest;
if(n != N && n->op == OTYPESW) if(n != N && n->op != OTYPESW)
n = n->left;
else
n = N; n = N;
typeswvar = nod(OXXX, typeswvar, n); typesw = nod(OXXX, typesw, n);
} }
switch_body switch_body
{ {
$$ = $3; $$ = $3;
$$->op = OSWITCH; $$->op = OSWITCH;
$$->list = $5; $$->list = $5;
typeswvar = typeswvar->left; typesw = typesw->left;
popdcl(); popdcl();
} }
@ -823,7 +818,7 @@ pexpr:
} }
| pexpr '.' '(' LTYPE ')' | pexpr '.' '(' LTYPE ')'
{ {
$$ = nod(OTYPESW, $1, N); $$ = nod(OTYPESW, N, $1);
} }
| pexpr '[' expr ']' | pexpr '[' expr ']'
{ {
@ -1289,20 +1284,14 @@ arg_type:
name_or_type name_or_type
| sym name_or_type | sym name_or_type
{ {
$$ = $1->def; $$ = nod(ONONAME, N, N);
if($$ == N) { $$->sym = $1;
$$ = nod(ONONAME, N, N);
$$->sym = $1;
}
$$ = nod(OKEY, $$, $2); $$ = nod(OKEY, $$, $2);
} }
| sym dotdotdot | sym dotdotdot
{ {
$$ = $1->def; $$ = nod(ONONAME, N, N);
if($$ == N) { $$->sym = $1;
$$ = nod(ONONAME, N, N);
$$->sym = $1;
}
$$ = nod(OKEY, $$, $2); $$ = nod(OKEY, $$, $2);
} }
| dotdotdot | dotdotdot

View File

@ -1359,6 +1359,7 @@ lexinit(void)
s->def->sym = s; s->def->sym = s;
types[TBLANK] = typ(TBLANK); types[TBLANK] = typ(TBLANK);
s->def->type = types[TBLANK]; s->def->type = types[TBLANK];
nblank = s->def;
} }
struct struct

View File

@ -249,13 +249,13 @@ newlabel(void)
* deal with fallthrough, break, unreachable statements * deal with fallthrough, break, unreachable statements
*/ */
void void
casebody(Node *sw) casebody(Node *sw, Node *typeswvar)
{ {
Node *os, *oc, *n, *c, *last; Node *os, *oc, *n, *c, *last;
Node *def; Node *def;
NodeList *cas, *stat, *l, *lc; NodeList *cas, *stat, *l, *lc;
Node *go, *br; Node *go, *br;
int32 lno; int32 lno, needvar;
lno = setlineno(sw); lno = setlineno(sw);
if(sw->list == nil) if(sw->list == nil)
@ -274,6 +274,7 @@ casebody(Node *sw)
if(n->op != OXCASE) if(n->op != OXCASE)
fatal("casebody %O", n->op); fatal("casebody %O", n->op);
n->op = OCASE; n->op = OCASE;
needvar = count(n->list) != 1 || n->list->n->op == OLITERAL;
go = nod(OGOTO, newlabel(), N); go = nod(OGOTO, newlabel(), N);
if(n->list == nil) { if(n->list == nil) {
@ -300,6 +301,14 @@ casebody(Node *sw)
} }
stat = list(stat, nod(OLABEL, go->left, N)); 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); stat = concat(stat, n->nbody);
// botch - shouldnt fall thru declaration // botch - shouldnt fall thru declaration
@ -348,16 +357,16 @@ mkcaselist(Node *sw, int arg)
switch(arg) { switch(arg) {
case Stype: case Stype:
c->hash = 0; c->hash = 0;
if(n->left->left == N) { if(n->left->op == OLITERAL) {
c->type = Ttypenil; c->type = Ttypenil;
continue; continue;
} }
if(istype(n->left->left->type, TINTER)) { if(istype(n->left->type, TINTER)) {
c->type = Ttypevar; c->type = Ttypevar;
continue; continue;
} }
c->hash = typehash(n->left->left->type); c->hash = typehash(n->left->type);
c->type = Ttypeconst; c->type = Ttypeconst;
continue; continue;
@ -483,6 +492,7 @@ exprswitch(Node *sw)
Type *t; Type *t;
int arg, ncase; int arg, ncase;
casebody(sw, N);
arg = Snorm; arg = Snorm;
if(isconst(sw->ntest, CTBOOL)) { if(isconst(sw->ntest, CTBOOL)) {
@ -564,13 +574,18 @@ typeone(Node *t)
NodeList *init; NodeList *init;
Node *a, *b, *var; Node *a, *b, *var;
var = t->left->left; var = t->nname;
init = list1(nod(ODCL, var, N)); init = nil;
if(var == N) {
typecheck(&nblank, Erv | Easgn);
var = nblank;
} else
init = list1(nod(ODCL, var, N));
a = nod(OAS2, N, N); a = nod(OAS2, N, N);
a->list = list(list1(var), boolname); // var,bool = a->list = list(list1(var), boolname); // var,bool =
b = nod(ODOTTYPE, facename, N); b = nod(ODOTTYPE, facename, N);
b->type = t->left->left->type; // interface.(type) b->type = t->left->type; // interface.(type)
a->rlist = list1(b); a->rlist = list1(b);
typecheck(&a, Etop); typecheck(&a, Etop);
init = list(init, a); init = list(init, a);
@ -678,6 +693,8 @@ typeswitch(Node *sw)
typecheck(&a, Etop); typecheck(&a, Etop);
cas = list(cas, a); cas = list(cas, a);
casebody(sw, facename);
boolname = nod(OXXX, N, N); boolname = nod(OXXX, N, N);
tempname(boolname, types[TBOOL]); tempname(boolname, types[TBOOL]);
typecheck(&boolname, Erv); typecheck(&boolname, Erv);
@ -758,10 +775,10 @@ walkswitch(Node *sw)
sw->ntest = nodbool(1); sw->ntest = nodbool(1);
typecheck(&sw->ntest, Erv); typecheck(&sw->ntest, Erv);
} }
casebody(sw);
if(sw->ntest->op == OTYPESW) { if(sw->ntest->op == OTYPESW) {
typeswitch(sw); typeswitch(sw);
//dump("sw", sw);
return; return;
} }
exprswitch(sw); exprswitch(sw);
@ -776,7 +793,7 @@ typecheckswitch(Node *n)
int top, lno; int top, lno;
Type *t; Type *t;
NodeList *l, *ll; NodeList *l, *ll;
Node *ncase; Node *ncase, *nvar;
Node *def; Node *def;
lno = lineno; lno = lineno;
@ -784,11 +801,11 @@ typecheckswitch(Node *n)
if(n->ntest != N && n->ntest->op == OTYPESW) { if(n->ntest != N && n->ntest->op == OTYPESW) {
// type switch // type switch
typecheck(&n->ntest, Etop);
top = Etype; top = Etype;
t = n->ntest->type; typecheck(&n->ntest->right, Erv);
t = n->ntest->right->type;
if(t != T && t->etype != TINTER) if(t != T && t->etype != TINTER)
yyerror("cannot type switch on non-interface value %+N", n->ntest); yyerror("cannot type switch on non-interface value %+N", n->ntest->right);
} else { } else {
// value switch // value switch
top = Erv; top = Erv;
@ -814,12 +831,37 @@ typecheckswitch(Node *n)
} else { } else {
for(ll=ncase->list; ll; ll=ll->next) { for(ll=ncase->list; ll; ll=ll->next) {
setlineno(ll->n); setlineno(ll->n);
typecheck(&ll->n, Erv); // TODO(rsc): top typecheck(&ll->n, Erv | Etype);
if(ll->n->type == T || t == T || top != Erv) if(ll->n->type == T || t == T)
continue; continue;
defaultlit(&ll->n, t); switch(top) {
if(ll->n->type != T && !eqtype(ll->n->type, t)) case Erv: // expression switch
yyerror("case %+N in switch of %+N %#O", ll->n, n->ntest, ll->n->op); 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))
yyerror("case %+N in switch of %+N %#O", ll->n, n->ntest, ll->n->op);
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);
}
} }
} }
typechecklist(ncase->nbody, Etop); typechecklist(ncase->nbody, Etop);

View File

@ -415,7 +415,7 @@ func (s *State) error(msg string) {
// //
func typename(typ reflect.Type) string { func typename(typ reflect.Type) string {
switch t := typ.(type) { switch typ.(type) {
case *reflect.ArrayType: case *reflect.ArrayType:
return "array"; return "array";
case *reflect.SliceType: case *reflect.SliceType:

View File

@ -19,12 +19,8 @@ type _StructBuilder struct {
var nobuilder *_StructBuilder var nobuilder *_StructBuilder
func isfloat(v reflect.Value) bool { func isfloat(v reflect.Value) bool {
switch v := v.(type) { switch v.(type) {
case *reflect.FloatValue: case *reflect.FloatValue, *reflect.Float32Value, *reflect.Float64Value:
return true;
case *reflect.Float32Value:
return true;
case *reflect.Float64Value:
return true; return true;
} }
return false; return false;

84
test/typeswitch1.go Normal file
View File

@ -0,0 +1,84 @@
// $G $D/$F.go && $L $F.$A && ./$A.out
// 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.
package main
import "fmt"
const (
a = iota;
b;
c;
d;
e;
)
var x = []int{1,2,3}
func f(x int, len *byte) {
*len = byte(x);
}
func whatis(x interface{}) string {
switch xx := x.(type) {
default:
return fmt.Sprint("default ", xx);
case int, int8, int16, int32:
return fmt.Sprint("signed ", xx);
case int64:
return fmt.Sprint("signed64 ", int64(xx));
case uint, uint8, uint16, uint32:
return fmt.Sprint("unsigned ", xx);
case uint64:
return fmt.Sprint("unsigned64 ", uint64(xx));
case nil:
return fmt.Sprint("nil ", xx);
}
panic("not reached");
}
func whatis1(x interface{}) string {
xx := x;
switch xx.(type) {
default:
return fmt.Sprint("default ", xx);
case int, int8, int16, int32:
return fmt.Sprint("signed ", xx);
case int64:
return fmt.Sprint("signed64 ", xx.(int64));
case uint, uint8, uint16, uint32:
return fmt.Sprint("unsigned ", xx);
case uint64:
return fmt.Sprint("unsigned64 ", xx.(uint64));
case nil:
return fmt.Sprint("nil ", xx);
}
panic("not reached");
}
func check(x interface{}, s string) {
w := whatis(x);
if w != s {
fmt.Println("whatis", x, "=>", w, "!=", s);
panic();
}
w = whatis1(x);
if w != s {
fmt.Println("whatis1", x, "=>", w, "!=", s);
panic();
}
}
func main() {
check(1, "signed 1");
check(uint(1), "unsigned 1");
check(int64(1), "signed64 1");
check(uint64(1), "unsigned64 1");
check(1.5, "default 1.5");
check(nil, "nil <nil>");
}