mirror of
https://github.com/golang/go
synced 2024-10-05 06:21:24 -06:00
164 lines
3.5 KiB
C
164 lines
3.5 KiB
C
|
// 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;
|
||
|
}
|