mirror of
https://github.com/golang/go
synced 2024-11-12 04:50:21 -07:00
move select into its own file.
split into typecheck + walk R=ken OCL=32726 CL=32726
This commit is contained in:
parent
d8c19c80dc
commit
f7a867e1a6
@ -36,6 +36,7 @@ OFILES=\
|
||||
obj.$O\
|
||||
print.$O\
|
||||
typecheck.$O\
|
||||
select.$O\
|
||||
|
||||
$(LIB): $(OFILES)
|
||||
ar rsc $(LIB) $(OFILES)
|
||||
|
@ -363,6 +363,7 @@ enum
|
||||
OSLICE, OSLICEARR, OSLICESTR,
|
||||
ORECV,
|
||||
ORUNESTR,
|
||||
OSELRECV,
|
||||
|
||||
// stmts
|
||||
OBLOCK,
|
||||
@ -973,6 +974,9 @@ void walkswitch(Node*);
|
||||
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**);
|
||||
@ -1000,6 +1004,7 @@ void walkdeflist(NodeList*);
|
||||
void walkdef(Node*);
|
||||
void typechecklist(NodeList*, int);
|
||||
void typecheckswitch(Node*);
|
||||
void typecheckselect(Node*);
|
||||
Node* typecheckconv(Node*, Node*, Type*, int);
|
||||
Node* typecheck(Node**, int);
|
||||
|
||||
|
163
src/cmd/gc/select.c
Normal file
163
src/cmd/gc/select.c
Normal file
@ -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;
|
||||
}
|
@ -575,6 +575,15 @@ dodump(Node *n, int dep)
|
||||
print("%O%J\n", n->op, n);
|
||||
dodump(n->left, dep+1);
|
||||
break;
|
||||
|
||||
case OXCASE:
|
||||
print("%N\n", n);
|
||||
dodump(n->left, dep+1);
|
||||
dodump(n->right, dep+1);
|
||||
indent(dep);
|
||||
print("%O-nbody\n", n->op);
|
||||
dodumplist(n->nbody, dep+1);
|
||||
break;
|
||||
}
|
||||
|
||||
if(n->ntype != nil) {
|
||||
|
@ -12,7 +12,6 @@
|
||||
*
|
||||
* TODO:
|
||||
* trailing ... section of function calls
|
||||
* select
|
||||
* range
|
||||
*/
|
||||
|
||||
@ -859,9 +858,7 @@ reswitch:
|
||||
goto ret;
|
||||
|
||||
case OSELECT:
|
||||
typechecklist(n->ninit, Etop);
|
||||
typecheck(&n->ntest, Erv);
|
||||
typechecklist(n->list, Etop);
|
||||
typecheckselect(n);
|
||||
goto ret;
|
||||
|
||||
case OSWITCH:
|
||||
@ -1757,4 +1754,3 @@ typecheckas2(Node *n)
|
||||
mismatch:
|
||||
yyerror("assignment count mismatch: %d = %d", cl, cr);
|
||||
}
|
||||
|
||||
|
@ -5,10 +5,7 @@
|
||||
#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
|
||||
@ -929,6 +926,7 @@ makenewvar(Type *t, NodeList **init, Node **nstar)
|
||||
return nvar;
|
||||
}
|
||||
|
||||
// TODO(rsc): cut
|
||||
void
|
||||
walkdottype(Node *n, NodeList **init)
|
||||
{
|
||||
@ -942,6 +940,7 @@ walkdottype(Node *n, NodeList **init)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(rsc): cut
|
||||
void
|
||||
walkconv(Node **np, NodeList **init)
|
||||
{
|
||||
@ -991,232 +990,6 @@ bad:
|
||||
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)
|
||||
{
|
||||
@ -2805,7 +2578,7 @@ vmkcall(Node *fn, Type *t, NodeList **init, va_list va)
|
||||
return r;
|
||||
}
|
||||
|
||||
static Node*
|
||||
Node*
|
||||
mkcall(char *name, Type *t, NodeList **init, ...)
|
||||
{
|
||||
Node *r;
|
||||
@ -2817,7 +2590,7 @@ mkcall(char *name, Type *t, NodeList **init, ...)
|
||||
return r;
|
||||
}
|
||||
|
||||
static Node*
|
||||
Node*
|
||||
mkcall1(Node *fn, Type *t, NodeList **init, ...)
|
||||
{
|
||||
Node *r;
|
||||
@ -2840,7 +2613,7 @@ conv(Node *n, Type *t)
|
||||
return n;
|
||||
}
|
||||
|
||||
static Node*
|
||||
Node*
|
||||
chanfn(char *name, int n, Type *t)
|
||||
{
|
||||
Node *fn;
|
||||
|
Loading…
Reference in New Issue
Block a user