Commit f7a867e1 authored by Russ Cox's avatar Russ Cox

move select into its own file.

split into typecheck + walk

R=ken
OCL=32726
CL=32726
parent d8c19c80
......@@ -36,6 +36,7 @@ OFILES=\
obj.$O\
print.$O\
typecheck.$O\
select.$O\
$(LIB): $(OFILES)
ar rsc $(LIB) $(OFILES)
......
......@@ -363,6 +363,7 @@ enum
OSLICE, OSLICEARR, OSLICESTR,
ORECV,
ORUNESTR,
OSELRECV,
// stmts
OBLOCK,
......@@ -973,6 +974,9 @@ void walkswitch(Node*);
void walkselect(Node*);
void walkdot(Node*, NodeList**);
void walkexpr(Node**, NodeList**);
Node* mkcall(char*, Type*, NodeList**, ...);
Node* mkcall1(Node*, Type*, NodeList**, ...);
Node* chanfn(char*, int, Type*);
Node* ascompatee1(int, Node*, Node*, NodeList**);
NodeList* ascompatee(int, NodeList*, NodeList*, NodeList**);
NodeList* ascompatet(int, NodeList*, Type**, int, NodeList**);
......@@ -1000,6 +1004,7 @@ void walkdeflist(NodeList*);
void walkdef(Node*);
void typechecklist(NodeList*, int);
void typecheckswitch(Node*);
void typecheckselect(Node*);
Node* typecheckconv(Node*, Node*, Type*, int);
Node* typecheck(Node**, int);
......
// 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.
/*
* select
*/
#include "go.h"
/*
* declare v in
* case v := <-chan // select and switch
* called during parse
*/
Node*
selectas(Node *name, Node *expr, NodeList **init)
{
Type *t;
if(expr == N || expr->op != ORECV)
goto bad;
walkexpr(&expr->left, init);
t = expr->left->type;
if(t == T)
goto bad;
if(t->etype != TCHAN)
goto bad;
t = t->type;
return old2new(name, t, init);
bad:
return name;
}
void
typecheckselect(Node *sel)
{
Node *ncase, *n, *def;
NodeList *l;
int lno, count;
def = nil;
lno = setlineno(sel);
count = 0;
typechecklist(sel->ninit, Etop);
for(l=sel->list; l; l=l->next) {
count++;
ncase = l->n;
setlineno(ncase);
if(ncase->op != OXCASE)
fatal("typecheckselect %O", ncase->op);
if(ncase->list == nil) {
// default
if(def != N)
yyerror("multiple defaults in select (first at %L)", def->lineno);
else
def = ncase;
} else if(ncase->list->next) {
yyerror("select cases cannot be lists");
} else {
n = typecheck(&ncase->list->n, Etop);
ncase->left = n;
ncase->list = nil;
setlineno(n);
switch(n->op) {
case OAS:
// convert x = <-c into OSELRECV(x, c)
if(n->right->op != ORECV) {
yyerror("select assignment must have receive on right hand side");
break;
}
n->op = OSELRECV;
n->right = n->right->left;
break;
case ORECV:
// convert <-c into OSELRECV(N, c)
n->op = OSELRECV;
n->right = n->left;
n->left = N;
break;
case OSEND:
break;
}
}
typechecklist(ncase->nbody, Etop);
}
sel->xoffset = count;
if(count == 0)
yyerror("empty select");
lineno = lno;
}
void
walkselect(Node *sel)
{
int lno;
Node *n, *ncase, *r, *a, *tmp, *var;
NodeList *l, *init;
lno = setlineno(sel);
init = sel->ninit;
sel->ninit = nil;
// generate sel-struct
var = nod(OXXX, N, N);
tempname(var, ptrto(types[TUINT8]));
r = nod(OAS, var, mkcall("newselect", var->type, nil, nodintconst(sel->xoffset)));
typecheck(&r, Etop);
init = list(init, r);
if(sel->list == nil)
fatal("double walkselect"); // already rewrote
// register cases
for(l=sel->list; l; l=l->next) {
ncase = l->n;
n = ncase->left;
r = nod(OIF, N, N);
r->nbody = ncase->ninit;
ncase->ninit = nil;
if(n == nil) {
// selectdefault(sel *byte);
r->ntest = mkcall("selectdefault", types[TBOOL], &init, var);
} else if(n->op == OSEND) {
// selectsend(sel *byte, hchan *chan any, elem any) (selected bool);
r->ntest = mkcall1(chanfn("selectsend", 2, n->left->type), types[TBOOL], &init, var, n->left, n->right);
} else if(n->op == OSELRECV) {
tmp = N;
if(n->left == N)
a = nodnil();
else {
// introduce temporary until we're sure this will succeed.
tmp = nod(OXXX, N, N);
tempname(tmp, n->left->type);
a = nod(OADDR, tmp, N);
}
// selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool);
r->ntest = mkcall1(chanfn("selectrecv", 2, n->right->type), types[TBOOL], &init, var, n->right, a);
if(tmp != N) {
a = nod(OAS, n->left, tmp);
typecheck(&a, Etop);
r->nbody = list(r->nbody, a);
}
} else
fatal("select %O", n->op);
r->nbody = concat(r->nbody, ncase->nbody);
r->nbody = list(r->nbody, nod(OBREAK, N, N));
init = list(init, r);
}
// run the select
init = list(init, mkcall("selectgo", T, nil, var));
sel->nbody = init;
sel->list = nil;
walkstmtlist(init);
lineno = lno;
}
......@@ -575,6 +575,15 @@ dodump(Node *n, int dep)
print("%O%J\n", n->op, n);
dodump(n->left, dep+1);
break;
case OXCASE:
print("%N\n", n);
dodump(n->left, dep+1);
dodump(n->right, dep+1);
indent(dep);
print("%O-nbody\n", n->op);
dodumplist(n->nbody, dep+1);
break;
}
if(n->ntype != nil) {
......
......@@ -12,7 +12,6 @@
*
* TODO:
* trailing ... section of function calls
* select
* range
*/
......@@ -859,9 +858,7 @@ reswitch:
goto ret;
case OSELECT:
typechecklist(n->ninit, Etop);
typecheck(&n->ntest, Erv);
typechecklist(n->list, Etop);
typecheckselect(n);
goto ret;
case OSWITCH:
......@@ -1757,4 +1754,3 @@ typecheckas2(Node *n)
mismatch:
yyerror("assignment count mismatch: %d = %d", cl, cr);
}
......@@ -5,10 +5,7 @@
#include "go.h"
static Node* walkprint(Node*, NodeList**);
static Node* mkcall(char*, Type*, NodeList**, ...);
static Node* mkcall1(Node*, Type*, NodeList**, ...);
static Node* conv(Node*, Type*);
static Node* chanfn(char*, int, Type*);
static Node* mapfn(char*, Type*);
static Node* makenewvar(Type*, NodeList**, Node**);
enum
......@@ -929,6 +926,7 @@ makenewvar(Type *t, NodeList **init, Node **nstar)
return nvar;
}
// TODO(rsc): cut
void
walkdottype(Node *n, NodeList **init)
{
......@@ -942,6 +940,7 @@ walkdottype(Node *n, NodeList **init)
}
}
// TODO(rsc): cut
void
walkconv(Node **np, NodeList **init)
{
......@@ -991,232 +990,6 @@ bad:
yyerror("invalid %s: %T to %T", what, l->type, t);
}
Node*
selcase(Node *n, Node *var, NodeList **init)
{
Node *a, *r, *c;
Type *t;
if(n->list == nil)
goto dflt;
c = n->list->n;
if(c->op == ORECV)
goto recv;
walkexpr(&c->left, init); // chan
walkexpr(&c->right, init); // elem
t = fixchan(c->left->type);
if(t == T)
return N;
if(!(t->chan & Csend)) {
yyerror("cannot send on %T", t);
return N;
}
convlit(&c->right, t->type);
// selectsend(sel *byte, hchan *chan any, elem any) (selected bool);
a = mkcall1(chanfn("selectsend", 2, t), types[TBOOL], init, var, c->left, c->right);
goto out;
recv:
if(c->right != N)
goto recv2;
walkexpr(&c->left, init); // chan
t = fixchan(c->left->type);
if(t == T)
return N;
if(!(t->chan & Crecv)) {
yyerror("cannot receive from %T", t);
return N;
}
// selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool);
a = mkcall1(chanfn("selectrecv", 2, t), types[TBOOL], init, var, c->left, nodnil());
goto out;
recv2:
walkexpr(&c->right, init); // chan
t = fixchan(c->right->type);
if(t == T)
return N;
if(!(t->chan & Crecv)) {
yyerror("cannot receive from %T", t);
return N;
}
walkexpr(&c->left, init);
// selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool);
a = mkcall1(chanfn("selectrecv", 2, t), types[TBOOL], init, var, c->right, nod(OADDR, c->left, N));
goto out;
dflt:
// selectdefault(sel *byte);
a = mkcall("selectdefault", types[TBOOL], init, var);
goto out;
out:
r = nod(OIF, N, N);
r->ntest = a;
return r;
}
/*
* enumerate the special cases
* of the case statement:
* case v := <-chan // select and switch
*/
Node*
selectas(Node *name, Node *expr, NodeList **init)
{
Type *t;
if(expr == N || expr->op != ORECV)
goto bad;
walkexpr(&expr->left, init);
t = expr->left->type;
if(t == T)
goto bad;
if(t->etype != TCHAN)
goto bad;
t = t->type;
return old2new(name, t, init);
bad:
return name;
}
void
walkselect(Node *sel)
{
Node *n, *l, *oc, *on, *r;
Node *var, *def;
NodeList *res, *bod, *nbod, *init, *ln;
int count, op;
int32 lno;
lno = setlineno(sel);
init = nil;
// generate sel-struct
var = nod(OXXX, N, N);
tempname(var, ptrto(types[TUINT8]));
if(sel->list == nil) {
yyerror("empty select");
return;
}
count = 0; // number of cases
res = nil; // entire select body
bod = nil; // body of each case
oc = N; // last case
def = N; // default case
for(ln=sel->list; ln; ln=ln->next) {
n = ln->n;
setlineno(n);
if(n->op != OXCASE)
fatal("walkselect %O", n->op);
count++;
l = N;
if(n->list == nil) {
op = ORECV; // actual value not used
if(def != N)
yyerror("repeated default; first at %L", def->lineno);
def = n;
} else {
l = n->list->n;
op = l->op;
if(n->list->next) {
yyerror("select cases cannot be lists");
continue;
}
}
nbod = nil;
switch(op) {
default:
yyerror("select cases must be send, recv or default %O", op);
continue;
case OAS:
// convert new syntax (a=recv(chan)) to (recv(a,chan))
if(l->right == N || l->right->op != ORECV) {
yyerror("select cases must be send, recv or default %O", l->right->op);
break;
}
r = l->right; // rcv
r->right = r->left;
r->left = l->left;
n->list->n = r;
// convert case x := foo: body
// to case tmp := foo: x := tmp; body.
// if x escapes and must be allocated
// on the heap, this delays the allocation
// until after the select has chosen this branch.
if(n->ninit != nil && n->ninit->n->op == ODCL) {
on = nod(OXXX, N, N);
tempname(on, l->left->type);
on->sym = lookup("!tmpselect!");
r->left = on;
on = nod(OAS, l->left, on);
typecheck(&on, Etop);
nbod = list(n->ninit, on);
n->ninit = nil;
}
break;
case OSEND:
case OSENDNB:
case ORECV:
break;
}
nbod = concat(nbod, n->nbody);
nbod = list(nbod, nod(OBREAK, N, N));
n->nbody = nil;
oc = selcase(n, var, &init);
if(oc != N) {
oc->nbody = nbod;
res = list(res, oc);
}
}
setlineno(sel);
// selectgo(sel *byte);
res = list(res, mkcall("selectgo", T, nil, var));
// newselect(size uint32) (sel *byte);
r = nod(OAS, var, mkcall("newselect", var->type, nil, nodintconst(count)));
typecheck(&r, Etop);
typechecklist(res, Etop);
sel->ninit = list1(r);
sel->nbody = res;
sel->left = N;
walkstmtlist(sel->ninit);
walkstmtlist(sel->nbody);
//dump("sel", sel);
sel->ninit = concat(sel->ninit, init);
lineno = lno;
}
Node*
ascompatee1(int op, Node *l, Node *r, NodeList **init)
{
......@@ -2805,7 +2578,7 @@ vmkcall(Node *fn, Type *t, NodeList **init, va_list va)
return r;
}
static Node*
Node*
mkcall(char *name, Type *t, NodeList **init, ...)
{
Node *r;
......@@ -2817,7 +2590,7 @@ mkcall(char *name, Type *t, NodeList **init, ...)
return r;
}
static Node*
Node*
mkcall1(Node *fn, Type *t, NodeList **init, ...)
{
Node *r;
......@@ -2840,7 +2613,7 @@ conv(Node *n, Type *t)
return n;
}
static Node*
Node*
chanfn(char *name, int n, Type *t)
{
Node *fn;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment