summaryrefslogtreecommitdiff
path: root/src/cmd/gc/select.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/gc/select.c')
-rw-r--r--src/cmd/gc/select.c163
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;
+}