diff options
Diffstat (limited to 'src/cmd/gc/select.c')
-rw-r--r-- | src/cmd/gc/select.c | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/src/cmd/gc/select.c b/src/cmd/gc/select.c new file mode 100644 index 000000000..2fd63cc7c --- /dev/null +++ b/src/cmd/gc/select.c @@ -0,0 +1,163 @@ +// 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; +} |