1
0
mirror of https://github.com/golang/go synced 2024-10-05 15:51:22 -06:00
go/src/cmd/gc/walk.c

4612 lines
84 KiB
C
Raw Normal View History

// 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.
#include "go.h"
static Node* curfn;
enum
{
Inone,
I2T,
2008-11-05 15:27:07 -07:00
I2T2,
I2I,
I2Ix,
2008-11-05 15:27:07 -07:00
I2I2,
T2I,
2009-01-27 19:21:03 -07:00
I2Isame,
E2T,
E2T2,
E2I,
E2I2,
I2E,
I2E2,
T2E,
E2Esame,
};
// can this code branch reach the end
// without an undcontitional RETURN
// this is hard, so it is conservative
int
walkret(NodeList *l)
{
Node *n;
loop:
while(l && l->next)
l = l->next;
if(l == nil)
return 1;
// at this point, we have the last
// statement of the function
n = l->n;
switch(n->op) {
case OBLOCK:
l = n->list;
goto loop;
case OGOTO:
case ORETURN:
return 0;
case OCALL:
if(n->left->op == ONAME) {
switch(n->left->etype) {
case OPANIC:
case OPANICN:
return 0;
}
}
break;
}
// all other statements
// will flow to the end
return 1;
}
void
walk(Node *fn)
{
char s[50];
curfn = fn;
if(debug['W']) {
snprint(s, sizeof(s), "\nbefore %S", curfn->nname->sym);
dumplist(s, curfn->nbody);
}
if(curfn->type->outtuple)
if(walkret(curfn->nbody))
yyerror("function ends without a return statement");
typechecklist(curfn->nbody, Etop);
walkstmtlist(curfn->nbody);
if(debug['W']) {
snprint(s, sizeof(s), "after walk %S", curfn->nname->sym);
dumplist(s, curfn->nbody);
}
heapmoves();
if(debug['W'] && curfn->enter != nil) {
snprint(s, sizeof(s), "enter %S", curfn->nname->sym);
dumplist(s, curfn->enter);
}
}
void
gettype(Node **np, NodeList **init)
{
if(debug['W'])
dump("\nbefore gettype", *np);
typecheck(np, Erv);
if(debug['W'])
dump("after gettype", *np);
}
void
walkdeflist(NodeList *l)
{
for(; l; l=l->next)
walkdef(l->n);
}
void
walkdef(Node *n)
{
int lno;
NodeList *init;
Node *e;
Type *t;
lno = lineno;
setlineno(n);
if(n->op == ONONAME) {
if(!n->diag) {
n->diag = 1;
yyerror("undefined: %S", n->sym);
}
return;
}
if(n->walkdef == 1)
return;
if(n->walkdef == 2) {
// TODO(rsc): better loop message
fatal("loop");
}
n->walkdef = 2;
if(n->type != T || n->sym == S) // builtin or no name
goto ret;
init = nil;
switch(n->op) {
case OLITERAL:
if(n->ntype != N) {
typecheck(&n->ntype, Etype);
n->type = n->ntype->type;
n->ntype = N;
if(n->type == T) {
n->diag = 1;
goto ret;
}
}
e = n->defn;
n->defn = N;
if(e == N) {
lineno = n->lineno;
dump("walkdef nil defn", n);
yyerror("xxx");
}
typecheck(&e, Erv);
if(e->op != OLITERAL) {
yyerror("const initializer must be constant");
goto ret;
}
t = n->type;
if(t != T)
convlit(&e, t);
n->val = e->val;
n->type = e->type;
break;
}
ret:
lineno = lno;
n->walkdef = 1;
}
void
walkstmtlist(NodeList *l)
{
for(; l; l=l->next)
walkstmt(&l->n);
}
void
walkstmt(Node **np)
2008-09-04 13:21:10 -06:00
{
NodeList *init;
NodeList *ll;
int lno;
Node *n;
n = *np;
2008-09-04 13:21:10 -06:00
if(n == N)
return;
lno = lineno;
setlineno(n);
2008-09-04 13:21:10 -06:00
switch(n->op) {
default:
if(n->op == ONAME)
yyerror("%S is not a top level statement", n->sym);
else
yyerror("%O is not a top level statement", n->op);
dump("nottop", n);
break;
2008-09-04 13:21:10 -06:00
case OASOP:
case OAS:
case OAS2:
case OCLOSE:
case OCLOSED:
2008-09-04 13:21:10 -06:00
case OCALLMETH:
case OCALLINTER:
case OCALL:
case OCALLFUNC:
2008-09-04 13:21:10 -06:00
case OSEND:
case ORECV:
case OPRINT:
case OPRINTN:
2008-09-04 13:21:10 -06:00
case OPANIC:
case OPANICN:
2008-09-04 13:21:10 -06:00
case OEMPTY:
init = n->ninit;
n->ninit = nil;
walkexpr(&n, Etop, &init);
n->ninit = concat(init, n->ninit);
break;
2008-09-04 13:21:10 -06:00
case OBREAK:
case ODCL:
2008-09-04 13:21:10 -06:00
case OCONTINUE:
case OFALL:
2008-09-04 13:21:10 -06:00
case OGOTO:
case OLABEL:
break;
case OBLOCK:
walkstmtlist(n->list);
break;
2008-09-04 13:21:10 -06:00
case OXCASE:
yyerror("case statement out of place");
n->op = OCASE;
2008-09-04 13:21:10 -06:00
case OCASE:
walkstmt(&n->right);
break;
2009-01-27 13:03:53 -07:00
case ODEFER:
hasdefer = 1;
walkexpr(&n->left, Etop, &n->ninit);
2008-09-04 13:21:10 -06:00
break;
case OFOR:
walkstmtlist(n->ninit);
walkbool(&n->ntest);
walkstmt(&n->nincr);
walkstmtlist(n->nbody);
break;
2008-09-04 13:21:10 -06:00
case OIF:
walkstmtlist(n->ninit);
walkbool(&n->ntest);
walkstmtlist(n->nbody);
walkstmtlist(n->nelse);
break;
case OPROC:
walkexpr(&n->left, Etop, &n->ninit);
break;
case ORETURN:
walkexprlist(n->list, Erv, &n->ninit);
if(curfn->type->outnamed && n->list == nil) {
// print("special return\n");
break;
}
ll = ascompatte(n->op, getoutarg(curfn->type), n->list, 1, &n->ninit);
n->list = reorder4(ll);
break;
case OSELECT:
walkselect(n);
break;
case OSWITCH:
walkswitch(n);
break;
case OXFALL:
yyerror("fallthrough statement out of place");
n->op = OFALL;
break;
2008-09-04 13:21:10 -06:00
}
*np = n;
}
/*
* walk the whole tree of the body of an
* expression or simple statement.
* the types expressions are calculated.
* compile-time constants are evaluated.
* complex side effects like statements are appended to init
*/
void
walkexprlist(NodeList *l, int top, NodeList **init)
{
for(; l; l=l->next)
walkexpr(&l->n, top, init);
}
void
walkexpr(Node **np, int top, NodeList **init)
{
Node *r, *l;
NodeList *ll, *lr;
Type *t;
Sym *s;
int et, cl, cr, typeok, op;
int32 lno;
Node *n;
n = *np;
if(n == N)
return;
lno = setlineno(n);
typeok = top & Etype;
top &= ~Etype;
if(debug['w'] > 1 && top == Etop)
dump("walk-before", n);
if(n->typecheck != 1) {
dump("missed typecheck", n);
fatal("missed typecheck");
}
reswitch:
t = T;
et = Txxx;
switch(n->op) {
default:
dump("walk", n);
fatal("walkexpr: switch 1 unknown op %N", n);
goto ret;
case OTYPE:
goto ret;
2008-09-03 15:40:22 -06:00
case OKEY:
walkexpr(&n->left, top | typeok, init);
walkexpr(&n->right, top | typeok, init);
goto ret;
case OPRINT:
if(top != Etop)
goto nottop;
walkexprlist(n->list, Erv, init);
n = prcompat(n->list, 0, 0);
//dump("prcompat", n);
goto ret;
case OPRINTN:
if(top != Etop)
goto nottop;
walkexprlist(n->list, Erv, init);
n = prcompat(n->list, 1, 0);
goto ret;
case OPANIC:
if(top != Etop)
goto nottop;
walkexprlist(n->list, Erv, init);
n = prcompat(n->list, 0, 1);
goto ret;
case OPANICN:
if(top != Etop)
goto nottop;
walkexprlist(n->list, Erv, init);
n = prcompat(n->list, 2, 1);
goto ret;
case OLITERAL:
if(!(top & Erv))
goto nottop;
n->addable = 1;
goto ret;
case ONONAME:
s = n->sym;
if(n->diag == 0) {
s->undef = 1;
n->diag = 1;
yyerror("undefined: %S", s);
goto ret;
}
if(top == Etop)
goto nottop;
goto ret;
case ONAME:
if(top == Etop)
goto nottop;
if(!(n->class & PHEAP) && n->class != PPARAMREF)
n->addable = 1;
if(n->type == T) {
s = n->sym;
if(s->undef == 0) {
if(n->etype != 0)
yyerror("walkexpr: %S must be called", s, init);
else
yyerror("walkexpr: %S undeclared", s, init);
s->undef = 1;
}
}
goto ret;
case OCALLMETH:
case OCALLINTER:
case OCALLFUNC:
goto ret;
case OCALL:
if(top == Elv)
goto nottop;
if(n->left == N)
goto ret;
2009-02-16 18:44:05 -07:00
if(n->left->op == ONAME && n->left->etype != 0) {
// builtin OLEN, OCAP, etc.
n->op = n->left->etype;
n->left = N;
goto reswitch;
}
walkexpr(&n->left, Erv | Etype | Ecall, init);
defaultlit(&n->left, T);
2009-02-16 18:44:05 -07:00
t = n->left->type;
if(t == T)
goto ret;
switch(n->left->op) {
case ODOTMETH:
n->op = OCALLMETH;
break;
case ODOTINTER:
n->op = OCALLINTER;
break;
case OTYPE:
n->op = OCONV;
if(!(top & Erv))
goto nottop;
// turn CALL(type, arg) into CONV(arg) w/ type.
n->type = n->left->type;
if(n->list == nil) {
yyerror("missing argument in type conversion");
goto ret;
}
if(n->list->next != nil) {
yyerror("too many arguments in type conversion");
goto ret;
}
n->left = n->list->n;
n->list = nil;
goto reswitch;
default:
n->op = OCALLFUNC;
break;
}
if(t->etype != TFUNC) {
yyerror("call of a non-function: %T", t);
goto ret;
}
dowidth(t);
n->type = *getoutarg(t);
switch(t->outtuple) {
case 0:
if(top == Erv) {
yyerror("function requires a return type");
n->type = types[TINT];
}
break;
case 1:
if(n->type != T && n->type->type != T && n->type->type->type != T)
n->type = n->type->type->type;
break;
}
walkexprlist(n->list, Erv, init);
switch(n->op) {
default:
fatal("walk: op: %O", n->op);
case OCALLINTER:
ll = ascompatte(n->op, getinarg(t), n->list, 0, init);
n->list = reorder1(ll);
break;
case OCALLFUNC:
ll = ascompatte(n->op, getinarg(t), n->list, 0, init);
n->list = reorder1(ll);
if(isselect(n)) {
// special prob with selectsend and selectrecv:
// if chan is nil, they don't know big the channel
// element is and therefore don't know how to find
// the output bool, so we clear it before the call.
Node *b;
b = nodbool(0);
lr = ascompatte(n->op, getoutarg(t), list1(b), 0, init);
n->list = concat(n->list, lr);
}
break;
case OCALLMETH:
ll = ascompatte(n->op, getinarg(t), n->list, 0, init);
lr = ascompatte(n->op, getthis(t), list1(n->left->left), 0, init);
ll = concat(ll, lr);
n->left->left = N;
ullmancalc(n->left);
n->list = reorder1(ll);
break;
}
goto ret;
case OAS:
if(top != Etop)
goto nottop;
*init = concat(*init, n->ninit);
n->ninit = nil;
walkexpr(&n->left, Elv, init);
walkexpr(&n->right, Erv, init);
l = n->left;
r = n->right;
2008-06-22 22:02:06 -06:00
if(l == N || r == N)
goto ret;
r = ascompatee1(n->op, l, r, init);
if(r != N)
n = r;
goto ret;
case OAS2:
if(top != Etop)
goto nottop;
*init = concat(*init, n->ninit);
n->ninit = nil;
walkexprlist(n->list, Elv, init);
2008-06-22 22:02:06 -06:00
cl = count(n->list);
cr = count(n->rlist);
2008-06-22 22:02:06 -06:00
if(cl == cr) {
multias:
walkexprlist(n->rlist, Erv, init);
ll = ascompatee(OAS, n->list, n->rlist, init);
ll = reorder3(ll);
n = liststmt(ll);
2008-06-22 22:02:06 -06:00
goto ret;
}
l = n->list->n;
r = n->rlist->n;
2008-07-13 15:29:46 -06:00
// count mismatch - special cases
switch(r->op) {
2008-06-22 22:02:06 -06:00
case OCALLMETH:
case OCALLINTER:
case OCALLFUNC:
2008-06-22 22:02:06 -06:00
case OCALL:
if(cr == 1) {
// a,b,... = fn()
walkexpr(&r, Erv, init);
if(r->type == T || r->type->etype != TSTRUCT)
break;
ll = ascompatet(n->op, n->list, &r->type, 0, init);
n = liststmt(concat(list1(r), ll));
goto ret;
2008-06-10 22:29:57 -06:00
}
2008-06-22 22:02:06 -06:00
break;
2008-06-17 19:07:40 -06:00
2008-06-22 22:02:06 -06:00
case OINDEX:
if(cl == 2 && cr == 1) {
// a,b = map[] - mapaccess2
walkexpr(&r->left, Erv, init);
if(!istype(r->left->type, TMAP))
break;
l = mapop(n, top, init);
if(l == N)
break;
n = l;
goto ret;
}
break;
2008-07-13 15:29:46 -06:00
case ORECV:
if(cl == 2 && cr == 1) {
// a,b = <chan - chanrecv2
walkexpr(&r->left, Erv, init);
if(!istype(r->left->type, TCHAN))
2008-07-13 15:29:46 -06:00
break;
l = chanop(n, top, init);
2008-07-13 15:29:46 -06:00
if(l == N)
break;
n = l;
2008-07-13 15:29:46 -06:00
goto ret;
}
break;
2008-11-05 15:27:07 -07:00
case ODOTTYPE:
walkdottype(r, init);
2008-11-05 15:27:07 -07:00
if(cl == 2 && cr == 1) {
// a,b = i.(T)
if(r->left == N)
break;
et = ifaceas1(r->type, r->left->type, 1);
2008-11-05 15:27:07 -07:00
switch(et) {
case I2Isame:
case E2Esame:
n->rlist = list(list1(r->left), nodbool(1));
typechecklist(n->rlist, Erv);
goto multias;
case I2E:
n->list = list(list1(n->right), nodbool(1));
typechecklist(n->rlist, Erv);
goto multias;
2008-11-05 15:27:07 -07:00
case I2T:
et = I2T2;
break;
case I2Ix:
2008-11-05 15:27:07 -07:00
et = I2I2;
break;
case E2I:
et = E2I2;
break;
case E2T:
et = E2T2;
break;
2008-11-05 15:27:07 -07:00
default:
et = Inone;
break;
}
if(et == Inone)
break;
r = ifacecvt(r->type, r->left, et);
ll = ascompatet(n->op, n->list, &r->type, 0, init);
n = liststmt(concat(list1(r), ll));
2008-11-05 15:27:07 -07:00
goto ret;
}
break;
}
switch(l->op) {
case OINDEX:
if(cl == 1 && cr == 2) {
// map[] = a,b - mapassign2
if(!istype(l->left->type, TMAP))
break;
l = mapop(n, top, init);
if(l == N)
break;
n = l;
goto ret;
}
2008-06-22 22:02:06 -06:00
break;
}
if(l->diag == 0) {
l->diag = 1;
yyerror("assignment count mismatch: %d = %d", cl, cr);
}
goto ret;
case OINDREG:
2008-09-29 21:33:51 -06:00
case OEMPTY:
goto ret;
case ODOTTYPE:
walkdottype(n, init);
// fall through
case OCONV:
if(!(top & Erv))
goto nottop;
walkconv(&n, init);
goto ret;
case OCONVNOP:
goto ret;
case OCOMPMAP:
case OCOMPSLICE:
goto ret;
case OCOMPOS:
walkexpr(&n->right, Etype, init);
t = n->right->type;
n->type = t;
if(t == T)
goto ret;
switch(t->etype) {
default:
yyerror("invalid type for composite literal: %T", t);
goto ret;
case TSTRUCT:
r = structlit(n, N, init);
break;
case TARRAY:
r = arraylit(n, N, init);
break;
case TMAP:
r = maplit(n, N, init);
break;
}
n = r;
goto ret;
case ONOT:
if(!(top & Erv))
goto nottop;
if(n->op == OLITERAL)
goto ret;
walkexpr(&n->left, Erv, init);
if(n->left == N || n->left->type == T)
goto ret;
et = n->left->type->etype;
break;
case OASOP:
if(top != Etop)
goto nottop;
walkexpr(&n->left, Elv, init);
l = n->left;
if(l->op == OINDEX && istype(l->left->type, TMAP))
n = mapop(n, top, init);
if(n->etype == OLSH || n->etype == ORSH)
goto shft;
goto com;
case OLSH:
case ORSH:
if(!(top & Erv))
goto nottop;
walkexpr(&n->left, Erv, init);
shft:
walkexpr(&n->right, Erv, init);
if(n->left == N || n->right == N)
goto ret;
evconst(n);
if(n->op == OLITERAL)
goto ret;
// do NOT defaultlit n->left.
// let parent defaultlit or convlit instead.
defaultlit(&n->right, types[TUINT]);
if(n->left->type == T || n->right->type == T)
goto ret;
2009-07-27 17:17:09 -06:00
et = n->right->type->etype;
if(issigned[et] || !isint[et])
goto badt;
// check of n->left->type happens in second switch.
break;
case OMOD:
case OAND:
case OANDNOT:
case OOR:
case OXOR:
case OANDAND:
case OOROR:
case OEQ:
case ONE:
case OLT:
case OLE:
case OGE:
case OGT:
case OADD:
case OSUB:
case OMUL:
case ODIV:
if(!(top & Erv))
goto nottop;
walkexpr(&n->left, Erv, init);
com:
walkexpr(&n->right, Erv, init);
if(n->left == N || n->right == N)
goto ret;
evconst(n);
if(n->op == OLITERAL)
goto ret;
defaultlit2(&n->left, &n->right, iscmp[n->op]);
if(n->left->type == T || n->right->type == T)
goto ret;
if(!eqtype(n->left->type, n->right->type))
goto badt;
switch(n->op) {
case OANDNOT:
n->op = OAND;
n->right = nod(OCOM, n->right, N);
n->right->type = n->right->left->type;
break;
case OASOP:
if(n->etype == OANDNOT) {
n->etype = OAND;
n->right = nod(OCOM, n->right, N);
n->right->type = n->right->left->type;
break;
}
if(istype(n->left->type, TSTRING)) {
n = stringop(n, top, init);
goto ret;
}
break;
case OEQ:
case ONE:
case OLT:
case OLE:
case OGE:
case OGT:
case OADD:
if(istype(n->left->type, TSTRING)) {
n = stringop(n, top, nil);
goto ret;
}
break;
}
break;
case OMINUS:
case OPLUS:
case OCOM:
if(!(top & Erv))
goto nottop;
walkexpr(&n->left, Erv, init);
if(n->left == N)
goto ret;
if(n->op == OLITERAL)
goto ret;
break;
case OLEN:
if(!(top & Erv))
goto nottop;
2009-07-09 12:41:06 -06:00
if(n->left == N) {
if(n->list == nil) {
yyerror("missing argument to len");
goto ret;
}
if(n->list->next)
yyerror("too many arguments to len");
n->left = n->list->n;
2009-07-09 12:41:06 -06:00
}
walkexpr(&n->left, Erv, init);
defaultlit(&n->left, T);
t = n->left->type;
if(t == T)
goto ret;
switch(t->etype) {
default:
goto badt;
case TSTRING:
if(isconst(n->left, CTSTR))
2009-03-10 20:16:31 -06:00
nodconst(n, types[TINT], n->left->val.u.sval->len);
break;
case TMAP:
2008-07-18 12:59:35 -06:00
break;
case TARRAY:
if(t->bound >= 0)
nodconst(n, types[TINT], t->bound);
break;
}
n->type = types[TINT];
goto ret;
2008-08-27 18:28:30 -06:00
case OCAP:
if(!(top & Erv))
2008-08-27 18:28:30 -06:00
goto nottop;
if(n->left == N) {
if(n->list == nil) {
yyerror("missing argument to cap");
goto ret;
}
if(n->list->next)
yyerror("too many arguments to cap");
n->left = n->list->n;
}
walkexpr(&n->left, Erv, init);
defaultlit(&n->left, T);
2008-08-27 18:28:30 -06:00
t = n->left->type;
if(t == T)
goto ret;
switch(t->etype) {
default:
goto badt;
case TARRAY:
if(t->bound >= 0)
nodconst(n, types[TINT], t->bound);
2008-08-27 18:28:30 -06:00
break;
}
n->type = types[TINT];
2008-08-27 18:28:30 -06:00
goto ret;
case OINDEX:
if(top == Etop)
goto nottop;
walkexpr(&n->left, Erv, init);
walkexpr(&n->right, Erv, init);
if(n->left == N || n->right == N)
goto ret;
2008-06-16 23:34:50 -06:00
defaultlit(&n->left, T);
t = n->left->type;
if(t == T)
goto ret;
switch(t->etype) {
default:
defaultlit(&n->right, T);
goto badt;
case TSTRING:
2008-07-05 18:43:25 -06:00
// right side must be an int
if(!(top & Erv))
2008-07-05 18:43:25 -06:00
goto nottop;
defaultlit(&n->right, types[TINT]);
if(n->right->type == T)
break;
2008-07-05 18:43:25 -06:00
if(!isint[n->right->type->etype])
goto badt;
n = stringop(n, top, nil);
break;
2008-06-16 23:34:50 -06:00
case TMAP:
2008-07-13 15:29:46 -06:00
// right side must be map type
defaultlit(&n->right, t->down);
if(n->right->type == T)
break;
if(!eqtype(n->right->type, t->down))
2008-06-16 23:34:50 -06:00
goto badt;
n->type = t->type;
if(top == Erv)
n = mapop(n, top, nil);
2008-06-16 23:34:50 -06:00
break;
2008-08-27 18:28:30 -06:00
case TARRAY:
2008-06-16 23:34:50 -06:00
// right side must be an int
defaultlit(&n->right, types[TINT]);
if(n->right->type == T)
break;
2008-06-16 23:34:50 -06:00
if(!isint[n->right->type->etype])
goto badt;
n->type = t->type;
break;
}
goto ret;
case OCLOSE:
if(top != Etop)
goto nottop;
walkexpr(&n->left, Erv, init); // chan
n = chanop(n, top, nil);
goto ret;
case OCLOSED:
if(top == Elv)
goto nottop;
walkexpr(&n->left, Erv, init); // chan
n = chanop(n, top, nil);
goto ret;
2008-07-13 15:29:46 -06:00
case OSEND:
2008-07-15 22:07:59 -06:00
if(top == Elv)
2008-07-13 15:29:46 -06:00
goto nottop;
walkexpr(&n->left, Erv, init); // chan
walkexpr(&n->right, Erv, init); // e
n = chanop(n, top, nil);
2008-07-13 15:29:46 -06:00
goto ret;
case ORECV:
2008-07-15 22:07:59 -06:00
if(top == Elv)
2008-07-13 15:29:46 -06:00
goto nottop;
2008-07-15 22:07:59 -06:00
if(n->right == N) {
walkexpr(&n->left, Erv, init); // chan
n = chanop(n, top, init); // returns e blocking
2008-07-15 22:07:59 -06:00
goto ret;
}
walkexpr(&n->left, Elv, init); // e
walkexpr(&n->right, Erv, init); // chan
n = chanop(n, top, nil); // returns bool non-blocking
2008-07-13 15:29:46 -06:00
goto ret;
case OSLICE:
if(top == Etop)
goto nottop;
walkexpr(&n->left, top, init);
walkexpr(&n->right->left, Erv, init);
walkexpr(&n->right->right, Erv, init);
if(n->left == N || n->right == N)
goto ret;
defaultlit(&n->left, T);
defaultlit(&n->right->left, types[TUINT]);
defaultlit(&n->right->right, types[TUINT]);
2008-08-27 18:28:30 -06:00
t = n->left->type;
if(t == T)
goto ret;
2008-08-27 18:28:30 -06:00
if(t->etype == TSTRING) {
n = stringop(n, top, nil);
goto ret;
}
if(t->etype == TARRAY) {
n = arrayop(n, top);
2008-08-27 18:28:30 -06:00
goto ret;
}
badtype(OSLICE, n->left->type, T);
goto ret;
case ODOT:
case ODOTPTR:
case ODOTMETH:
case ODOTINTER:
walkexpr(&n->left, Erv, init);
goto ret;
case OADDR:
if(!(top & Erv))
goto nottop;
defaultlit(&n->left, T);
if(n->left->op == OCOMPOS) {
walkexpr(&n->left->right, Etype, init);
n->left->type = n->left->right->type;
if(n->left->type == T)
goto ret;
Node *nvar, *nas, *nstar;
// turn &Point(1, 2) or &[]int(1, 2) or &[...]int(1, 2) into allocation.
// initialize with
// nvar := new(*Point);
// *nvar = Point(1, 2);
// and replace expression with nvar
nvar = nod(OXXX, N, N);
tempname(nvar, ptrto(n->left->type));
nas = nod(OAS, nvar, callnew(n->left->type));
typecheck(&nas, Etop);
walkexpr(&nas, Etop, init);
*init = list(*init, nas);
nstar = nod(OIND, nvar, N);
nstar->type = n->left->type;
switch(n->left->type->etype) {
case TSTRUCT:
structlit(n->left, nstar, init);
break;
case TARRAY:
arraylit(n->left, nstar, init);
break;
case TMAP:
maplit(n->left, nstar, init);
break;
default:
goto badlit;
}
// walkexpr(&n->left->left, Erv, init);
n = nvar;
goto ret;
}
badlit:
if(istype(n->left->type, TFUNC) && n->left->class == PFUNC) {
if(!n->diag) {
n->diag = 1;
yyerror("cannot take address of function");
}
}
if(n->left == N)
goto ret;
walkexpr(&n->left, Elv, init);
t = n->left->type;
if(t == T)
goto ret;
addrescapes(n->left);
n->type = ptrto(t);
goto ret;
case OIND:
if(top == Etop)
goto nottop;
if(top == Elv) // even if n is lvalue, n->left is rvalue
top = Erv;
if(n->left == N)
goto ret;
walkexpr(&n->left, top | Etype, init);
defaultlit(&n->left, T);
if(n->left->op == OTYPE) {
n->op = OTYPE;
n->type = ptrto(n->left->type);
goto ret;
}
t = n->left->type;
if(t == T)
goto ret;
if(!isptr[t->etype])
goto badt;
n->type = t->type;
goto ret;
case OMAKE:
if(!(top & Erv))
goto nottop;
n = makecompat(n);
goto ret;
case ONEW:
if(!(top & Erv))
goto nottop;
if(n->list == nil) {
yyerror("missing argument to new");
goto ret;
}
if(n->list->next)
yyerror("too many arguments to new");
walkexpr(&n->list->n, Etype, init);
l = n->list->n;
if((t = l->type) == T)
;
else
n = callnew(t);
goto ret;
}
/*
* ======== second switch ========
*/
op = n->op;
if(op == OASOP)
op = n->etype;
switch(op) {
default:
fatal("walkexpr: switch 2 unknown op %N", n, init);
goto ret;
case OASOP:
break;
case ONOT:
case OANDAND:
case OOROR:
if(n->left->type == T)
goto ret;
et = n->left->type->etype;
if(et != TBOOL)
goto badt;
t = types[TBOOL];
break;
case OEQ:
case ONE:
if(n->left->type == T)
goto ret;
et = n->left->type->etype;
if(!okforeq[et] && !isslice(n->left->type))
goto badt;
if(isinter(n->left->type)) {
n = ifaceop(n);
goto ret;
}
t = types[TBOOL];
break;
case OLT:
case OLE:
case OGE:
case OGT:
if(n->left->type == T)
goto ret;
et = n->left->type->etype;
if(!okforarith[et] && et != TSTRING)
goto badt;
t = types[TBOOL];
break;
case OADD:
case OSUB:
case OMUL:
case ODIV:
case OPLUS:
if(n->left->type == T)
goto ret;
et = n->left->type->etype;
if(!okforarith[et])
goto badt;
break;
case OMINUS:
if(n->left->type == T)
goto ret;
et = n->left->type->etype;
if(!okforarith[et])
goto badt;
if(isfloat[et]) {
// TODO(rsc): Can do this more efficiently,
// but OSUB is wrong. Should be in back end anyway.
n = nod(OMUL, n->left, nodintconst(-1));
typecheck(&n, Erv);
walkexpr(&n, Erv, init);
goto ret;
}
break;
case OLSH:
case ORSH:
case OAND:
case OANDNOT:
case OOR:
case OXOR:
case OMOD:
case OCOM:
if(n->left->type == T)
goto ret;
et = n->left->type->etype;
if(et != TIDEAL && !okforand[et])
goto badt;
break;
}
/*
* rewrite div and mod into function calls
* on 32-bit architectures.
*/
switch(n->op) {
case ODIV:
case OMOD:
et = n->left->type->etype;
if(widthptr > 4 || (et != TUINT64 && et != TINT64))
break;
if(et == TINT64)
strcpy(namebuf, "int64");
else
strcpy(namebuf, "uint64");
if(n->op == ODIV)
strcat(namebuf, "div");
else
strcat(namebuf, "mod");
l = syslook(namebuf, 0);
n->left = nod(OCONV, n->left, N);
n->left->type = types[et];
n->right = nod(OCONV, n->right, N);
n->right->type = types[et];
r = nod(OCALL, l, N);
r->list = list(list1(n->left), n->right);
r = nod(OCONV, r, N);
r->type = n->left->left->type;
typecheck(&r, Erv);
walkexpr(&r, Erv, init);
n = r;
goto ret;
case OASOP:
et = n->left->type->etype;
if(widthptr > 4 || (et != TUINT64 && et != TINT64))
break;
l = saferef(n->left, init);
r = nod(OAS, l, nod(n->etype, l, n->right));
typecheck(&r, Etop);
walkexpr(&r, Etop, init);
n = r;
goto ret;
}
if(t == T)
t = n->left->type;
n->type = t;
goto ret;
nottop:
if(n->diag)
goto ret;
n->diag = 1;
switch(top | typeok) {
2008-11-22 18:58:53 -07:00
default:
yyerror("didn't expect %O here [top=%d]", n->op, top);
break;
case Etype:
yyerror("operation %O not allowed in type context", n->op);
2008-11-22 18:58:53 -07:00
break;
case Etop:
yyerror("operation %O not allowed in statement context", n->op);
break;
case Elv:
yyerror("operation %O not allowed in assignment context", n->op);
break;
case Erv:
case Erv | Etype:
2008-11-22 18:58:53 -07:00
yyerror("operation %O not allowed in expression context", n->op);
break;
}
2008-06-10 22:29:57 -06:00
goto ret;
badt:
if(n->diag)
goto ret;
n->diag = 1;
if(n->right == N) {
if(n->left == N) {
badtype(n->op, T, T);
goto ret;
}
badtype(n->op, n->left->type, T);
goto ret;
}
op = n->op;
if(op == OASOP)
op = n->etype;
badtype(op, n->left->type, n->right->type);
goto ret;
ret:
2008-06-17 19:07:40 -06:00
if(debug['w'] && top == Etop && n != N)
dump("walk", n);
2008-06-16 23:34:50 -06:00
if(typeok && top == 0) { // must be type
if(n->op != OTYPE) {
if(n->sym) {
if(!n->sym->undef)
yyerror("%S is not a type", n->sym);
} else {
yyerror("expr %O is not type", n->op);
n->op = OTYPE; // leads to fewer errors later
n->type = T;
}
}
}
if(!typeok && n->op == OTYPE)
yyerror("cannot use type %T as expr", n->type);
ullmancalc(n);
2008-06-26 18:54:44 -06:00
lineno = lno;
*np = n;
}
void
walkbool(Node **np)
{
Node *n;
n = *np;
if(n == N)
return;
walkexpr(np, Erv, &n->ninit);
defaultlit(np, T);
n = *np;
if(n->type != T && !eqtype(n->type, types[TBOOL]))
yyerror("IF and FOR require a boolean type");
}
void
walkdottype(Node *n, NodeList **init)
{
walkexpr(&n->left, Erv, init);
if(n->left == N)
return;
defaultlit(&n->left, T);
if(!isinter(n->left->type))
yyerror("type assertion requires interface on left, have %T", n->left->type);
if(n->right != N) {
walkexpr(&n->right, Etype, init);
n->type = n->right->type;
n->right = N;
}
}
void
walkconv(Node **np, NodeList **init)
{
int et;
char *what;
Type *t;
Node *l;
Node *n;
n = *np;
t = n->type;
if(t == T)
return;
typecheck(&n->left, Erv);
walkexpr(&n->left, Erv, init);
l = n->left;
if(l == N)
return;
if(l->type == T)
return;
// if using .(T), interface assertion.
if(n->op == ODOTTYPE) {
et = ifaceas1(t, l->type, 1);
if(et == I2Isame || et == E2Esame)
goto nop;
if(et != Inone) {
n = ifacecvt(t, l, et);
*np = n;
return;
}
goto bad;
}
// otherwise, conversion.
convlit1(&n->left, t, 1);
l = n->left;
if(l->type == T)
return;
// no-op conversion
if(cvttype(t, l->type) == 1) {
nop:
if(l->op == OLITERAL) {
*n = *l;
n->type = t;
return;
}
// leave OCONV node in place
// in case tree gets walked again.
// back end will ignore.
n->op = OCONVNOP;
return;
}
// to/from interface.
// ifaceas1 will generate a good error
// if the conversion is invalid.
if(t->etype == TINTER || l->type->etype == TINTER) {
n = ifacecvt(t, l, ifaceas1(t, l->type, 0));
*np = n;
return;
}
// simple fix-float
if(isint[l->type->etype] || isfloat[l->type->etype])
if(isint[t->etype] || isfloat[t->etype]) {
evconst(n);
return;
}
// to string
if(l->type != T)
if(istype(t, TSTRING)) {
et = l->type->etype;
if(isint[et]) {
n = stringop(n, Erv, nil);
*np = n;
return;
}
// can convert []byte and *[10]byte
if((isptr[et] && isfixedarray(l->type->type) && istype(l->type->type->type, TUINT8))
|| (isslice(l->type) && istype(l->type->type, TUINT8))) {
n->op = OARRAY;
n = stringop(n, Erv, nil);
*np = n;
return;
}
// can convert []int and *[10]int
if((isptr[et] && isfixedarray(l->type->type) && istype(l->type->type->type, TINT))
|| (isslice(l->type) && istype(l->type->type, TINT))) {
n->op = OARRAY;
n = stringop(n, Erv, nil);
*np = n;
return;
}
}
// convert dynamic to static generated by ONEW/OMAKE
if(isfixedarray(t) && isslice(l->type))
return;
// convert static array to dynamic array
if(isslice(t) && isptr[l->type->etype] && isfixedarray(l->type->type)) {
if(eqtype(t->type->type, l->type->type->type->type)) {
n = arrayop(n, Erv);
*np = n;
return;
}
}
// convert to unsafe.pointer
if(isptrto(n->type, TANY)) {
if(isptr[l->type->etype])
return;
if(l->type->etype == TUINTPTR)
return;
}
// convert from unsafe.pointer
if(isptrto(l->type, TANY)) {
if(isptr[t->etype])
return;
if(t->etype == TUINTPTR)
return;
}
bad:
if(n->diag)
return;
n->diag = 1;
if(n->op == ODOTTYPE)
what = "type assertion";
else
what = "conversion";
if(l->type != T)
yyerror("invalid %s: %T to %T", what, l->type, t);
}
Node*
selcase(Node *n, Node *var, NodeList **init)
{
Node *a, *r, *on, *c;
Type *t;
NodeList *args;
if(n->list == nil)
2008-11-05 22:50:28 -07:00
goto dflt;
c = n->list->n;
if(c->op == ORECV)
goto recv;
walkexpr(&c->left, Erv, init); // chan
walkexpr(&c->right, Erv, 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);
if(!ascompat(t->type, c->right->type)) {
badtype(c->op, t->type, c->right->type);
return N;
}
// selectsend(sel *byte, hchan *chan any, elem any) (selected bool);
on = syslook("selectsend", 1);
argtype(on, t->type);
argtype(on, t->type);
a = var; // sel-var
args = list1(a);
a = c->left; // chan
args = list(args, a);
a = c->right; // elem
args = list(args, a);
goto out;
recv:
if(c->right != N)
goto recv2;
walkexpr(&c->left, Erv, 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);
on = syslook("selectrecv", 1);
argtype(on, t->type);
argtype(on, t->type);
a = var; // sel-var
args = list1(a);
a = c->left; // chan
args = list(args, a);
a = c->left; // nil elem
a = nod(OLITERAL, N, N);
a->val.ctype = CTNIL;
a->type = types[TNIL];
args = list(args, a);
goto out;
recv2:
walkexpr(&c->right, Erv, 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, Elv, init); // check elem
convlit(&c->left, t->type);
if(!ascompat(t->type, c->left->type)) {
badtype(c->op, t->type, c->left->type);
return N;
}
// selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool);
on = syslook("selectrecv", 1);
argtype(on, t->type);
argtype(on, t->type);
a = var; // sel-var
args = list1(a);
a = c->right; // chan
args = list(args, a);
a = c->left; // elem
a = nod(OADDR, a, N);
args = list(args, a);
2008-11-05 22:50:28 -07:00
goto out;
dflt:
// selectdefault(sel *byte);
on = syslook("selectdefault", 0);
a = var;
args = list1(a);
2008-11-05 22:50:28 -07:00
goto out;
out:
a = nod(OCALL, on, N);
a->list = args;
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, Erv, 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 *args, *res, *bod, *nbod, *init, *ln;
2008-11-05 22:50:28 -07:00
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) {
2009-05-30 22:18:15 -06:00
yyerror("empty select");
return;
}
count = 0; // number of cases
res = nil; // entire select body
bod = nil; // body of each case
oc = N; // last case
2008-11-05 22:50:28 -07:00
def = N; // default case
for(ln=sel->list; ln; ln=ln->next) {
n = ln->n;
setlineno(n);
2009-05-30 22:18:15 -06:00
if(n->op != OXCASE)
fatal("walkselect %O", n->op);
2009-05-30 22:18:15 -06:00
count++;
l = N;
if(n->list == nil) {
2009-05-30 22:18:15 -06:00
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;
}
}
2009-05-30 22:18:15 -06:00
nbod = nil;
2009-05-30 22:18:15 -06:00
switch(op) {
default:
yyerror("select cases must be send, recv or default %O", op);
2009-05-30 22:18:15 -06:00
continue;
2009-05-30 22:18:15 -06:00
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;
}
2009-05-30 22:18:15 -06:00
r = l->right; // rcv
r->right = r->left;
r->left = l->left;
n->list->n = r;
2009-05-30 22:18:15 -06:00
// 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) {
2009-05-30 22:18:15 -06:00
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;
2009-05-30 22:18:15 -06:00
}
break;
case OSEND:
case ORECV:
break;
}
2009-05-30 22:18:15 -06:00
nbod = concat(nbod, n->nbody);
2009-05-30 22:18:15 -06:00
nbod = list(nbod, nod(OBREAK, N, N));
n->nbody = nil;
2009-05-30 22:18:15 -06:00
oc = selcase(n, var, &init);
2009-05-30 22:18:15 -06:00
if(oc != N) {
oc->nbody = nbod;
2009-05-30 22:18:15 -06:00
res = list(res, oc);
}
}
setlineno(sel);
// selectgo(sel *byte);
on = syslook("selectgo", 0);
r = nod(OCALL, on, N);
r->list = list1(var); // sel-var
res = list(res, r);
// newselect(size uint32) (sel *byte);
on = syslook("newselect", 0);
r = nod(OXXX, N, N);
nodconst(r, types[TINT], count); // count
args = list1(r);
r = nod(OCALL, on, N);
r->list = args;
r = nod(OAS, var, r);
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)
{
Node *a;
2008-06-10 22:29:57 -06:00
/*
* check assign expression to
* a expression. called in
* expr = expr
2008-06-10 22:29:57 -06:00
*/
convlit(&r, l->type);
if(!ascompat(l->type, r->type)) {
badtype(op, l->type, r->type);
return N;
}
if(l->op == ONAME && l->class == PFUNC)
yyerror("cannot assign to function");
a = nod(OAS, l, r);
a = convas(a, init);
return a;
}
NodeList*
ascompatee(int op, NodeList *nl, NodeList *nr, NodeList **init)
{
NodeList *ll, *lr, *nn;
/*
* check assign expression list to
* a expression list. called in
* expr-list = expr-list
*/
nn = nil;
for(ll=nl, lr=nr; ll && lr; ll=ll->next, lr=lr->next)
nn = list(nn, ascompatee1(op, ll->n, lr->n, init));
// cannot happen: caller checked that lists had same length
if(ll || lr)
yyerror("error in shape across %O", op);
return nn;
}
2009-05-26 16:56:37 -06:00
/*
* n is an lv and t is the type of an rv
* return 1 if this implies a function call
* evaluating the lv or a function call
* in the conversion of the types
*/
int
fncall(Node *l, Type *rt)
{
if(l->ullman >= UINF)
return 1;
if(eqtype(l->type, rt))
return 0;
return 1;
}
NodeList*
ascompatet(int op, NodeList *nl, Type **nr, int fp, NodeList **init)
{
Node *l, *tmp, *a;
NodeList *ll;
Type *r;
Iter saver;
2009-05-26 16:56:37 -06:00
int ucount;
NodeList *nn, *mm;
2008-06-10 22:29:57 -06:00
/*
* check assign type list to
* a expression list. called in
* expr-list = func()
*/
r = structfirst(&saver, nr);
nn = nil;
mm = nil;
2009-05-26 16:56:37 -06:00
ucount = 0;
for(ll=nl; ll; ll=ll->next) {
if(r == T)
break;
l = ll->n;
if(!ascompat(l->type, r->type)) {
badtype(op, l->type, r->type);
return nil;
2009-05-26 16:56:37 -06:00
}
// any lv that causes a fn call must be
// deferred until all the return arguments
// have been pulled from the output arguments
if(fncall(l, r->type)) {
tmp = nod(OXXX, N, N);
tempname(tmp, r->type);
a = nod(OAS, l, tmp);
a = convas(a, init);
mm = list(mm, a);
l = tmp;
}
a = nod(OAS, l, nodarg(r, fp));
a = convas(a, init);
ullmancalc(a);
if(a->ullman >= UINF)
ucount++;
nn = list(nn, a);
r = structnext(&saver);
2009-05-26 16:56:37 -06:00
}
if(ll != nil || r != T)
yyerror("assignment count mismatch: %d = %d",
count(nl), structcount(*nr));
if(ucount)
yyerror("reorder2: too many function calls evaluating parameters");
return concat(nn, mm);
}
2008-11-01 17:36:46 -06:00
/*
* make a tsig for the structure
* carrying the ... arguments
*/
Type*
sigtype(Type *st)
{
Sym *s;
Type *t;
static int sigdddgen;
dowidth(st);
sigdddgen++;
snprint(namebuf, sizeof(namebuf), "dsigddd_%d", sigdddgen);
s = lookup(namebuf);
t = newtype(s);
t = dodcltype(t);
updatetype(t, st);
t->local = 1;
return t;
2008-11-01 17:36:46 -06:00
}
/*
* package all the arguments that
* match a ... parameter into an
* automatic structure.
* then call the ... arg (interface)
* with a pointer to the structure.
2008-11-01 17:36:46 -06:00
*/
NodeList*
mkdotargs(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init)
2008-11-01 17:36:46 -06:00
{
Node *r;
2008-11-01 17:36:46 -06:00
Type *t, *st, *ft;
Node *a, *var;
NodeList *lr, *n;
2008-11-01 17:36:46 -06:00
n = nil; // list of assignments
2008-11-01 17:36:46 -06:00
st = typ(TSTRUCT); // generated structure
ft = T; // last field
for(lr=lr0; lr; lr=lr->next) {
r = lr->n;
2009-05-27 19:37:02 -06:00
if(r->op == OLITERAL && r->val.ctype == CTNIL) {
if(r->type == T || r->type->etype == TNIL) {
yyerror("inappropriate use of nil in ... argument");
return nil;
2009-05-27 19:37:02 -06:00
}
}
defaultlit(&r, T);
lr->n = r;
if(r->type == T) // type check failed
return nil;
2008-11-01 17:36:46 -06:00
// generate the next structure field
t = typ(TFIELD);
t->type = r->type;
if(ft == T)
st->type = t;
else
ft->down = t;
ft = t;
a = nod(OAS, N, r);
n = list(n, a);
}
// make a named type for the struct
st = sigtype(st);
dowidth(st);
2008-11-01 17:36:46 -06:00
// now we have the size, make the struct
var = nod(OXXX, N, N);
tempname(var, st);
var->sym = lookup(".ddd");
2008-11-01 17:36:46 -06:00
// assign the fields to the struct.
// use the init list so that reorder1 doesn't reorder
// these assignments after the interface conversion
// below.
2008-11-01 17:36:46 -06:00
t = st->type;
for(lr=n; lr; lr=lr->next) {
r = lr->n;
2008-11-01 17:36:46 -06:00
r->left = nod(OXXX, N, N);
*r->left = *var;
r->left->type = r->right->type;
r->left->xoffset += t->width;
typecheck(&r, Etop);
walkexpr(&r, Etop, init);
lr->n = r;
2008-11-01 17:36:46 -06:00
t = t->down;
}
*init = concat(*init, n);
2008-11-01 17:36:46 -06:00
// last thing is to put assignment
// of the structure to the DDD parameter
a = nod(OAS, nodarg(l, fp), var);
nn = list(nn, convas(a, init));
2008-11-01 17:36:46 -06:00
return nn;
}
/*
* helpers for shape errors
*/
static void
dumptypes(Type **nl, char *what)
{
int first;
Type *l;
Iter savel;
l = structfirst(&savel, nl);
print("\t");
first = 1;
for(l = structfirst(&savel, nl); l != T; l = structnext(&savel)) {
if(first)
first = 0;
else
print(", ");
print("%T", l);
}
if(first)
print("[no arguments %s]", what);
print("\n");
}
static void
dumpnodetypes(NodeList *l, char *what)
{
int first;
Node *r;
print("\t");
first = 1;
for(; l; l=l->next) {
r = l->n;
if(first)
first = 0;
else
print(", ");
print("%T", r->type);
}
if(first)
print("[no arguments %s]", what);
print("\n");
}
/*
* check assign expression list to
* a type list. called in
* return expr-list
* func(expr-list)
*/
NodeList*
ascompatte(int op, Type **nl, NodeList *lr, int fp, NodeList **init)
{
Type *l, *ll;
Node *r, *a;
NodeList *nn, *lr0;
Iter savel, peekl;
lr0 = lr;
l = structfirst(&savel, nl);
r = N;
if(lr)
r = lr->n;
nn = nil;
2008-11-01 17:36:46 -06:00
// 1 to many
peekl = savel;
if(l != T && r != N
&& structnext(&peekl) != T
&& lr->next == nil
&& eqtypenoname(r->type, *nl)) {
// clumsy check for differently aligned structs.
// now that output structs are aligned separately
// from the input structs, should never happen.
if(r->type->width != (*nl)->width)
fatal("misaligned multiple return\n\t%T\n\t%T", r->type, *nl);
a = nodarg(*nl, fp);
a->type = r->type;
nn = list1(convas(nod(OAS, a, r), init));
goto ret;
}
loop:
2008-11-01 17:36:46 -06:00
if(l != T && isddd(l->type)) {
// the ddd parameter must be last
ll = structnext(&savel);
if(ll != T)
yyerror("... must be last argument");
// special case --
// only if we are assigning a single ddd
// argument to a ddd parameter then it is
// passed thru unencapsulated
if(r != N && lr->next == nil && isddd(r->type)) {
a = nod(OAS, nodarg(l, fp), r);
a = convas(a, init);
nn = list(nn, a);
goto ret;
2008-11-01 17:36:46 -06:00
}
// normal case -- make a structure of all
// remaining arguments and pass a pointer to
// it to the ddd parameter (empty interface)
nn = mkdotargs(lr, nn, l, fp, init);
goto ret;
2008-11-01 17:36:46 -06:00
}
if(l == T || r == N) {
if(l != T || r != N) {
if(l != T)
yyerror("not enough arguments to %O", op);
else
yyerror("too many arguments to %O", op);
dumptypes(nl, "expected");
dumpnodetypes(lr0, "given");
}
goto ret;
}
convlit(&r, l->type);
if(!ascompat(l->type, r->type)) {
badtype(op, l->type, r->type);
return nil;
}
a = nod(OAS, nodarg(l, fp), r);
a = convas(a, init);
nn = list(nn, a);
l = structnext(&savel);
r = N;
lr = lr->next;
if(lr != nil)
r = lr->n;
goto loop;
ret:
for(lr=nn; lr; lr=lr->next)
lr->n->typecheck = 1;
return nn;
}
/*
* do the export rules allow writing to this type?
* cannot be implicitly assigning to any type with
* an unavailable field.
*/
int
exportasok(Type *t)
{
Type *f;
Sym *s;
if(t == T)
return 1;
switch(t->etype) {
default:
// most types can't contain others; they're all fine.
break;
case TSTRUCT:
for(f=t->type; f; f=f->down) {
if(f->etype != TFIELD)
fatal("structas: not field");
s = f->sym;
// s == nil doesn't happen for embedded fields (they get the type symbol).
// it only happens for fields in a ... struct.
if(s != nil && !exportname(s->name) && strcmp(package, s->package) != 0) {
yyerror("implicit assignment of %T field '%s'", t, s->name);
return 0;
}
if(!exportasok(f->type))
return 0;
}
break;
case TARRAY:
if(t->bound < 0) // slices are pointers; that's fine
break;
if(!exportasok(t->type))
return 0;
break;
}
return 1;
}
/*
* can we assign var of type src to var of type dst?
* return 0 if not, 1 if conversion is trivial, 2 if conversion is non-trivial.
*/
int
ascompat(Type *dst, Type *src)
{
if(eqtype(dst, src)) {
exportasok(src);
return 1;
}
if(dst == T || src == T)
return 0;
if(dst->etype == TFORWINTER || dst->etype == TFORWSTRUCT || dst->etype == TFORW)
return 0;
if(src->etype == TFORWINTER || src->etype == TFORWSTRUCT || src->etype == TFORW)
return 0;
// interfaces go through even if names don't match
if(isnilinter(dst) || isnilinter(src))
return 2;
if(isinter(dst) && isinter(src))
return 2;
if(isinter(dst) && methtype(src))
return 2;
if(isinter(src) && methtype(dst))
return 2;
// otherwise, if concrete types have names, they must match
if(dst->sym && src->sym && dst != src)
return 0;
if(dst->etype == TCHAN && src->etype == TCHAN) {
if(!eqtype(dst->type, src->type))
return 0;
if(dst->chan & ~src->chan)
return 0;
return 1;
}
if(isslice(dst)
&& isptr[src->etype]
&& isfixedarray(src->type)
&& eqtype(dst->type, src->type->type))
return 2;
return 0;
}
// generate code for print
// fmt = 0: print
// fmt = 1: println
Node*
prcompat(NodeList *all, int fmt, int dopanic)
{
Node *r;
Node *n;
NodeList *l;
Node *on;
Type *t;
int notfirst, et;
NodeList *calls;
calls = nil;
notfirst = 0;
for(l=all; l; l=l->next) {
if(notfirst) {
on = syslook("printsp", 0);
calls = list(calls, nod(OCALL, on, N));
}
notfirst = fmt;
typecheck(&l->n, Erv);
n = l->n;
if(n->op == OLITERAL) {
switch(n->val.ctype) {
case CTINT:
defaultlit(&n, types[TINT64]);
break;
case CTFLT:
defaultlit(&n, types[TFLOAT64]);
break;
}
2008-12-18 23:17:05 -07:00
}
if(n->op != OLITERAL && n->type && n->type->etype == TIDEAL)
defaultlit(&n, types[TINT64]);
2009-07-27 17:17:09 -06:00
defaultlit(&n, nil);
l->n = n;
if(n->type == T)
continue;
et = n->type->etype;
if(isinter(n->type)) {
if(isnilinter(n->type))
on = syslook("printeface", 1);
else
on = syslook("printiface", 1);
argtype(on, n->type); // any-1
} else if(isptr[et] || et == TCHAN || et == TMAP || et == TFUNC) {
on = syslook("printpointer", 1);
argtype(on, n->type); // any-1
} else if(isslice(n->type)) {
on = syslook("printarray", 1);
argtype(on, n->type); // any-1
} else if(isint[et]) {
if(et == TUINT64)
on = syslook("printuint", 0);
else
on = syslook("printint", 0);
} else if(isfloat[et]) {
on = syslook("printfloat", 0);
} else if(et == TBOOL) {
on = syslook("printbool", 0);
} else if(et == TSTRING) {
on = syslook("printstring", 0);
} else {
badtype(OPRINT, n->type, T);
continue;
}
t = *getinarg(on->type);
if(t != nil)
t = t->type;
if(t != nil)
t = t->type;
if(!eqtype(t, n->type)) {
n = nod(OCONV, n, N);
n->type = t;
}
r = nod(OCALL, on, N);
r->list = list1(n);
calls = list(calls, r);
}
if(fmt == 1 && !dopanic) {
on = syslook("printnl", 0);
calls = list(calls, nod(OCALL, on, N));
}
typechecklist(calls, Etop);
walkexprlist(calls, Etop, nil);
if(dopanic)
r = nodpanic(0);
else
r = nod(OEMPTY, N, N);
typecheck(&r, Etop);
walkexpr(&r, Etop, nil);
r->ninit = calls;
return r;
}
Node*
nodpanic(int32 lineno)
{
Node *n, *on;
NodeList *args;
on = syslook("panicl", 0);
n = nodintconst(lineno);
args = list1(n);
n = nod(OCALL, on, N);
n->list = args;
typecheck(&n, Etop);
walkexpr(&n, Etop, nil);
return n;
}
Node*
makecompat(Node *n)
{
Type *t;
Node *l, *r;
NodeList *args, *init;
2008-12-18 21:06:28 -07:00
//dump("makecompat", n);
args = n->list;
if(args == nil) {
yyerror("make requires type argument");
return n;
}
r = N;
l = args->n;
args = args->next;
init = nil;
walkexpr(&l, Etype, &init);
if(l->op != OTYPE) {
yyerror("cannot make(expr)");
return n;
}
t = l->type;
n->type = t;
n->list = args;
if(t != T)
switch(t->etype) {
case TARRAY:
if(!isslice(t))
goto bad;
return arrayop(n, Erv);
case TMAP:
return mapop(n, Erv, nil);
case TCHAN:
return chanop(n, Erv, nil);
}
bad:
if(!n->diag) {
n->diag = 1;
yyerror("cannot make(%T)", t);
}
return n;
}
Node*
callnew(Type *t)
{
Node *r, *on;
NodeList *args;
dowidth(t);
on = syslook("mal", 1);
argtype(on, t);
r = nodintconst(t->width);
args = list1(r);
r = nod(OCALL, on, N);
r->list = args;
typecheck(&r, Erv);
walkexpr(&r, Erv, nil);
return r;
}
Node*
stringop(Node *n, int top, NodeList **init)
{
Node *r, *c, *on;
NodeList *args;
switch(n->op) {
default:
2008-07-13 15:29:46 -06:00
fatal("stringop: unknown op %O", n->op);
case OEQ:
case ONE:
case OGE:
case OGT:
case OLE:
case OLT:
// sys_cmpstring(s1, s2) :: 0
on = syslook("cmpstring", 0);
2009-05-26 17:30:35 -06:00
r = nod(OCONV, n->left, N);
r->type = types[TSTRING];
args = list1(r);
2009-05-26 17:30:35 -06:00
c = nod(OCONV, n->right, N);
c->type = types[TSTRING];
args = list(args, c);
r = nod(OCALL, on, N);
r->list = args;
c = nodintconst(0);
r = nod(n->op, r, c);
break;
case OADD:
// sys_catstring(s1, s2)
on = syslook("catstring", 0);
2009-05-26 17:30:35 -06:00
r = nod(OCONV, n->left, N);
r->type = types[TSTRING];
args = list1(r);
2009-05-26 17:30:35 -06:00
c = nod(OCONV, n->right, N);
c->type = types[TSTRING];
args = list(args, c);
r = nod(OCALL, on, N);
r->list = args;
break;
case OASOP:
// sys_catstring(s1, s2)
switch(n->etype) {
default:
2008-07-13 15:29:46 -06:00
fatal("stringop: unknown op %O-%O", n->op, n->etype);
case OADD:
// s1 = sys_catstring(s1, s2)
if(n->etype != OADD)
fatal("stringop: not cat");
on = syslook("catstring", 0);
2009-05-26 17:30:35 -06:00
r = nod(OCONV, n->left, N);
r->type = types[TSTRING];
args = list1(r);
2009-05-26 17:30:35 -06:00
c = nod(OCONV, n->right, N);
c->type = types[TSTRING];
args = list(args, c);
r = nod(OCALL, on, N);
r->list = args;
r = nod(OAS, n->left, r);
break;
}
break;
case OSLICE:
r = nod(OCONV, n->left, N);
r->type = types[TSTRING];
args = list1(r);
// sys_slicestring(s, lb, hb)
r = nod(OCONV, n->right->left, N);
r->type = types[TINT];
args = list(args, r);
c = nod(OCONV, n->right->right, N);
c->type = types[TINT];
args = list(args, c);
on = syslook("slicestring", 0);
r = nod(OCALL, on, N);
r->list = args;
break;
2008-07-05 18:43:25 -06:00
case OINDEX:
// sys_indexstring(s, i)
r = nod(OCONV, n->left, N);
r->type = types[TSTRING];
args = list1(r);
r = nod(OCONV, n->right, N);
r->type = types[TINT];
args = list(args, r);
on = syslook("indexstring", 0);
r = nod(OCALL, on, N);
r->list = args;
break;
case OCONV:
// sys_intstring(v)
r = nod(OCONV, n->left, N);
r->type = types[TINT64];
args = list1(r);
on = syslook("intstring", 0);
r = nod(OCALL, on, N);
r->list = args;
break;
case OARRAY:
// arraystring([]byte) string;
on = syslook("arraystring", 0);
r = n->left;
if(r->type != T && r->type->type != T) {
if(istype(r->type->type, TINT) || istype(r->type->type->type, TINT)) {
// arraystring([]byte) string;
on = syslook("arraystringi", 0);
}
}
args = list1(r);
r = nod(OCALL, on, N);
r->list = args;
break;
}
typecheck(&r, top);
walkexpr(&r, top, init);
return r;
}
Type*
fixmap(Type *t)
{
if(t == T)
goto bad;
if(t->etype != TMAP)
goto bad;
if(t->down == T || t->type == T)
goto bad;
dowidth(t->down);
dowidth(t->type);
return t;
bad:
yyerror("not a map: %lT", t);
return T;
}
Type*
fixchan(Type *t)
{
if(t == T)
goto bad;
if(t->etype != TCHAN)
goto bad;
if(t->type == T)
goto bad;
dowidth(t->type);
return t;
bad:
yyerror("not a channel: %lT", t);
return T;
}
Node*
mapop(Node *n, int top, NodeList **init)
{
Node *r, *a, *l;
Type *t;
Node *on;
int cl, cr;
NodeList *args;
r = n;
switch(n->op) {
default:
2008-07-13 15:29:46 -06:00
fatal("mapop: unknown op %O", n->op);
case OMAKE:
cl = count(n->list);
2009-03-11 18:37:04 -06:00
if(cl > 1)
yyerror("too many arguments to make map");
if(!(top & Erv))
2008-06-16 23:34:50 -06:00
goto nottop;
// newmap(keysize int, valsize int,
// keyalg int, valalg int,
// hint int) (hmap map[any-1]any-2);
t = fixmap(n->type);
if(t == T)
break;
a = nodintconst(t->down->width); // key width
args = list1(a);
a = nodintconst(t->type->width); // val width
args = list(args, a);
a = nodintconst(algtype(t->down)); // key algorithm
args = list(args, a);
a = nodintconst(algtype(t->type)); // val algorithm
args = list(args, a);
if(cl == 1)
a = n->list->n; // hint
else
a = nodintconst(0);
args = list(args, a);
on = syslook("newmap", 1);
argtype(on, t->down); // any-1
argtype(on, t->type); // any-2
r = nod(OCALL, on, N);
r->list = args;
typecheck(&r, Erv);
walkexpr(&r, top, nil);
r->type = n->type;
break;
case OINDEX:
if(!(top & Erv))
2008-06-16 23:34:50 -06:00
goto nottop;
// mapaccess1(hmap map[any]any, key any) (val any);
t = fixmap(n->left->type);
if(t == T)
break;
convlit(&n->right, t->down);
if(!eqtype(n->right->type, t->down)) {
badtype(n->op, n->right->type, t->down);
break;
}
a = n->left; // map
args = list1(a);
a = n->right; // key
args = list(args, a);
2008-06-24 12:10:47 -06:00
on = syslook("mapaccess1", 1);
argtype(on, t->down); // any-1
argtype(on, t->type); // any-2
argtype(on, t->down); // any-3
argtype(on, t->type); // any-4
r = nod(OCALL, on, N);
r->list = args;
typecheck(&r, Erv);
walkexpr(&r, Erv, nil);
r->type = t->type;
break;
case OAS:
// mapassign1(hmap map[any-1]any-2, key any-3, val any-4);
if(n->left->op != OINDEX)
goto shape;
t = fixmap(n->left->left->type);
2008-06-16 23:34:50 -06:00
if(t == T)
break;
a = n->left->left; // map
args = list1(a);
a = n->left->right; // key
args = list(args, a);
a = n->right; // val
args = list(args, a);
2008-06-16 23:34:50 -06:00
on = syslook("mapassign1", 1);
2008-06-16 23:34:50 -06:00
argtype(on, t->down); // any-1
argtype(on, t->type); // any-2
argtype(on, t->down); // any-3
argtype(on, t->type); // any-4
r = nod(OCALL, on, N);
r->list = args;
typecheck(&r, Etop);
walkexpr(&r, Etop, init);
2008-06-16 23:34:50 -06:00
break;
case OAS2:
cl = count(n->list);
cr = count(n->rlist);
if(cl == 1 && cr == 2)
goto assign2;
if(cl == 2 && cr == 1)
goto access2;
goto shape;
assign2:
// mapassign2(hmap map[any]any, key any, val any, pres bool);
l = n->list->n;
if(l->op != OINDEX)
goto shape;
2008-06-16 23:34:50 -06:00
t = fixmap(l->left->type);
2008-06-16 23:34:50 -06:00
if(t == T)
break;
args = list1(l->left); // map
args = list(args, l->right); // key
args = list(args, n->rlist->n); // val
args = list(args, n->rlist->next->n); // pres
2008-06-16 23:34:50 -06:00
on = syslook("mapassign2", 1);
2008-06-16 23:34:50 -06:00
argtype(on, t->down); // any-1
argtype(on, t->type); // any-2
argtype(on, t->down); // any-3
argtype(on, t->type); // any-4
r = nod(OCALL, on, N);
r->list = args;
typecheck(&r, Etop);
walkexpr(&r, Etop, init);
2008-06-16 23:34:50 -06:00
break;
access2:
// mapaccess2(hmap map[any-1]any-2, key any-3) (val-4 any, pres bool);
2008-06-16 23:34:50 -06:00
//dump("access2", n);
r = n->rlist->n;
if(r->op != OINDEX)
goto shape;
2008-06-16 23:34:50 -06:00
t = fixmap(r->left->type);
2008-06-16 23:34:50 -06:00
if(t == T)
break;
args = list1(r->left); // map
args = list(args, r->right); // key
2008-06-16 23:34:50 -06:00
on = syslook("mapaccess2", 1);
2008-06-16 23:34:50 -06:00
argtype(on, t->down); // any-1
argtype(on, t->type); // any-2
argtype(on, t->down); // any-3
argtype(on, t->type); // any-4
a = nod(OCALL, on, N);
a->list = args;
typecheck(&a, Erv);
n->rlist = list1(a);
typecheck(&n, Etop);
walkexpr(&n, Etop, init);
r = n;
2008-06-16 23:34:50 -06:00
break;
case OASOP:
// rewrite map[index] op= right
// into tmpi := index; map[tmpi] = map[tmpi] op right
// TODO(rsc): does this double-evaluate map?
t = n->left->left->type;
a = nod(OXXX, N, N);
tempname(a, t->down); // tmpi
r = nod(OAS, a, n->left->right); // tmpi := index
n->left->right = a; // m[tmpi]
typecheck(&r, Etop);
walkexpr(&r, Etop, init);
*init = list(*init, r);
a = nod(OXXX, N, N);
*a = *n->left; // copy of map[tmpi]
a = nod(n->etype, a, n->right); // m[tmpi] op right
r = nod(OAS, n->left, a); // map[tmpi] = map[tmpi] op right
typecheck(&r, Etop);
walkexpr(&r, Etop, init);
2008-10-04 18:11:01 -06:00
break;
2008-06-16 23:34:50 -06:00
}
return r;
2008-06-16 23:34:50 -06:00
shape:
dump("shape", n);
fatal("mapop: cl=%d cr=%d, %O", top, n->op);
return N;
2008-06-16 23:34:50 -06:00
nottop:
2008-11-14 17:24:27 -07:00
yyerror("didn't expect %O here", n->op);
2008-06-16 23:34:50 -06:00
return N;
}
Node*
chanop(Node *n, int top, NodeList **init)
{
Node *r, *a, *on;
NodeList *args;
Type *t;
int cl, cr;
r = n;
switch(n->op) {
default:
2008-07-13 15:29:46 -06:00
fatal("chanop: unknown op %O", n->op);
case OCLOSE:
// closechan(hchan *chan any);
t = fixchan(n->left->type);
if(t == T)
break;
a = n->left; // chan
args = list1(a);
on = syslook("closechan", 1);
argtype(on, t); // any-1
r = nod(OCALL, on, N);
r->list = args;
typecheck(&r, Etop);
walkexpr(&r, top, nil);
r->type = n->type;
break;
case OCLOSED:
// closedchan(hchan *chan any) bool;
t = fixchan(n->left->type);
if(t == T)
break;
a = n->left; // chan
args = list1(a);
on = syslook("closedchan", 1);
argtype(on, t); // any-1
r = nod(OCALL, on, N);
r->list = args;
typecheck(&r, Erv);
walkexpr(&r, top, nil);
n->type = r->type;
break;
case OMAKE:
cl = count(n->list);
2009-03-11 18:37:04 -06:00
if(cl > 1)
yyerror("too many arguments to make chan");
// newchan(elemsize int, elemalg int,
// hint int) (hmap *chan[any-1]);
t = fixchan(n->type);
if(t == T)
break;
a = nodintconst(t->type->width); // elem width
args = list1(a);
a = nodintconst(algtype(t->type)); // elem algorithm
args = list(args, a);
2009-03-11 18:37:04 -06:00
a = nodintconst(0);
if(cl == 1) {
// async buf size
a = nod(OCONV, n->list->n, N);
2008-10-29 14:18:21 -06:00
a->type = types[TINT];
2009-03-11 18:37:04 -06:00
}
args = list(args, a);
on = syslook("newchan", 1);
argtype(on, t->type); // any-1
r = nod(OCALL, on, N);
r->list = args;
typecheck(&r, Erv);
walkexpr(&r, top, nil);
r->type = n->type;
break;
2008-07-13 15:29:46 -06:00
case OAS2:
cl = count(n->list);
cr = count(n->rlist);
2008-07-13 15:29:46 -06:00
if(cl != 2 || cr != 1 || n->rlist->n->op != ORECV)
2008-07-13 15:29:46 -06:00
goto shape;
2008-07-15 22:07:59 -06:00
// chanrecv2(hchan *chan any) (elem any, pres bool);
r = n->rlist->n;
defaultlit(&r->left, T);
t = fixchan(r->left->type);
2008-07-13 15:29:46 -06:00
if(t == T)
break;
if(!(t->chan & Crecv)) {
yyerror("cannot receive from %T", t);
break;
}
a = r->left; // chan
args = list1(a);
2008-07-13 15:29:46 -06:00
2008-07-15 22:07:59 -06:00
on = syslook("chanrecv2", 1);
2008-07-13 15:29:46 -06:00
argtype(on, t->type); // any-1
argtype(on, t->type); // any-2
r = nod(OCALL, on, N);
r->list = args;
typecheck(&r, Etop);
n->rlist->n = r;
2008-07-15 22:07:59 -06:00
r = n;
walkexpr(&r, Etop, init);
2008-07-13 15:29:46 -06:00
break;
2008-07-13 17:20:27 -06:00
case ORECV:
// should not happen - nonblocking is OAS w/ ORECV now.
if(n->right != N) {
dump("recv2", n);
fatal("chanop recv2");
}
2008-07-15 22:07:59 -06:00
2008-07-13 17:20:27 -06:00
// chanrecv1(hchan *chan any) (elem any);
defaultlit(&n->left, T);
2008-07-13 17:20:27 -06:00
t = fixchan(n->left->type);
if(t == T)
break;
if(!(t->chan & Crecv)) {
yyerror("cannot receive from %T", t);
break;
}
2008-07-13 17:20:27 -06:00
a = n->left; // chan
args = list1(a);
2008-07-13 17:20:27 -06:00
on = syslook("chanrecv1", 1);
argtype(on, t->type); // any-1
argtype(on, t->type); // any-2
r = nod(OCALL, on, N);
r->list = args;
typecheck(&r, Erv);
walkexpr(&r, Erv, nil);
2008-07-13 17:20:27 -06:00
break;
2008-07-15 22:07:59 -06:00
case OSEND:
t = fixchan(n->left->type);
if(t == T)
break;
if(!(t->chan & Csend)) {
yyerror("cannot send to %T", t);
break;
}
2008-07-15 22:07:59 -06:00
if(top != Etop)
goto send2;
// chansend1(hchan *chan any, elem any);
a = n->left; // chan
args = list1(a);
a = n->right; // e
args = list(args, a);
2008-07-15 22:07:59 -06:00
on = syslook("chansend1", 1);
argtype(on, t->type); // any-1
argtype(on, t->type); // any-2
r = nod(OCALL, on, N);
r->list = args;
typecheck(&r, Etop);
walkexpr(&r, Etop, nil);
2008-07-15 22:07:59 -06:00
break;
send2:
// chansend2(hchan *chan any, val any) (pres bool);
a = n->left; // chan
args = list1(a);
a = n->right; // e
args = list(args, a);
2008-07-15 22:07:59 -06:00
on = syslook("chansend2", 1);
argtype(on, t->type); // any-1
argtype(on, t->type); // any-2
r = nod(OCALL, on, N);
r->list = args;
typecheck(&r, Etop);
walkexpr(&r, Etop, nil);
2008-07-15 22:07:59 -06:00
break;
}
return r;
2008-07-13 15:29:46 -06:00
shape:
fatal("chanop: %O", n->op);
return N;
}
2008-08-27 18:28:30 -06:00
Type*
2008-12-18 21:06:28 -07:00
fixarray(Type *t)
2008-08-27 18:28:30 -06:00
{
if(t == T)
goto bad;
if(t->etype != TARRAY)
goto bad;
if(t->type == T)
goto bad;
2008-12-18 21:06:28 -07:00
dowidth(t);
2008-08-27 18:28:30 -06:00
return t;
bad:
2008-12-18 21:06:28 -07:00
yyerror("not an array: %lT", t);
return T;
2008-08-27 18:28:30 -06:00
}
Node*
arrayop(Node *n, int top)
{
Node *r, *a;
NodeList *args;
2008-12-18 21:06:28 -07:00
Type *t, *tl;
2008-08-27 18:28:30 -06:00
Node *on;
2009-03-11 18:37:04 -06:00
int cl;
2008-08-27 18:28:30 -06:00
r = n;
switch(n->op) {
default:
fatal("darrayop: unknown op %O", n->op);
case OCONV:
// arrays2d(old *any, nel int) (ary []any)
if(n->left->type == T || !isptr[n->left->type->etype])
break;
t = fixarray(n->left->type->type);
tl = fixarray(n->type);
if(t == T || tl == T)
break;
args = list1(n->left); // old
a = nodintconst(t->bound); // nel
a = nod(OCONV, a, N);
a->type = types[TINT];
args = list(args, a);
on = syslook("arrays2d", 1);
argtype(on, t); // any-1
argtype(on, tl->type); // any-2
r = nod(OCALL, on, N);
r->list = args;
2009-01-07 16:54:08 -07:00
n->left = r;
typecheck(&n, Erv);
walkexpr(&n, top, nil);
2009-01-07 16:54:08 -07:00
return n;
2008-12-18 21:06:28 -07:00
case OAS:
r = nod(OCONV, n->right, N);
r->type = n->left->type;
n->right = arrayop(r, Erv);
2008-12-18 21:06:28 -07:00
return n;
case OMAKE:
cl = count(n->list);
2009-03-11 18:37:04 -06:00
if(cl > 2)
yyerror("too many arguments to make array");
2008-12-18 21:06:28 -07:00
// newarray(nel int, max int, width int) (ary []any)
2008-08-27 18:28:30 -06:00
t = fixarray(n->type);
if(t == T)
break;
2008-08-27 18:28:30 -06:00
// nel
a = n->list->n;
if(a == N) {
yyerror("new slice must have size");
a = nodintconst(1);
}
2008-08-27 18:28:30 -06:00
a = nod(OCONV, a, N);
a->type = types[TINT];
args = list1(a);
2008-08-27 18:28:30 -06:00
// max
if(cl < 2)
2008-08-27 18:28:30 -06:00
a = nodintconst(0);
else
a = n->list->next->n;
2008-08-27 18:28:30 -06:00
a = nod(OCONV, a, N);
a->type = types[TINT];
args = list(args, a);
2008-08-27 18:28:30 -06:00
// width
a = nodintconst(t->type->width); // width
2008-08-27 18:28:30 -06:00
a = nod(OCONV, a, N);
a->type = types[TINT];
args = list(args, a);
2008-08-27 18:28:30 -06:00
on = syslook("newarray", 1);
argtype(on, t->type); // any-1
r = nod(OCALL, on, N);
r->list = args;
typecheck(&r, Erv);
walkexpr(&r, top, nil);
r->type = t; // if t had a name, going through newarray lost it
2008-08-27 18:28:30 -06:00
break;
case OSLICE:
2008-12-18 21:06:28 -07:00
// arrayslices(old any, nel int, lb int, hb int, width int) (ary []any)
// arraysliced(old []any, lb int, hb int, width int) (ary []any)
2008-08-27 18:28:30 -06:00
t = fixarray(n->left->type);
if(t == T)
break;
2008-08-27 18:28:30 -06:00
2008-12-18 21:06:28 -07:00
if(t->bound >= 0) {
// static slice
a = nod(OADDR, n->left, N); // old
args = list1(a);
2008-12-18 21:06:28 -07:00
a = nodintconst(t->bound); // nel
a = nod(OCONV, a, N);
a->type = types[TINT];
args = list(args, a);
2008-12-18 21:06:28 -07:00
on = syslook("arrayslices", 1);
argtype(on, t); // any-1
argtype(on, t->type); // any-2
} else {
// dynamic slice
a = n->left; // old
args = list1(a);
2008-12-18 21:06:28 -07:00
on = syslook("arraysliced", 1);
argtype(on, t->type); // any-1
argtype(on, t->type); // any-2
}
a = nod(OCONV, n->right->left, N); // lb
a->type = types[TINT];
args = list(args, a);
a = nod(OCONV, n->right->right, N); // hb
a->type = types[TINT];
args = list(args, a);
a = nodintconst(t->type->width); // width
a = nod(OCONV, a, N);
a->type = types[TINT];
args = list(args, a);
r = nod(OCALL, on, N);
r->list = args;
typecheck(&r, Erv);
2008-08-27 18:28:30 -06:00
break;
}
return r;
}
/*
* assigning src to dst involving interfaces?
* return op to use.
*/
int
ifaceas1(Type *dst, Type *src, int explicit)
{
if(src == T || dst == T)
return Inone;
if(explicit && !isinter(src))
yyerror("cannot use .(T) on non-interface type %T", src);
if(isinter(dst)) {
if(isinter(src)) {
if(isnilinter(dst)) {
if(isnilinter(src))
return E2Esame;
return I2E;
}
if(eqtype(dst, src))
2009-01-27 19:21:03 -07:00
return I2Isame;
ifacecheck(dst, src, lineno, explicit);
if(isnilinter(src))
return E2I;
if(explicit)
return I2Ix;
return I2I;
}
if(isnilinter(dst))
return T2E;
ifacecheck(dst, src, lineno, explicit);
return T2I;
}
if(isinter(src)) {
ifacecheck(dst, src, lineno, explicit);
if(isnilinter(src))
return E2T;
return I2T;
}
return Inone;
}
2009-01-27 19:21:03 -07:00
/*
* treat convert T to T as noop
*/
int
ifaceas(Type *dst, Type *src, int explicit)
2009-01-27 19:21:03 -07:00
{
int et;
et = ifaceas1(dst, src, explicit);
if(et == I2Isame || et == E2Esame)
2009-01-27 19:21:03 -07:00
et = Inone;
return et;
}
2008-11-05 15:27:07 -07:00
static char*
ifacename[] =
{
2009-01-27 19:21:03 -07:00
[I2T] = "ifaceI2T",
[I2T2] = "ifaceI2T2",
[I2I] = "ifaceI2I",
[I2Ix] = "ifaceI2Ix",
2009-01-27 19:21:03 -07:00
[I2I2] = "ifaceI2I2",
[I2Isame] = "ifaceI2Isame",
[E2T] = "ifaceE2T",
[E2T2] = "ifaceE2T2",
[E2I] = "ifaceE2I",
[E2I2] = "ifaceE2I2",
[I2E] = "ifaceI2E",
[I2E2] = "ifaceI2E2",
[T2I] = "ifaceT2I",
[T2E] = "ifaceT2E",
[E2Esame] = "ifaceE2Esame",
2008-11-05 15:27:07 -07:00
};
Node*
ifacecvt(Type *tl, Node *n, int et)
{
Type *tr;
Node *r, *on;
NodeList *args;
tr = n->type;
switch(et) {
default:
fatal("ifacecvt: unknown op %d\n", et);
case T2I:
2008-09-22 17:58:30 -06:00
// ifaceT2I(sigi *byte, sigt *byte, elem any) (ret any);
args = list1(typename(tl)); // sigi
args = list(args, typename(tr)); // sigt
args = list(args, n); // elem
on = syslook("ifaceT2I", 1);
argtype(on, tr);
2008-09-22 17:58:30 -06:00
argtype(on, tl);
break;
2008-11-05 15:27:07 -07:00
case I2T:
case I2T2:
case I2I:
case I2Ix:
2008-11-05 15:27:07 -07:00
case I2I2:
case E2T:
case E2T2:
case E2I:
case E2I2:
2008-11-05 15:27:07 -07:00
// iface[IT]2[IT][2](sigt *byte, iface any) (ret any[, ok bool]);
args = list1(typename(tl)); // sigi or sigt
args = list(args, n); // iface
2008-09-22 17:58:30 -06:00
on = syslook(ifacename[et], 1);
argtype(on, tr);
2008-09-22 17:58:30 -06:00
argtype(on, tl);
break;
2008-11-05 15:27:07 -07:00
case I2E:
// TODO(rsc): Should do this in back end, without a call.
// ifaceI2E(elem any) (ret any);
args = list1(n); // elem
on = syslook("ifaceI2E", 1);
argtype(on, tr);
argtype(on, tl);
break;
case T2E:
// TODO(rsc): Should do this in back end for pointer case, without a call.
// ifaceT2E(sigt *byte, elem any) (ret any);
args = list1(typename(tr)); // sigt
args = list(args, n); // elem
on = syslook("ifaceT2E", 1);
argtype(on, tr);
argtype(on, tl);
break;
}
r = nod(OCALL, on, N);
r->list = args;
typecheck(&r, Erv);
walkexpr(&r, Erv, nil);
return r;
}
Node*
ifaceop(Node *n)
{
Node *r, *on;
NodeList *args;
switch(n->op) {
default:
fatal("ifaceop %O", n->op);
case OEQ:
case ONE:
// ifaceeq(i1 any-1, i2 any-2) (ret bool);
args = list1(n->left); // i1
args = list(args, n->right); // i2
if(!eqtype(n->left->type, n->right->type))
fatal("ifaceop %O %T %T", n->op, n->left->type, n->right->type);
if(isnilinter(n->left->type))
on = syslook("efaceeq", 1);
else
on = syslook("ifaceeq", 1);
argtype(on, n->right->type);
argtype(on, n->left->type);
r = nod(OCALL, on, N);
r->list = args;
if(n->op == ONE)
r = nod(ONOT, r, N);
typecheck(&r, Erv);
walkexpr(&r, Erv, nil);
return r;
}
}
Node*
convas(Node *n, NodeList **init)
{
Node *l, *r;
Type *lt, *rt;
int et;
if(n->op != OAS)
fatal("convas: not OAS %O", n->op);
2008-11-01 17:36:46 -06:00
lt = T;
rt = T;
l = n->left;
r = n->right;
if(l == N || r == N)
goto out;
lt = l->type;
rt = r->type;
if(lt == T || rt == T)
goto out;
2008-06-16 23:34:50 -06:00
if(n->left->op == OINDEX)
if(istype(n->left->left->type, TMAP)) {
n = mapop(n, Elv, init);
goto out;
2008-06-16 23:34:50 -06:00
}
2008-07-13 15:29:46 -06:00
if(n->left->op == OSEND)
if(n->left->type != T) {
n = chanop(n, Elv, init);
goto out;
2008-07-13 15:29:46 -06:00
}
if(eqtype(lt, rt))
goto out;
et = ifaceas(lt, rt, 0);
if(et != Inone) {
n->right = ifacecvt(lt, r, et);
goto out;
}
if(isslice(lt) && isptr[rt->etype] && isfixedarray(rt->type)) {
if(!eqtype(lt->type->type, rt->type->type->type))
2008-08-27 18:28:30 -06:00
goto bad;
n = arrayop(n, Etop);
goto out;
2008-08-27 18:28:30 -06:00
}
if(ascompat(lt, rt))
goto out;
2008-08-27 18:28:30 -06:00
bad:
badtype(n->op, lt, rt);
out:
ullmancalc(n);
return n;
}
int
colasname(Node *n)
{
// TODO(rsc): can probably simplify
// once late-binding of names goes in
switch(n->op) {
case ONAME:
case ONONAME:
case OPACK:
break;
case OTYPE:
case OLITERAL:
if(n->sym != S)
break;
// fallthrough
default:
return 0;
}
return 1;
}
Node*
old2new(Node *n, Type *t, NodeList **init)
{
Node *l;
if(!colasname(n)) {
yyerror("left side of := must be a name");
return n;
}
if(t != T && t->funarg) {
yyerror("use of multi func value as single value in :=");
return n;
}
l = newname(n->sym);
dodclvar(l, t, init);
return l;
}
static Node*
mixedoldnew(Node *n, Type *t)
{
n = nod(OXXX, n, N);
n->type = t;
return n;
}
static NodeList*
checkmixed(NodeList *nl, NodeList **init)
{
Node *a, *l;
NodeList *ll, *n;
Type *t;
int ntot, nred;
// first pass, check if it is a special
// case of new and old declarations
ntot = 0; // number assignments
nred = 0; // number redeclarations
for(ll=nl; ll; ll=ll->next) {
l = ll->n;
t = l->type;
l = l->left;
if(!colasname(l))
goto allnew;
if(l->sym->block == block) {
if(!eqtype(l->type, t))
goto allnew;
nred++;
}
ntot++;
}
// test for special case
// a) multi-assignment (ntot>1)
// b) at least one redeclaration (red>0)
// c) not all redeclarations (nred!=ntot)
if(nred == 0 || ntot <= 1 || nred == ntot)
goto allnew;
n = nil;
for(ll=nl; ll; ll=ll->next) {
l = ll->n;
t = l->type;
l = l->left;
a = l;
if(l->sym->block != block)
a = old2new(l, t, init);
n = list(n, a);
}
return n;
allnew:
// same as original
n = nil;
for(ll=nl; ll; ll=ll->next) {
l = ll->n;
t = l->type;
l = l->left;
a = old2new(l, t, init);
n = list(n, a);
}
return n;
}
Node*
colas(NodeList *ll, NodeList *lr)
{
Node *l, *r, *a, *nl, *nr;
Iter savet;
NodeList *init, *savel, *saver, *n;
Type *t;
int cl, cr;
/* nl is an expression list.
* nr is an expression list.
* return a newname-list from
2008-06-22 22:02:06 -06:00
* types derived from the rhs.
*/
cr = count(lr);
cl = count(ll);
init = nil;
n = nil;
/* check calls early, to give better message for a := f() */
if(cr == 1) {
nr = lr->n;
switch(nr->op) {
case OCALL:
case OCALLFUNC:
if(nr->left->op == ONAME && nr->left->etype != 0)
break;
typecheck(&nr->left, Erv | Etype | Ecall);
walkexpr(&nr->left, Erv | Etype | Ecall, &init);
if(nr->left->op == OTYPE)
break;
goto call;
case OCALLMETH:
case OCALLINTER:
typecheck(&nr->left, Erv);
walkexpr(&nr->left, Erv, &init);
call:
convlit(&nr->left, types[TFUNC]);
t = nr->left->type;
if(t == T)
goto outl; // error already printed
if(t->etype == tptr)
t = t->type;
if(t == T || t->etype != TFUNC) {
yyerror("cannot call %T", t);
goto outl;
}
if(t->outtuple != cl) {
cr = t->outtuple;
goto badt;
}
// finish call - first half above
t = structfirst(&savet, getoutarg(t));
if(t == T)
goto outl;
for(savel=ll; savel; savel=savel->next) {
l = savel->n;
a = mixedoldnew(l, t->type);
n = list(n, a);
t = structnext(&savet);
}
n = checkmixed(n, &init);
goto out;
}
}
if(cl != cr) {
if(cr == 1) {
nr = lr->n;
goto multi;
}
goto badt;
}
for(savel=ll, saver=lr; savel != nil; savel=savel->next, saver=saver->next) {
l = savel->n;
r = saver->n;
typecheck(&r, Erv);
defaultlit(&r, T);
saver->n = r;
a = mixedoldnew(l, r->type);
2008-11-01 17:36:46 -06:00
n = list(n, a);
2008-06-22 22:02:06 -06:00
}
n = checkmixed(n, &init);
goto out;
multi:
typecheck(&nr, Erv);
lr->n = nr;
/*
* there is a list on the left
* and a mono on the right.
* go into the right to get
* individual types for the left.
*/
switch(nr->op) {
default:
goto badt;
case OINDEX:
// check if rhs is a map index.
// if so, types are valuetype,bool
if(cl != 2)
goto badt;
walkexpr(&nr->left, Erv, &init);
t = nr->left->type;
if(!istype(t, TMAP))
goto badt;
a = mixedoldnew(ll->n, t->type);
n = list1(a);
a = mixedoldnew(ll->next->n, types[TBOOL]);
n = list(n, a);
n = checkmixed(n, &init);
break;
2008-07-13 17:20:27 -06:00
case ODOTTYPE:
// a,b := i.(T)
walkdottype(nr, &init);
if(cl != 2)
goto badt;
// a,b = iface
a = mixedoldnew(ll->n, nr->type);
n = list1(a);
a = mixedoldnew(ll->next->n, types[TBOOL]);
n = list(n, a);
n = checkmixed(n, &init);
break;
2008-07-13 17:20:27 -06:00
case ORECV:
if(cl != 2)
goto badt;
walkexpr(&nr->left, Erv, &init);
2008-07-13 17:20:27 -06:00
t = nr->left->type;
if(!istype(t, TCHAN))
2008-07-13 17:20:27 -06:00
goto badt;
a = mixedoldnew(ll->n, t->type);
n = list1(a);
a = mixedoldnew(ll->next->n, types[TBOOL]);
n = list(n, a);
n = checkmixed(n, &init);
break;
}
goto out;
badt:
nl = ll->n;
if(nl->diag == 0) {
nl->diag = 1;
yyerror("assignment count mismatch: %d = %d %#N", cl, cr, lr->n);
}
outl:
n = ll;
out:
// n is the lhs of the assignment.
// init holds the list of declarations.
a = nod(OAS2, N, N);
a->list = n;
a->rlist = lr;
a->ninit = init;
a->colas = 1;
return a;
}
/*
* rewrite a range statement
* k and v are names/new_names
* m is an array or map
* local is 0 (meaning =) or 1 (meaning :=)
*/
2008-12-05 19:24:05 -07:00
Node*
dorange(Node *nn)
2008-12-05 19:24:05 -07:00
{
Node *k, *v, *m;
Node *n, *hv, *hc, *ha, *hk, *ohk, *on, *r, *a, *as;
NodeList *init, *args;
2008-12-05 19:24:05 -07:00
Type *t, *th;
int local;
NodeList *nl;
2008-12-05 19:24:05 -07:00
if(nn->op != ORANGE)
fatal("dorange not ORANGE");
nl = nn->list;
k = nl->n;
if((nl = nl->next) != nil) {
v = nl->n;
nl = nl->next;
} else
v = N;
if(nl != nil)
yyerror("too many variables in range");
2008-12-05 19:24:05 -07:00
n = nod(OFOR, N, N);
init = nil;
2008-12-05 19:24:05 -07:00
typecheck(&nn->right, Erv);
m = nn->right;
local = nn->etype;
2008-12-05 19:24:05 -07:00
t = m->type;
if(t == T)
goto out;
if(t->etype == TARRAY)
goto ary;
if(t->etype == TMAP)
goto map;
if(t->etype == TCHAN)
goto chan;
if(t->etype == TSTRING)
goto strng;
2008-12-05 19:24:05 -07:00
yyerror("range must be over map/array/chan/string");
2008-12-05 19:24:05 -07:00
goto out;
ary:
hk = nod(OXXX, N, N); // hidden key
tempname(hk, types[TINT]);
ha = nod(OXXX, N, N); // hidden array
tempname(ha, t);
2008-12-05 19:24:05 -07:00
2009-05-26 20:48:39 -06:00
a = nod(OAS, hk, nodintconst(0));
init = list(init, a);
a = nod(OAS, ha, m);
init = list(init, a);
n->ntest = nod(OLT, hk, nod(OLEN, ha, N));
n->nincr = nod(OASOP, hk, nodintconst(1));
2008-12-05 19:24:05 -07:00
n->nincr->etype = OADD;
if(local)
k = old2new(k, hk->type, &init);
n->nbody = list1(nod(OAS, k, hk));
2008-12-05 19:24:05 -07:00
if(v != N) {
if(local)
v = old2new(v, t->type, &init);
2008-12-05 19:24:05 -07:00
n->nbody = list(n->nbody,
nod(OAS, v, nod(OINDEX, ha, hk)) );
2008-12-05 19:24:05 -07:00
}
goto out;
map:
th = typ(TARRAY);
th->type = ptrto(types[TUINT8]);
th->bound = (sizeof(struct Hiter) + types[tptr]->width - 1) /
types[tptr]->width;
hk = nod(OXXX, N, N); // hidden iterator
tempname(hk, th); // hashmap hash_iter
on = syslook("mapiterinit", 1);
argtype(on, t->down);
argtype(on, t->type);
argtype(on, th);
a = nod(OADDR, hk, N);
r = nod(OCALL, on, N);
r->list = list(list1(m), a);
2009-05-26 20:48:39 -06:00
init = list(init, r);
2008-12-05 19:24:05 -07:00
r = nod(OINDEX, hk, nodintconst(0));
2008-12-05 19:24:05 -07:00
a = nod(OLITERAL, N, N);
a->val.ctype = CTNIL;
a->type = types[TNIL];
2008-12-05 19:24:05 -07:00
r = nod(ONE, r, a);
n->ntest = r;
on = syslook("mapiternext", 1);
argtype(on, th);
r = nod(OADDR, hk, N);
args = list1(r);
r = nod(OCALL, on, N);
r->list = args;
2008-12-05 19:24:05 -07:00
n->nincr = r;
if(local)
k = old2new(k, t->down, &init);
2008-12-05 19:24:05 -07:00
if(v == N) {
on = syslook("mapiter1", 1);
argtype(on, th);
argtype(on, t->down);
r = nod(OADDR, hk, N);
args = list1(r);
r = nod(OCALL, on, N);
r->list = args;
n->nbody = list1(nod(OAS, k, r));
2008-12-05 19:24:05 -07:00
goto out;
}
if(local)
v = old2new(v, t->type, &init);
2008-12-05 19:24:05 -07:00
on = syslook("mapiter2", 1);
argtype(on, th);
argtype(on, t->down);
argtype(on, t->type);
r = nod(OADDR, hk, N);
args = list1(r);
r = nod(OCALL, on, N);
r->list = args;
as = nod(OAS2, N, N);
as->list = list(list1(k), v);
as->rlist = list1(r);
n->nbody = list1(as);
goto out;
chan:
if(v != N)
yyerror("chan range can only have one variable");
hc = nod(OXXX, N, N); // hidden chan
tempname(hc, t);
hv = nod(OXXX, N, N); // hidden value
tempname(hv, t->type);
2008-12-05 19:24:05 -07:00
2009-05-26 20:48:39 -06:00
a = nod(OAS, hc, m);
init = list(init, a);
a = nod(ORECV, hc, N);
a = nod(OAS, hv, a);
init = list(init, a);
a = nod(OCLOSED, N, N);
a->list = list1(hc);
n->ntest = nod(ONOT, a, N);
n->nincr = nod(OAS, hv, nod(ORECV, hc, N));
if(local)
k = old2new(k, hv->type, &init);
n->nbody = list1(nod(OAS, k, hv));
2008-12-05 19:24:05 -07:00
goto out;
strng:
hk = nod(OXXX, N, N); // hidden key
tempname(hk, types[TINT]);
ohk = nod(OXXX, N, N); // old hidden key
tempname(ohk, types[TINT]);
ha = nod(OXXX, N, N); // hidden string
tempname(ha, types[TSTRING]);
hv = N;
if(v != N) {
hv = nod(OXXX, N, N); // hidden value
tempname(hv, types[TINT]);
}
if(local) {
k = old2new(k, types[TINT], &init);
if(v != N)
v = old2new(v, types[TINT], &init);
}
// ha = s
a = nod(OCONV, m, N);
a->type = ha->type;
a = nod(OAS, ha, a);
2009-05-26 20:48:39 -06:00
init = list(init, a);
// ohk = 0
a = nod(OAS, ohk, nodintconst(0));
2009-05-26 20:48:39 -06:00
init = list(init, a);
// hk[,hv] = stringiter(ha,hk)
if(v != N) {
// hk,v = stringiter2(ha, hk)
on = syslook("stringiter2", 0);
a = nod(OCALL, on, N);
a->list = list(list1(ha), nodintconst(0));
as = nod(OAS2, N, N);
as->list = list(list1(hk), hv);
as->rlist = list1(a);
a = as;
} else {
// hk = stringiter(ha, hk)
on = syslook("stringiter", 0);
a = nod(OCALL, on, N);
a->list = list(list1(ha), nodintconst(0));
a = nod(OAS, hk, a);
}
2009-05-26 20:48:39 -06:00
init = list(init, a);
// while(hk != 0)
n->ntest = nod(ONE, hk, nodintconst(0));
// hk[,hv] = stringiter(ha,hk)
if(v != N) {
// hk,hv = stringiter2(ha, hk)
on = syslook("stringiter2", 0);
a = nod(OCALL, on, N);
a->list = list(list1(ha), hk);
as = nod(OAS2, N, N);
as->list = list(list1(hk), hv);
as->rlist = list1(a);
a = as;
} else {
// hk = stringiter(ha, hk)
on = syslook("stringiter", 0);
a = nod(OCALL, on, N);
a->list = list(list1(ha), hk);
a = nod(OAS, hk, a);
}
n->nincr = a;
// k,ohk[,v] = ohk,hk,[,hv]
a = nod(OAS, k, ohk);
n->nbody = list1(a);
a = nod(OAS, ohk, hk);
n->nbody = list(n->nbody, a);
if(v != N) {
a = nod(OAS, v, hv);
n->nbody = list(n->nbody, a);
}
2008-12-05 19:24:05 -07:00
out:
n->ninit = concat(n->ninit, init);
2008-12-05 19:24:05 -07:00
return n;
}
/*
* from ascompat[te]
* evaluating actual function arguments.
* f(a,b)
* if there is exactly one function expr,
* then it is done first. otherwise must
* make temp variables
*/
NodeList*
reorder1(NodeList *all)
2008-06-10 22:29:57 -06:00
{
Node *f, *a, *n;
NodeList *l, *r, *g;
2008-06-10 22:29:57 -06:00
int c, t;
c = 0; // function calls
t = 0; // total parameters
for(l=all; l; l=l->next) {
n = l->n;
t++;
ullmancalc(n);
if(n->ullman >= UINF)
c++;
2008-06-10 22:29:57 -06:00
}
if(c == 0 || t == 1)
return all;
2008-06-10 22:29:57 -06:00
g = nil; // fncalls assigned to tempnames
2008-06-11 22:06:26 -06:00
f = N; // one fncall assigned to stack
r = nil; // non fncalls and tempnames assigned to stack
2008-06-11 22:06:26 -06:00
for(l=all; l; l=l->next) {
n = l->n;
ullmancalc(n);
if(n->ullman < UINF) {
r = list(r, n);
continue;
}
if(f == N) {
f = n;
continue;
}
2008-06-11 22:06:26 -06:00
// make assignment of fncall to tempname
a = nod(OXXX, N, N);
tempname(a, n->right->type);
a = nod(OAS, a, n->right);
g = list(g, a);
// put normal arg assignment on list
// with fncall replaced by tempname
n->right = a->left;
r = list(r, n);
}
2008-06-10 22:29:57 -06:00
if(f != N)
g = list(g, f);
return concat(g, r);
2008-06-10 22:29:57 -06:00
}
/*
* from ascompat[ee]
* a,b = c,d
2008-06-25 12:35:06 -06:00
* simultaneous assignment. there cannot
* be later use of an earlier lvalue.
*/
int
2008-06-25 12:35:06 -06:00
vmatch2(Node *l, Node *r)
{
NodeList *ll;
2008-06-25 12:35:06 -06:00
/*
* isolate all right sides
*/
if(r == N)
return 0;
switch(r->op) {
case ONAME:
// match each right given left
if(l == r)
return 1;
case OLITERAL:
return 0;
}
if(vmatch2(l, r->left))
return 1;
2008-06-25 12:35:06 -06:00
if(vmatch2(l, r->right))
return 1;
for(ll=r->list; ll; ll=ll->next)
if(vmatch2(l, ll->n))
return 1;
return 0;
2008-06-25 12:35:06 -06:00
}
int
vmatch1(Node *l, Node *r)
{
NodeList *ll;
2008-06-25 12:35:06 -06:00
/*
* isolate all left sides
*/
if(l == N)
return 0;
switch(l->op) {
case ONAME:
// match each left with all rights
return vmatch2(l, r);
case OLITERAL:
return 0;
}
if(vmatch1(l->left, r))
return 1;
2008-06-25 12:35:06 -06:00
if(vmatch1(l->right, r))
return 1;
for(ll=l->list; ll; ll=ll->next)
if(vmatch1(ll->n, r))
return 1;
return 0;
}
NodeList*
reorder3(NodeList *all)
2008-06-10 22:29:57 -06:00
{
Node *n1, *n2, *q;
int c1, c2;
NodeList *l1, *l2, *r;
r = nil;
for(l1=all, c1=0; l1; l1=l1->next, c1++) {
n1 = l1->n;
for(l2=all, c2=0; l2; l2=l2->next, c2++) {
n2 = l2->n;
if(c2 > c1) {
if(vmatch1(n1->left, n2->right)) {
2008-06-25 12:35:06 -06:00
q = nod(OXXX, N, N);
tempname(q, n1->right->type);
q = nod(OAS, n1->left, q);
n1->left = q->right;
r = list(r, q);
2008-06-25 12:35:06 -06:00
break;
}
}
}
2008-06-25 12:35:06 -06:00
}
return concat(all, r);
2008-06-10 22:29:57 -06:00
}
NodeList*
reorder4(NodeList *ll)
2008-06-10 22:29:57 -06:00
{
/*
* from ascompat[te]
* return c,d
* return expression assigned to output
* parameters. there may be no problems.
*
* TODO(rsc): i don't believe that.
* func f() (a, b int) {
* a, b = 1, 2;
* return b, a;
* }
2008-06-10 22:29:57 -06:00
*/
return ll;
2008-06-10 22:29:57 -06:00
}
static void
fielddup(Node *n, Node *hash[], ulong nhash)
{
uint h;
char *s;
Node *a;
if(n->op != ONAME)
fatal("fielddup: not ONAME");
s = n->sym->name;
h = stringhash(s)%nhash;
for(a=hash[h]; a!=N; a=a->ntest) {
if(strcmp(a->sym->name, s) == 0) {
yyerror("duplicate field name in struct literal: %s", s);
return;
}
}
n->ntest = hash[h];
hash[h] = n;
}
Node*
structlit(Node *n, Node *var, NodeList **init)
{
Iter savel;
Type *l, *t;
Node *r, *a;
Node* hash[101];
NodeList *nl;
int nerr;
nerr = nerrors;
t = n->type;
if(t->etype != TSTRUCT)
fatal("structlit: not struct");
if(var == N) {
var = nod(OXXX, N, N);
tempname(var, t);
}
nl = n->list;
if(nl == nil || nl->n->op == OKEY)
goto keyval;
l = structfirst(&savel, &n->type);
for(; nl; nl=nl->next) {
r = nl->n;
// assignment to every field
if(l == T)
break;
if(r->op == OKEY) {
yyerror("mixture of value and field:value initializers");
return var;
}
// build list of var.field = expr
a = nod(ODOT, var, newname(l->sym));
a = nod(OAS, a, r);
typecheck(&a, Etop);
walkexpr(&a, Etop, init);
if(nerr != nerrors)
return var;
*init = list(*init, a);
l = structnext(&savel);
}
if(l != T)
yyerror("struct literal expect expr of type %T", l);
if(nl != nil)
yyerror("struct literal too many expressions");
return var;
keyval:
memset(hash, 0, sizeof(hash));
a = nod(OAS, var, N);
typecheck(&a, Etop);
walkexpr(&a, Etop, init);
*init = list(*init, a);
for(; nl; nl=nl->next) {
r = nl->n;
// assignment to field:value elements
if(r->op != OKEY) {
yyerror("mixture of field:value and value initializers");
break;
}
// build list of var.field = expr
a = nod(ODOT, var, newname(r->left->sym));
fielddup(a->right, hash, nelem(hash));
if(nerr != nerrors)
break;
a = nod(OAS, a, r->right);
typecheck(&a, Etop);
walkexpr(&a, Etop, init);
if(nerr != nerrors)
break;
*init = list(*init, a);
}
return var;
}
static void
indexdup(Node *n, Node *hash[], ulong nhash)
{
uint h;
Node *a;
ulong b, c;
if(n->op != OLITERAL)
fatal("indexdup: not OLITERAL");
b = mpgetfix(n->val.u.xval);
h = b%nhash;
for(a=hash[h]; a!=N; a=a->ntest) {
c = mpgetfix(a->val.u.xval);
if(b == c) {
yyerror("duplicate index in array literal: %ld", b);
return;
}
}
n->ntest = hash[h];
hash[h] = n;
}
2008-09-03 15:09:29 -06:00
Node*
arraylit(Node *n, Node *var, NodeList **init)
{
Type *t;
Node *r, *a;
NodeList *l;
long ninit, b;
Node* hash[101];
int nerr;
nerr = nerrors;
t = n->type;
if(t->etype != TARRAY)
fatal("arraylit: not array");
// find max index
2009-01-07 14:20:10 -07:00
ninit = 0;
b = 0;
for(l=n->list; l; l=l->next) {
r = l->n;
if(r->op == OKEY) {
evconst(r->left);
b = nonnegconst(r->left);
}
b++;
if(b > ninit)
ninit = b;
2009-01-07 14:20:10 -07:00
}
b = t->bound;
if(b == -100) {
// flag for [...]
b = ninit;
if(var == N)
t = shallow(t);
2009-01-07 14:20:10 -07:00
t->bound = b;
}
if(var == N) {
var = nod(OXXX, N, N);
tempname(var, t);
}
2009-01-07 14:20:10 -07:00
if(b < 0) {
// slice
a = nod(OMAKE, N, N);
a->list = list(list1(typenod(t)), nodintconst(ninit));
a = nod(OAS, var, a);
typecheck(&a, Etop);
walkexpr(&a, Etop, init);
*init = list(*init, a);
2009-01-07 14:20:10 -07:00
} else {
// if entire array isnt initialized,
// then clear the array
2009-01-07 14:20:10 -07:00
if(ninit < b) {
a = nod(OAS, var, N);
typecheck(&a, Etop);
walkexpr(&a, Etop, init);
*init = list(*init, a);
}
}
b = 0;
memset(hash, 0, sizeof(hash));
for(l=n->list; l; l=l->next) {
r = l->n;
// build list of var[c] = expr
if(r->op == OKEY) {
b = nonnegconst(r->left);
if(b < 0) {
yyerror("array index must be non-negative constant");
break;
}
r = r->right;
}
if(t->bound >= 0 && b > t->bound) {
yyerror("array index out of bounds");
break;
}
a = nodintconst(b);
indexdup(a, hash, nelem(hash));
if(nerr != nerrors)
break;
a = nod(OINDEX, var, a);
a = nod(OAS, a, r);
typecheck(&a, Etop);
walkexpr(&a, Etop, init); // add any assignments in r to top
if(nerr != nerrors)
break;
*init = list(*init, a);
b++;
}
return var;
}
static void
keydup(Node *n, Node *hash[], ulong nhash)
{
uint h;
ulong b;
double d;
int i;
Node *a;
Node cmp;
char *s;
evconst(n);
if(n->op != OLITERAL)
return; // we dont check variables
switch(n->val.ctype) {
default: // unknown, bool, nil
b = 23;
break;
case CTINT:
b = mpgetfix(n->val.u.xval);
break;
case CTFLT:
d = mpgetflt(n->val.u.fval);
s = (char*)&d;
b = 0;
for(i=sizeof(d); i>0; i--)
b = b*PRIME1 + *s++;
break;
case CTSTR:
b = 0;
s = n->val.u.sval->s;
for(i=n->val.u.sval->len; i>0; i--)
b = b*PRIME1 + *s++;
break;
}
h = b%nhash;
memset(&cmp, 0, sizeof(cmp));
for(a=hash[h]; a!=N; a=a->ntest) {
cmp.op = OEQ;
cmp.left = n;
cmp.right = a;
evconst(&cmp);
b = cmp.val.u.bval;
if(b) {
// too lazy to print the literal
yyerror("duplicate key in map literal");
return;
}
}
n->ntest = hash[h];
hash[h] = n;
}
2008-09-03 15:40:22 -06:00
Node*
maplit(Node *n, Node *var, NodeList **init)
2008-09-03 15:40:22 -06:00
{
Type *t;
Node *r, *a;
Node* hash[101];
NodeList *l;
int nerr;
2008-09-03 15:40:22 -06:00
nerr = nerrors;
2008-09-03 15:40:22 -06:00
t = n->type;
if(t->etype != TMAP)
fatal("maplit: not map");
2008-09-03 15:40:22 -06:00
if(var == N) {
var = nod(OXXX, N, N);
tempname(var, t);
}
2008-09-03 15:40:22 -06:00
a = nod(OMAKE, N, N);
a->list = list1(typenod(t));
2008-09-03 15:40:22 -06:00
a = nod(OAS, var, a);
typecheck(&a, Etop);
walkexpr(&a, Etop, init);
*init = list(*init, a);
2008-09-03 15:40:22 -06:00
memset(hash, 0, sizeof(hash));
for(l=n->list; l; l=l->next) {
r = l->n;
if(r->op != OKEY) {
yyerror("map literal must have key:value pairs");
break;
}
2008-09-03 15:40:22 -06:00
// build list of var[c] = expr
keydup(r->left, hash, nelem(hash));
if(nerr != nerrors)
break;
a = nod(OINDEX, var, r->left);
a = nod(OAS, a, r->right);
typecheck(&a, Etop);
walkexpr(&a, Etop, init);
if(nerr != nerrors)
break;
*init = list(*init, a);
}
return var;
2008-09-03 15:40:22 -06:00
}
/*
* the address of n has been taken and might be used after
* the current function returns. mark any local vars
* as needing to move to the heap.
*/
void
addrescapes(Node *n)
{
char buf[100];
switch(n->op) {
default:
// probably a type error already.
// dump("addrescapes", n);
break;
case ONAME:
if(n->noescape)
break;
switch(n->class) {
case PPARAMOUT:
yyerror("cannot take address of out parameter %s", n->sym->name);
break;
case PAUTO:
case PPARAM:
// if func param, need separate temporary
// to hold heap pointer.
if(n->class == PPARAM) {
// expression to refer to stack copy
n->stackparam = nod(OPARAM, n, N);
n->stackparam->type = n->type;
n->stackparam->addable = 1;
n->stackparam->xoffset = n->xoffset;
}
n->class |= PHEAP;
n->addable = 0;
n->ullman = 2;
n->alloc = callnew(n->type);
n->xoffset = 0;
// create stack variable to hold pointer to heap
n->heapaddr = nod(0, N, N);
tempname(n->heapaddr, ptrto(n->type));
snprint(buf, sizeof buf, "&%S", n->sym);
n->heapaddr->sym = lookup(buf);
break;
}
break;
case OIND:
case ODOTPTR:
break;
case ODOT:
case OINDEX:
// ODOTPTR has already been introduced,
// so these are the non-pointer ODOT and OINDEX.
// In &x[0], if x is a slice, then x does not
// escape--the pointer inside x does, but that
// is always a heap pointer anyway.
if(!isslice(n->left->type))
addrescapes(n->left);
break;
}
}
/*
* walk through argin parameters.
* generate and return code to allocate
* copies of escaped parameters to the heap.
*/
NodeList*
paramstoheap(Type **argin)
{
Type *t;
Iter savet;
Node *v;
NodeList *nn;
nn = nil;
for(t = structfirst(&savet, argin); t != T; t = structnext(&savet)) {
v = t->nname;
if(v == N || !(v->class & PHEAP))
continue;
// generate allocation & copying code
nn = list(nn, nod(OAS, v->heapaddr, v->alloc));
nn = list(nn, nod(OAS, v, v->stackparam));
}
return nn;
}
/*
* take care of migrating any function in/out args
* between the stack and the heap. adds code to
* curfn's before and after lists.
*/
void
heapmoves(void)
{
NodeList *nn;
nn = paramstoheap(getthis(curfn->type));
nn = concat(nn, paramstoheap(getinarg(curfn->type)));
curfn->enter = concat(curfn->enter, nn);
}