1
0
mirror of https://github.com/golang/go synced 2024-11-22 00:44:39 -07:00

introduce typecheck pass before walkexpr.

not complete but compiler still works.

R=ken
OCL=32424
CL=32426
This commit is contained in:
Russ Cox 2009-07-29 12:47:51 -07:00
parent 056940d84b
commit 9271c6402e
6 changed files with 421 additions and 136 deletions

View File

@ -257,8 +257,10 @@ typeinit(void)
* initialize okfor
*/
for(i=0; i<NTYPE; i++) {
if(isint[i]) {
if(isint[i] || i == TIDEAL) {
okforeq[i] = 1;
okforcmp[i] = 1;
okforarith[i] = 1;
okforadd[i] = 1;
okforand[i] = 1;
issimple[i] = 1;
@ -267,26 +269,76 @@ typeinit(void)
}
if(isfloat[i]) {
okforeq[i] = 1;
okforcmp[i] = 1;
okforadd[i] = 1;
okforarith[i] = 1;
issimple[i] = 1;
minfltval[i] = mal(sizeof(*minfltval[i]));
maxfltval[i] = mal(sizeof(*maxfltval[i]));
}
switch(i) {
case TBOOL:
issimple[i] = 1;
case TPTR32:
case TPTR64:
case TINTER:
case TMAP:
case TCHAN:
case TFUNC:
okforeq[i] = 1;
break;
}
}
issimple[TBOOL] = 1;
okforadd[TSTRING] = 1;
okforbool[TBOOL] = 1;
okforcap[TARRAY] = 1;
//okforcap[TCHAN] = 1;
//okforcap[TMAP] = 1;
okforlen[TARRAY] = 1;
//okforlen[TCHAN] = 1;
okforlen[TMAP] = 1;
okforlen[TSTRING] = 1;
okforeq[TPTR32] = 1;
okforeq[TPTR64] = 1;
okforeq[TINTER] = 1;
okforeq[TMAP] = 1;
okforeq[TCHAN] = 1;
okforeq[TFUNC] = 1;
okforeq[TSTRING] = 1;
okforeq[TBOOL] = 1;
okforeq[TARRAY] = 1; // refined in typecheck
okforcmp[TSTRING] = 1;
for(i=0; i<nelem(okfor); i++)
okfor[i] = okfornone;
// binary
okfor[OADD] = okforadd;
okfor[OAND] = okforand;
okfor[OANDAND] = okforbool;
okfor[OANDNOT] = okforand;
okfor[ODIV] = okforarith;
okfor[OEQ] = okforeq;
okfor[OGE] = okforcmp;
okfor[OGT] = okforcmp;
okfor[OLE] = okforcmp;
okfor[OLT] = okforcmp;
okfor[OMOD] = okforarith;
okfor[OMUL] = okforarith;
okfor[ONE] = okforeq;
okfor[OOR] = okforand;
okfor[OOROR] = okforbool;
okfor[OSUB] = okforarith;
okfor[OXOR] = okforand;
okfor[OLSH] = okforand;
okfor[ORSH] = okforand;
// unary
okfor[OCOM] = okforand;
okfor[OMINUS] = okforarith;
okfor[ONOT] = okforbool;
okfor[OPLUS] = okforadd;
// special
okfor[OCAP] = okforcap;
okfor[OLEN] = okforlen;
mpatofix(maxintval[TINT8], "0x7f");
mpatofix(minintval[TINT8], "-0x80");
mpatofix(maxintval[TINT16], "0x7fff");

View File

@ -587,6 +587,12 @@ unary:
}
return;
case TUP(OCONV, CTINT):
case TUP(OCONV, CTFLT):
case TUP(OCONV, CTSTR):
convlit1(&nl, n->type, 1);
break;
case TUP(OPLUS, CTINT):
break;
case TUP(OMINUS, CTINT):
@ -711,7 +717,7 @@ defaultlit(Node **np, Type *t)
lineno = n->lineno;
switch(n->val.ctype) {
default:
yyerror("defaultlit: unknown literal: %N", n);
yyerror("defaultlit: unknown literal: %#N", n);
break;
case CTINT:
n->type = types[TINT];

View File

@ -195,6 +195,7 @@ struct Node
uchar funcdepth;
uchar builtin; // built-in name, like len or close
uchar walkdef;
uchar typecheck;
// most nodes
Node* left;
@ -435,12 +436,11 @@ enum
enum
{
Exxx,
Eyyy,
Etop, // evaluated at statement level
Elv, // evaluated in lvalue context
Erv, // evaluated in rvalue context
Etype = 1<<8,
Etop = 1<<1, // evaluated at statement level
Elv = 1<<2, // evaluated in lvalue context
Erv = 1<<3, // evaluated in rvalue context
Etype = 1<<4,
Eideal = 1<<5,
};
#define BITS 5
@ -574,6 +574,7 @@ EXTERN char* filename; // name to uniqify names
EXTERN Idir* idirs;
EXTERN Type* types[NTYPE];
EXTERN Type* idealstring;
EXTERN uchar simtype[NTYPE];
EXTERN uchar isptr[NTYPE];
EXTERN uchar isforw[NTYPE];
@ -581,10 +582,17 @@ EXTERN uchar isint[NTYPE];
EXTERN uchar isfloat[NTYPE];
EXTERN uchar issigned[NTYPE];
EXTERN uchar issimple[NTYPE];
EXTERN uchar okforeq[NTYPE];
EXTERN uchar okforadd[NTYPE];
EXTERN uchar okforand[NTYPE];
EXTERN Type* idealstring;
EXTERN uchar okfornone[NTYPE];
EXTERN uchar okforcmp[NTYPE];
EXTERN uchar okforbool[NTYPE];
EXTERN uchar okforcap[NTYPE];
EXTERN uchar okforlen[NTYPE];
EXTERN uchar okforarith[NTYPE];
EXTERN uchar* okfor[OEND];
EXTERN Mpint* minintval[NTYPE];
EXTERN Mpint* maxintval[NTYPE];
@ -977,6 +985,8 @@ void addrescapes(Node*);
void heapmoves(void);
void walkdeflist(NodeList*);
void walkdef(Node*);
void typechecklist(NodeList*, int);
Node* typecheck(Node**, int);
/*
* const.c

View File

@ -1324,7 +1324,7 @@ Nconv(Fmt *fp)
fmtprint(fp, "<N>");
goto out;
}
if(fp->flags & FmtSharp) {
exprfmt(fp, n, 0);
goto out;
@ -2123,7 +2123,6 @@ out:
void
badtype(int o, Type *tl, Type *tr)
{
yyerror("illegal types for operand: %O", o);
if(tl != T)
print(" %T\n", tl);
@ -2346,7 +2345,6 @@ tempname(Node *n, Type *t)
n->op = ONAME;
n->sym = s;
n->type = t;
n->etype = t->etype;
n->class = PAUTO;
n->addable = 1;
n->ullman = 1;

View File

@ -137,6 +137,10 @@ walkdef(Node *n)
}
n->walkdef = 2;
if(n->type != T || n->sym == S) // builtin or no name
goto ret;
init = nil;
switch(n->op) {
case OLITERAL:
@ -151,8 +155,11 @@ walkdef(Node *n)
}
e = n->defn;
n->defn = N;
if(e == N)
dump("walkdef", n);
if(e == N) {
lineno = n->lineno;
dump("walkdef nil defn", n);
yyerror("xxx");
}
walkexpr(&e, Erv, &init);
if(e->op != OLITERAL) {
yyerror("const initializer must be constant");
@ -185,7 +192,7 @@ walkstmt(Node **np)
NodeList *ll;
int lno;
Node *n;
n = *np;
if(n == N)
return;
@ -288,7 +295,7 @@ walkstmt(Node **np)
n->op = OFALL;
break;
}
*np = n;
}
@ -313,6 +320,299 @@ implicitstar(Node **nn)
*nn = n;
}
void
typechecklist(NodeList *l, int top)
{
for(; l; l=l->next)
typecheck(&l->n, top);
}
/*
* type check the whole tree of an expression.
* calculates expression types.
* evaluates compile time constants.
* marks variables that escape the local frame.
* rewrites n->op to be more specific in some cases.
* replaces *np with a new pointer in some cases.
* returns the final value of *np as a convenience.
*/
Node*
typecheck(Node **np, int top)
{
int et, et1, et2;
Node *n, *l, *r;
int lno, ok;
Type *t;
n = *np;
if(n == N || n->typecheck == 1)
return n;
if(n->typecheck == 2)
fatal("typecheck loop");
n->typecheck = 2;
if(n->sym && n->walkdef != 1)
walkdef(n);
lno = setlineno(n);
ok = 0;
switch(n->op) {
default:
// until typecheck is complete, do nothing.
goto ret;
dump("typecheck", n);
fatal("typecheck %O", n->op);
/*
* names
*/
case OLITERAL:
ok |= Erv;
goto ret;
case ONONAME:
ok |= Elv | Erv;
goto ret;
case ONAME:
if(n->etype != 0) {
yyerror("must call builtin %S", n->sym);
goto error;
}
ok |= Erv;
if(n->class != PFUNC)
ok |= Elv;
goto ret;
/*
* types (OIND is with exprs)
*/
case OTYPE:
ok |= Etype;
if(n->type == T)
goto error;
break;
case OTARRAY:
ok |= Etype;
t = typ(TARRAY);
l = n->left;
r = n->right;
if(l == nil) {
t->bound = -1;
} else {
typecheck(&l, Erv | Etype);
walkexpr(&l, Erv | Etype, &n->ninit); // TODO: remove
switch(l->op) {
default:
yyerror("invalid array bound %O", l->op);
goto error;
case OLITERAL:
if(consttype(l) == CTINT) {
t->bound = mpgetfix(l->val.u.xval);
if(t->bound < 0) {
yyerror("array bound must be non-negative");
goto error;
}
}
break;
case OTYPE:
if(l->type == T)
goto error;
if(l->type->etype != TDDD) {
yyerror("invalid array bound %T", l->type);
goto error;
}
t->bound = -100;
break;
}
}
typecheck(&r, Etype);
if(r->type == T)
goto error;
t->type = r->type;
n->op = OTYPE;
n->type = t;
n->left = N;
n->right = N;
checkwidth(t);
break;
case OTMAP:
ok |= Etype;
l = typecheck(&n->left, Etype);
r = typecheck(&n->right, Etype);
if(l->type == T || r->type == T)
goto error;
n->op = OTYPE;
n->type = maptype(l->type, r->type);
n->left = N;
n->right = N;
break;
case OTCHAN:
ok |= Etype;
l = typecheck(&n->left, Etype);
if(l->type == T)
goto error;
t = typ(TCHAN);
t->type = l->type;
t->chan = n->etype;
n->op = OTYPE;
n->type = t;
n->left = N;
n->etype = 0;
break;
case OTSTRUCT:
ok |= Etype;
n->op = OTYPE;
n->type = dostruct(n->list, TSTRUCT);
if(n->type == T)
goto error;
n->list = nil;
break;
case OTINTER:
ok |= Etype;
n->op = OTYPE;
n->type = dostruct(n->list, TINTER);
if(n->type == T)
goto error;
n->type = sortinter(n->type);
break;
case OTFUNC:
ok |= Etype;
n->op = OTYPE;
n->type = functype(n->left, n->list, n->rlist);
if(n->type == T)
goto error;
break;
/*
* exprs
*/
case OADD:
case OAND:
case OANDAND:
case OANDNOT:
case ODIV:
case OEQ:
case OGE:
case OGT:
case OLE:
case OLT:
case OMOD:
case OMUL:
case ONE:
case OOR:
case OOROR:
case OSUB:
case OXOR:
ok |= Erv;
l = typecheck(&n->left, Erv | Eideal);
r = typecheck(&n->right, Erv | Eideal);
if(l->type == T || r->type == T)
goto error;
et1 = l->type->etype;
et2 = r->type->etype;
if(et1 == TIDEAL || et1 == TNIL || et2 == TIDEAL || et2 == TNIL)
if(et1 != TIDEAL && et1 != TNIL || et2 != TIDEAL && et2 != TNIL) {
// ideal mixed with non-ideal
defaultlit2(&l, &r);
n->left = l;
n->right = r;
}
t = l->type;
if(t->etype == TIDEAL)
t = r->type;
et = t->etype;
if(et == TIDEAL)
et = TINT;
if(t->etype != TIDEAL && !eqtype(l->type, r->type)) {
badbinary:
yyerror("invalid operation: %#N", n);
goto error;
}
if(!okfor[n->op][et])
goto badbinary;
// okfor allows any array == array;
// restrict to slice == nil and nil == slice.
if(l->type->etype == TARRAY && !isslice(l->type))
goto badbinary;
if(r->type->etype == TARRAY && !isslice(r->type))
goto badbinary;
if(isslice(l->type) && !isnil(l) && !isnil(r))
goto badbinary;
evconst(n);
goto ret;
case OCOM:
case OMINUS:
case ONOT:
case OPLUS:
ok |= Erv;
l = typecheck(&n->left, Erv | Eideal);
walkexpr(&n->left, Erv | Eideal, &n->ninit); // TODO: remove
if((t = l->type) == T)
goto error;
if(!okfor[n->op][t->etype]) {
yyerror("invalid operation: %#O %T", n->op, t);
goto error;
}
n->type = t;
goto ret;
/*
* type or expr
*/
case OIND:
typecheck(&n->left, top | Etype);
if(n->left->op == OTYPE) {
ok |= Etype;
n->op = OTYPE;
n->type = ptrto(n->left->type);
n->left = N;
goto ret;
}
// TODO: OIND expression type checking
goto ret;
}
ret:
evconst(n);
if(n->op == OTYPE && !(top & Etype)) {
yyerror("type %T is not an expression", n->type);
goto error;
}
if((top & (Elv|Erv|Etype)) == Etype && n->op != OTYPE) {
yyerror("%O is not a type", n->op);
goto error;
}
/* TODO
if(n->type == T)
fatal("typecheck nil type");
*/
goto out;
error:
n->type = T;
out:
lineno = lno;
n->typecheck = 1;
*np = n;
return n;
}
/*
* walk the whole tree of the body of an
* expression or simple statement.
@ -351,20 +651,13 @@ walkexpr(Node **np, int top, NodeList **init)
if(debug['w'] > 1 && top == Etop)
dump("walk-before", n);
if(n->typecheck != 1)
typecheck(&n, top | typeok);
reswitch:
t = T;
et = Txxx;
switch(n->op) {
case ONAME:
case OTYPE:
case OLITERAL:
case ONONAME:
if(n->sym != S && n->type == T)
walkdef(n);
break;
}
switch(n->op) {
default:
dump("walk", n);
@ -374,80 +667,6 @@ reswitch:
case OTYPE:
goto ret;
case OTARRAY:
t = typ(TARRAY);
l = n->left;
r = n->right;
if(l == nil) {
t->bound = -1;
} else {
walkexpr(&l, Erv | Etype, init);
switch(l->op) {
default:
yyerror("invalid array bound %O", l->op);
break;
case OLITERAL:
if(consttype(l) == CTINT) {
t->bound = mpgetfix(l->val.u.xval);
if(t->bound < 0) {
yyerror("array bound must be non-negative");
t->bound = 1;
}
}
break;
case OTYPE:
if(l->type == T)
break;
if(l->type->etype != TDDD)
yyerror("invalid array bound %T", l->type);
t->bound = -100;
break;
}
}
walkexpr(&r, Etype, init);
t->type = r->type;
n->op = OTYPE;
n->type = t;
checkwidth(t);
goto ret;
case OTMAP:
l = n->left;
r = n->right;
walkexpr(&l, Etype, init);
walkexpr(&r, Etype, init);
n->op = OTYPE;
n->type = maptype(l->type, r->type);
goto ret;
case OTCHAN:
t = typ(TCHAN);
l = n->left;
walkexpr(&l, Etype, init);
t->type = l->type;
t->chan = n->etype;
n->op = OTYPE;
n->type = t;
goto ret;
case OTSTRUCT:
n->op = OTYPE;
n->type = dostruct(n->list, TSTRUCT);
goto ret;
case OTINTER:
n->op = OTYPE;
n->type = dostruct(n->list, TINTER);
n->type = sortinter(n->type);
goto ret;
case OTFUNC:
n->op = OTYPE;
n->type = functype(n->left, n->list, n->rlist);
goto ret;
case OKEY:
walkexpr(&n->left, top | typeok, init);
walkexpr(&n->right, top | typeok, init);
@ -483,7 +702,7 @@ reswitch:
goto ret;
case OLITERAL:
if(top != Erv)
if(!(top & Erv))
goto nottop;
n->addable = 1;
goto ret;
@ -550,7 +769,7 @@ reswitch:
n->op = OCALLINTER;
if(n->left->op == OTYPE) {
n->op = OCONV;
if(top != Erv)
if(!(top & Erv))
goto nottop;
// turn CALL(type, arg) into CONV(arg) w/ type.
n->type = n->left->type;
@ -777,7 +996,7 @@ reswitch:
walkdottype(n, init);
// fall through
case OCONV:
if(top != Erv)
if(!(top & Erv))
goto nottop;
walkconv(&n, init);
goto ret;
@ -817,9 +1036,8 @@ reswitch:
goto ret;
case ONOT:
if(top != Erv)
if(!(top & Erv))
goto nottop;
evconst(n);
if(n->op == OLITERAL)
goto ret;
walkexpr(&n->left, Erv, init);
@ -841,7 +1059,7 @@ reswitch:
case OLSH:
case ORSH:
if(top != Erv)
if(!(top & Erv))
goto nottop;
walkexpr(&n->left, Erv, init);
@ -880,7 +1098,7 @@ reswitch:
case OSUB:
case OMUL:
case ODIV:
if(top != Erv)
if(!(top & Erv))
goto nottop;
walkexpr(&n->left, Erv, init);
@ -935,18 +1153,17 @@ reswitch:
case OMINUS:
case OPLUS:
case OCOM:
if(top != Erv)
if(!(top & Erv))
goto nottop;
walkexpr(&n->left, Erv, init);
if(n->left == N)
goto ret;
evconst(n);
if(n->op == OLITERAL)
goto ret;
break;
case OLEN:
if(top != Erv)
if(!(top & Erv))
goto nottop;
if(n->left == N) {
if(n->list == nil) {
@ -981,7 +1198,7 @@ reswitch:
goto ret;
case OCAP:
if(top != Erv)
if(!(top & Erv))
goto nottop;
if(n->left == N) {
if(n->list == nil) {
@ -1033,7 +1250,7 @@ reswitch:
case TSTRING:
// right side must be an int
if(top != Erv)
if(!(top & Erv))
goto nottop;
defaultlit(&n->right, types[TINT]);
if(n->right->type == T)
@ -1107,7 +1324,8 @@ reswitch:
goto nottop;
walkexpr(&n->left, top, init);
walkexpr(&n->right, Erv, 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);
@ -1139,7 +1357,7 @@ reswitch:
goto ret;
case OADDR:
if(top != Erv)
if(!(top & Erv))
goto nottop;
defaultlit(&n->left, T);
if(n->left->op == OCOMPOS) {
@ -1225,13 +1443,13 @@ reswitch:
goto ret;
case OMAKE:
if(top != Erv)
if(!(top & Erv))
goto nottop;
n = makecompat(n);
goto ret;
case ONEW:
if(top != Erv)
if(!(top & Erv))
goto nottop;
if(n->list == nil) {
yyerror("missing argument to new");
@ -1295,7 +1513,7 @@ reswitch:
if(n->left->type == T)
goto ret;
et = n->left->type->etype;
if(!okforadd[et] && et != TSTRING)
if(!okforarith[et] && et != TSTRING)
goto badt;
t = types[TBOOL];
break;
@ -1308,7 +1526,7 @@ reswitch:
if(n->left->type == T)
goto ret;
et = n->left->type->etype;
if(!okforadd[et])
if(!okforarith[et])
goto badt;
break;
@ -1316,7 +1534,7 @@ reswitch:
if(n->left->type == T)
goto ret;
et = n->left->type->etype;
if(!okforadd[et])
if(!okforarith[et])
goto badt;
if(isfloat[et]) {
// TODO(rsc): Can do this more efficiently,
@ -1394,7 +1612,7 @@ nottop:
if(n->diag)
goto ret;
n->diag = 1;
switch(top | typeok) {
switch((top | typeok) & ~Eideal) {
default:
yyerror("didn't expect %O here [top=%d]", n->op, top);
break;
@ -1495,7 +1713,7 @@ walkconv(Node **np, NodeList **init)
Type *t;
Node *l;
Node *n;
n = *np;
t = n->type;
if(t == T)
@ -2846,7 +3064,7 @@ mapop(Node *n, int top, NodeList **init)
if(cl > 1)
yyerror("too many arguments to make map");
if(top != Erv)
if(!(top & Erv))
goto nottop;
// newmap(keysize int, valsize int,
@ -2884,7 +3102,7 @@ mapop(Node *n, int top, NodeList **init)
break;
case OINDEX:
if(top != Erv)
if(!(top & Erv))
goto nottop;
// mapaccess1(hmap map[any]any, key any) (val any);

View File

@ -133,6 +133,7 @@ fixedbugs/bug039.go:6: variable x redeclared in this block
previous declaration at fixedbugs/bug039.go:5
=========== fixedbugs/bug049.go
fixedbugs/bug049.go:6: invalid operation: s == nil
fixedbugs/bug049.go:6: illegal types for operand: EQ
string
nil