OSLICE, OSLICEARR, OSLICESTR,
ORECV,
ORUNESTR,
+ OSELRECV,
// stmts
OBLOCK,
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**);
void walkdef(Node*);
void typechecklist(NodeList*, int);
void typecheckswitch(Node*);
+void typecheckselect(Node*);
Node* typecheckconv(Node*, Node*, Type*, int);
Node* typecheck(Node**, int);
--- /dev/null
+// 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;
+}
#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
return nvar;
}
+// TODO(rsc): cut
void
walkdottype(Node *n, NodeList **init)
{
}
}
+// TODO(rsc): cut
void
walkconv(Node **np, NodeList **init)
{
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)
{
return r;
}
-static Node*
+Node*
mkcall(char *name, Type *t, NodeList **init, ...)
{
Node *r;
return r;
}
-static Node*
+Node*
mkcall1(Node *fn, Type *t, NodeList **init, ...)
{
Node *r;
return n;
}
-static Node*
+Node*
chanfn(char *name, int n, Type *t)
{
Node *fn;