| // 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->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; |
| } |