uchar funcdepth;
uchar builtin; // built-in name, like len or close
uchar walkdef;
+ uchar typecheck;
// most nodes
Node* left;
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
EXTERN Idir* idirs;
EXTERN Type* types[NTYPE];
+EXTERN Type* idealstring;
EXTERN uchar simtype[NTYPE];
EXTERN uchar isptr[NTYPE];
EXTERN uchar isforw[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];
void heapmoves(void);
void walkdeflist(NodeList*);
void walkdef(Node*);
+void typechecklist(NodeList*, int);
+Node* typecheck(Node**, int);
/*
* const.c
}
n->walkdef = 2;
+ if(n->type != T || n->sym == S) // builtin or no name
+ goto ret;
+
+
init = nil;
switch(n->op) {
case OLITERAL:
}
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");
NodeList *ll;
int lno;
Node *n;
-
+
n = *np;
if(n == N)
return;
n->op = OFALL;
break;
}
-
+
*np = n;
}
*nn = 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)
+typechecklist(NodeList *l, int top)
{
for(; l; l=l->next)
- walkexpr(&l->n, top, init);
+ typecheck(&l->n, top);
}
-void
-walkexpr(Node **np, int top, NodeList **init)
+/*
+ * 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)
{
- Node *r, *l;
- NodeList *ll, *lr;
+ int et, et1, et2;
+ Node *n, *l, *r;
+ int lno, ok;
Type *t;
- Sym *s;
- int et, cl, cr, typeok, op;
- int32 lno;
- Node *n;
n = *np;
+ if(n == N || n->typecheck == 1)
+ return n;
+ if(n->typecheck == 2)
+ fatal("typecheck loop");
+ n->typecheck = 2;
- if(n == N)
- return;
+ if(n->sym && n->walkdef != 1)
+ walkdef(n);
lno = setlineno(n);
- typeok = top & Etype;
- top &= ~Etype;
-
- if(debug['w'] > 1 && top == Etop)
- dump("walk-before", n);
-
-reswitch:
- t = T;
- et = Txxx;
+ ok = 0;
switch(n->op) {
- case ONAME:
- case OTYPE:
+ 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:
- if(n->sym != S && n->type == T)
- walkdef(n);
- break;
- }
+ ok |= Elv | Erv;
+ goto ret;
- switch(n->op) {
- default:
- dump("walk", n);
- fatal("walkexpr: switch 1 unknown op %N", n);
+ 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:
- goto ret;
+ 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 {
- walkexpr(&l, Erv | Etype, init);
+ typecheck(&l, Erv | Etype);
+ walkexpr(&l, Erv | Etype, &n->ninit); // TODO: remove
switch(l->op) {
default:
yyerror("invalid array bound %O", l->op);
- break;
+ 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");
- t->bound = 1;
+ goto error;
}
}
break;
case OTYPE:
if(l->type == T)
- break;
- if(l->type->etype != TDDD)
+ goto error;
+ if(l->type->etype != TDDD) {
yyerror("invalid array bound %T", l->type);
+ goto error;
+ }
t->bound = -100;
break;
}
}
- walkexpr(&r, Etype, init);
+ 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);
- goto ret;
+ break;
case OTMAP:
- l = n->left;
- r = n->right;
- walkexpr(&l, Etype, init);
- walkexpr(&r, Etype, init);
+ 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);
- goto ret;
+ 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);
- l = n->left;
- walkexpr(&l, Etype, init);
t->type = l->type;
t->chan = n->etype;
n->op = OTYPE;
n->type = t;
- goto ret;
+ n->left = N;
+ n->etype = 0;
+ break;
case OTSTRUCT:
+ ok |= Etype;
n->op = OTYPE;
n->type = dostruct(n->list, TSTRUCT);
- goto ret;
+ 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);
- goto ret;
+ 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.
+ * 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)
+ typecheck(&n, top | typeok);
+
+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;
case OKEY:
goto ret;
case OLITERAL:
- if(top != Erv)
+ if(!(top & Erv))
goto nottop;
n->addable = 1;
goto ret;
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;
walkdottype(n, init);
// fall through
case OCONV:
- if(top != Erv)
+ if(!(top & Erv))
goto nottop;
walkconv(&n, init);
goto ret;
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);
case OLSH:
case ORSH:
- if(top != Erv)
+ if(!(top & Erv))
goto nottop;
walkexpr(&n->left, Erv, init);
case OSUB:
case OMUL:
case ODIV:
- if(top != Erv)
+ if(!(top & Erv))
goto nottop;
walkexpr(&n->left, Erv, init);
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) {
goto ret;
case OCAP:
- if(top != Erv)
+ if(!(top & Erv))
goto nottop;
if(n->left == N) {
if(n->list == nil) {
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)
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);
goto ret;
case OADDR:
- if(top != Erv)
+ if(!(top & Erv))
goto nottop;
defaultlit(&n->left, T);
if(n->left->op == OCOMPOS) {
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");
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;
if(n->left->type == T)
goto ret;
et = n->left->type->etype;
- if(!okforadd[et])
+ if(!okforarith[et])
goto badt;
break;
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,
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;
Type *t;
Node *l;
Node *n;
-
+
n = *np;
t = n->type;
if(t == T)
if(cl > 1)
yyerror("too many arguments to make map");
- if(top != Erv)
+ if(!(top & Erv))
goto nottop;
// newmap(keysize int, valsize int,
break;
case OINDEX:
- if(top != Erv)
+ if(!(top & Erv))
goto nottop;
// mapaccess1(hmap map[any]any, key any) (val any);