// 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"
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) {
default:
yyerror("select case must be receive, send or assign recv");;
break;
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) {
r->nbody = concat(r->nbody, n->ninit);
n->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->right->type->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;
}
|