summaryrefslogtreecommitdiff
path: root/limbo/typecheck.c
diff options
context:
space:
mode:
Diffstat (limited to 'limbo/typecheck.c')
-rw-r--r--limbo/typecheck.c3635
1 files changed, 3635 insertions, 0 deletions
diff --git a/limbo/typecheck.c b/limbo/typecheck.c
new file mode 100644
index 00000000..827aa1e1
--- /dev/null
+++ b/limbo/typecheck.c
@@ -0,0 +1,3635 @@
+#include "limbo.h"
+#include "y.tab.h"
+
+Node **labstack;
+int labdep;
+static Node* inexcept;
+static Decl* fndec;
+
+void checkraises(Node *n);
+
+static void
+increfs(Decl *id)
+{
+ for( ; id != nil; id = id->link)
+ id->refs++;
+}
+
+static int
+fninline(Decl *d)
+{
+ Node *n, *left, *right;
+
+ left = right = nil;
+ n = d->init;
+ if(dontinline || d->caninline < 0 || d->locals != nil || ispoly(d) || n->ty->tof->kind == Tnone || nodes(n) >= 100)
+ return 0;
+ n = n->right;
+ if(n->op == Oseq && n->right == nil)
+ n = n->left;
+ /*
+ * inline
+ * (a) return e;
+ * (b) if(c) return e1; else return e2;
+ * (c) if(c) return e1; return e2;
+ */
+ switch(n->op){
+ case Oret:
+ break;
+ case Oif:
+ right = n->right;
+ if(right->right == nil || right->left->op != Oret || right->right->op != Oret || !tequal(right->left->left->ty, right->right->left->ty))
+ return 0;
+ break;
+ case Oseq:
+ left = n->left;
+ right = n->right;
+ if(left->op != Oif || left->right->right != nil || left->right->left->op != Oret || right->op != Oseq || right->right != nil || right->left->op != Oret || !tequal(left->right->left->left->ty, right->left->left->ty))
+ return 0;
+ break;
+ default:
+ return 0;
+ }
+ if(occurs(d, n) || hasasgns(n))
+ return 0;
+ if(n->op == Oseq){
+ left->right->right = right->left;
+ n = left;
+ right = n->right;
+ d->init->right->right = nil;
+ }
+ if(n->op == Oif){
+ n->ty = right->ty = right->left->left->ty;
+ right->left = right->left->left;
+ right->right = right->right->left;
+ d->init->right->left = mkunary(Oret, n);
+ }
+ return 1;
+}
+
+static int
+isfnrefty(Type *t)
+{
+ return t->kind == Tref && t->tof->kind == Tfn;
+}
+
+static int
+isfnref(Decl *d)
+{
+ switch(d->store){
+ case Dglobal:
+ case Darg:
+ case Dlocal:
+ case Dfield:
+ case Dimport:
+ return isfnrefty(d->ty);
+ }
+ return 0;
+}
+
+int
+argncompat(Node *n, Decl *f, Node *a)
+{
+ for(; a != nil; a = a->right){
+ if(f == nil){
+ nerror(n, "%V: too many function arguments", n->left);
+ return 0;
+ }
+ f = f->next;
+ }
+ if(f != nil){
+ nerror(n, "%V: too few function arguments", n->left);
+ return 0;
+ }
+ return 1;
+}
+
+static void
+rewind(Node *n)
+{
+ Node *r, *nn;
+
+ r = n;
+ nn = n->left;
+ for(n = n->right; n != nil; n = n->right){
+ if(n->right == nil){
+ r->left = nn;
+ r->right = n->left;
+ }
+ else
+ nn = mkbin(Oindex, nn, n->left);
+ }
+}
+
+static void
+ckmod(Node *n, Decl *id)
+{
+ Type *t;
+ Decl *d, *idc;
+ Node *mod;
+
+ if(id == nil)
+ fatal("can't find function: %n", n);
+ idc = nil;
+ mod = nil;
+ if(n->op == Oname){
+ idc = id;
+ mod = id->eimport;
+ }
+ else if(n->op == Omdot)
+ mod = n->left;
+ else if(n->op == Odot){
+ idc = id->dot;
+ t = n->left->ty;
+ if(t->kind == Tref)
+ t = t->tof;
+ if(t->kind == Tadtpick)
+ t = t->decl->dot->ty;
+ d = t->decl;
+ while(d != nil && d->link != nil)
+ d = d->link;
+ if(d != nil && d->timport != nil)
+ mod = d->timport->eimport;
+ n->right->left = mod;
+ }
+ if(mod != nil && mod->ty->kind != Tmodule){
+ nerror(n, "cannot use %V as a function reference", n);
+ return;
+ }
+ if(mod != nil){
+ if(valistype(mod)){
+ nerror(n, "cannot use %V as a function reference because %V is a module interface", n, mod);
+ return;
+ }
+ }else if(idc != nil && idc->dot != nil && !isimpmod(idc->dot->sym)){
+ nerror(n, "cannot use %V without importing %s from a variable", n, idc->sym->name);
+ return;
+ }
+ if(mod != nil)
+ modrefable(n->ty);
+}
+
+static void
+addref(Node *n)
+{
+ Node *nn;
+
+ nn = mkn(0, nil, nil);
+ *nn = *n;
+ n->op = Oref;
+ n->left = nn;
+ n->right = nil;
+ n->decl = nil;
+ n->ty = usetype(mktype(&n->src.start, &n->src.stop, Tref, nn->ty, nil));
+}
+
+static void
+fnref(Node *n, Decl *id)
+{
+ id->caninline = -1;
+ ckmod(n, id);
+ addref(n);
+ while(id->link != nil)
+ id = id->link;
+ if(ispoly(id) && encpolys(id) != nil)
+ nerror(n, "cannot have a polymorphic adt function reference %s", id->sym->name);
+}
+
+Decl*
+typecheck(int checkimp)
+{
+ Decl *entry, *m, *d;
+ Sym *s;
+ int i;
+
+ if(errors)
+ return nil;
+
+ /*
+ * generate the set of all functions
+ * compile one function at a time
+ */
+ gdecl(tree);
+ gbind(tree);
+ fns = allocmem(nfns * sizeof(Decl));
+ i = gcheck(tree, fns, 0);
+ if(i != nfns)
+ fatal("wrong number of functions found in gcheck");
+
+ maxlabdep = 0;
+ for(i = 0; i < nfns; i++){
+ d = fns[i];
+ if(d != nil)
+ fndec = d;
+ if(d != nil)
+ fncheck(d);
+ fndec = nil;
+ }
+
+ if(errors)
+ return nil;
+
+ entry = nil;
+ if(checkimp){
+ Decl *im;
+ Dlist *dm;
+
+ if(impmods == nil){
+ yyerror("no implementation module");
+ return nil;
+ }
+ for(im = impmods; im != nil; im = im->next){
+ for(dm = impdecls; dm != nil; dm = dm->next)
+ if(dm->d->sym == im->sym)
+ break;
+ if(dm == nil || dm->d->ty == nil){
+ yyerror("no definition for implementation module %s", im->sym->name);
+ return nil;
+ }
+ }
+
+ /*
+ * can't check the module spec until all types and imports are determined,
+ * which happens in scheck
+ */
+ for(dm = impdecls; dm != nil; dm = dm->next){
+ im = dm->d;
+ im->refs++;
+ im->ty = usetype(im->ty);
+ if(im->store != Dtype || im->ty->kind != Tmodule){
+ error(im->src.start, "cannot implement %K", im);
+ return nil;
+ }
+ }
+
+ /* now check any multiple implementations */
+ impdecl = modimp(impdecls, impmods);
+
+ s = enter("init", 0);
+ entry = nil;
+ for(dm = impdecls; dm != nil; dm = dm->next){
+ im = dm->d;
+ for(m = im->ty->ids; m != nil; m = m->next){
+ m->ty = usetype(m->ty);
+ m->refs++;
+
+ if(m->sym == s && m->ty->kind == Tfn && entry == nil)
+ entry = m;
+
+ if(m->store == Dglobal || m->store == Dfn)
+ modrefable(m->ty);
+
+ if(m->store == Dtype && m->ty->kind == Tadt){
+ for(d = m->ty->ids; d != nil; d = d->next){
+ d->ty = usetype(d->ty);
+ modrefable(d->ty);
+ d->refs++;
+ }
+ }
+ }
+ checkrefs(im->ty->ids);
+ }
+ }
+
+ if(errors)
+ return nil;
+ gsort(tree);
+ tree = nil;
+ return entry;
+}
+
+/*
+ * introduce all global declarations
+ * also adds all fields to adts and modules
+ * note the complications due to nested Odas expressions
+ */
+void
+gdecl(Node *n)
+{
+ for(;;){
+ if(n == nil)
+ return;
+ if(n->op != Oseq)
+ break;
+ gdecl(n->left);
+ n = n->right;
+ }
+ switch(n->op){
+ case Oimport:
+ importdecled(n);
+ gdasdecl(n->right);
+ break;
+ case Oadtdecl:
+ adtdecled(n);
+ break;
+ case Ocondecl:
+ condecled(n);
+ gdasdecl(n->right);
+ break;
+ case Oexdecl:
+ exdecled(n);
+ break;
+ case Omoddecl:
+ moddecled(n);
+ break;
+ case Otypedecl:
+ typedecled(n);
+ break;
+ case Ovardecl:
+ vardecled(n);
+ break;
+ case Ovardecli:
+ vardecled(n->left);
+ gdasdecl(n->right);
+ break;
+ case Ofunc:
+ fndecled(n);
+ break;
+ case Oas:
+ case Odas:
+ case Onothing:
+ gdasdecl(n);
+ break;
+ default:
+ fatal("can't deal with %O in gdecl", n->op);
+ }
+}
+
+/*
+ * bind all global type ids,
+ * including those nested inside modules
+ * this needs to be done, since we may use such
+ * a type later in a nested scope, so if we bound
+ * the type ids then, the type could get bound
+ * to a nested declaration
+ */
+void
+gbind(Node *n)
+{
+ Decl *d, *ids;
+
+ for(;;){
+ if(n == nil)
+ return;
+ if(n->op != Oseq)
+ break;
+ gbind(n->left);
+ n = n->right;
+ }
+ switch(n->op){
+ case Oas:
+ case Ocondecl:
+ case Odas:
+ case Oexdecl:
+ case Ofunc:
+ case Oimport:
+ case Onothing:
+ case Ovardecl:
+ case Ovardecli:
+ break;
+ case Ofielddecl:
+ bindtypes(n->decl->ty);
+ break;
+ case Otypedecl:
+ bindtypes(n->decl->ty);
+ if(n->left != nil)
+ gbind(n->left);
+ break;
+ case Opickdecl:
+ gbind(n->left);
+ d = n->right->left->decl;
+ bindtypes(d->ty);
+ repushids(d->ty->ids);
+ gbind(n->right->right);
+ /* get new ids for undefined types; propagate outwards */
+ ids = popids(d->ty->ids);
+ if(ids != nil)
+ installids(Dundef, ids);
+ break;
+ case Oadtdecl:
+ case Omoddecl:
+ bindtypes(n->ty);
+ if(n->ty->polys != nil)
+ repushids(n->ty->polys);
+ repushids(n->ty->ids);
+ gbind(n->left);
+ /* get new ids for undefined types; propagate outwards */
+ ids = popids(n->ty->ids);
+ if(ids != nil)
+ installids(Dundef, ids);
+ if(n->ty->polys != nil)
+ popids(n->ty->polys);
+ break;
+ default:
+ fatal("can't deal with %O in gbind", n->op);
+ }
+}
+
+/*
+ * check all of the > declarations
+ * bind all type ids referred to within types at the global level
+ * record decls for defined functions
+ */
+int
+gcheck(Node *n, Decl **fns, int nfns)
+{
+ Ok rok;
+ Decl *d;
+
+ for(;;){
+ if(n == nil)
+ return nfns;
+ if(n->op != Oseq)
+ break;
+ nfns = gcheck(n->left, fns, nfns);
+ n = n->right;
+ }
+
+ switch(n->op){
+ case Ofielddecl:
+ if(n->decl->ty->u.eraises)
+ raisescheck(n->decl->ty);
+ break;
+ case Onothing:
+ case Opickdecl:
+ break;
+ case Otypedecl:
+ tcycle(n->ty);
+ break;
+ case Oadtdecl:
+ case Omoddecl:
+ if(n->ty->polys != nil)
+ repushids(n->ty->polys);
+ repushids(n->ty->ids);
+ if(gcheck(n->left, nil, 0))
+ fatal("gcheck fn decls nested in modules or adts");
+ if(popids(n->ty->ids) != nil)
+ fatal("gcheck installs new ids in a module or adt");
+ if(n->ty->polys != nil)
+ popids(n->ty->polys);
+ break;
+ case Ovardecl:
+ varcheck(n, 1);
+ break;
+ case Ocondecl:
+ concheck(n, 1);
+ break;
+ case Oexdecl:
+ excheck(n, 1);
+ break;
+ case Oimport:
+ importcheck(n, 1);
+ break;
+ case Ovardecli:
+ varcheck(n->left, 1);
+ rok = echeck(n->right, 0, 1, nil);
+ if(rok.ok){
+ if(rok.allok)
+ n->right = fold(n->right);
+ globalas(n->right->left, n->right->right, rok.allok);
+ }
+ break;
+ case Oas:
+ case Odas:
+ rok = echeck(n, 0, 1, nil);
+ if(rok.ok){
+ if(rok.allok)
+ n = fold(n);
+ globalas(n->left, n->right, rok.allok);
+ }
+ break;
+ case Ofunc:
+ rok = echeck(n->left, 0, 1, n);
+ if(rok.ok && n->ty->u.eraises)
+ raisescheck(n->ty);
+ d = nil;
+ if(rok.ok)
+ d = fnchk(n);
+ fns[nfns++] = d;
+ break;
+ default:
+ fatal("can't deal with %O in gcheck", n->op);
+ }
+ return nfns;
+}
+
+/*
+ * check for unused expression results
+ * make sure the any calculated expression has
+ * a destination
+ */
+Node*
+checkused(Node *n)
+{
+ Type *t;
+ Node *nn;
+
+ /*
+ * only nil; and nil = nil; should have type tany
+ */
+ if(n->ty == tany){
+ if(n->op == Oname)
+ return n;
+ if(n->op == Oas)
+ return checkused(n->right);
+ fatal("line %L checkused %n", n->src.start, n);
+ }
+
+ if(n->op == Ocall && n->left->ty->kind == Tfn && n->left->ty->tof != tnone){
+ n = mkunary(Oused, n);
+ n->ty = n->left->ty;
+ return n;
+ }
+ if(n->op == Ocall && isfnrefty(n->left->ty)){
+ if(n->left->ty->tof->tof != tnone){
+ n = mkunary(Oused, n);
+ n->ty = n->left->ty;
+ }
+ return n;
+ }
+ if(isused[n->op] && (n->op != Ocall || n->left->ty->kind == Tfn))
+ return n;
+ t = n->ty;
+ if(t->kind == Tfn)
+ nerror(n, "function %V not called", n);
+ else if(t->kind == Tadt && t->tags != nil || t->kind == Tadtpick)
+ nerror(n, "expressions cannot have type %T", t);
+ else if(n->op == Otuple){
+ for(nn = n->left; nn != nil; nn = nn->right)
+ checkused(nn->left);
+ }
+ else
+ nwarn(n, "result of expression %V not used", n);
+ n = mkunary(Oused, n);
+ n->ty = n->left->ty;
+ return n;
+}
+
+void
+fncheck(Decl *d)
+{
+ Node *n;
+ Decl *adtp;
+
+ n = d->init;
+ if(debug['t'])
+ print("typecheck tree: %n\n", n);
+
+ fndecls = nil;
+ adtp = outerpolys(n->left);
+ if(n->left->op == Odot)
+ repushids(adtp);
+ if(d->ty->polys != nil)
+ repushids(d->ty->polys);
+ repushids(d->ty->ids);
+
+ labdep = 0;
+ labstack = allocmem(maxlabdep * sizeof *labstack);
+ n->right = scheck(n->right, d->ty->tof, Sother);
+ if(labdep != 0)
+ fatal("unbalanced label stack in fncheck");
+ free(labstack);
+
+ d->locals = appdecls(popids(d->ty->ids), fndecls);
+ if(d->ty->polys != nil)
+ popids(d->ty->polys);
+ if(n->left->op == Odot)
+ popids(adtp);
+ fndecls = nil;
+
+ checkrefs(d->ty->ids);
+ checkrefs(d->ty->polys);
+ checkrefs(d->locals);
+
+ checkraises(n);
+
+ d->caninline = fninline(d);
+}
+
+Node*
+scheck(Node *n, Type *ret, int kind)
+{
+ Node *left, *right, *last, *top;
+ Decl *d;
+ Sym *s;
+ Ok rok;
+ int i;
+
+ top = n;
+ last = nil;
+ for(; n != nil; n = n->right){
+ left = n->left;
+ right = n->right;
+ switch(n->op){
+ case Ovardecl:
+ vardecled(n);
+ varcheck(n, 0);
+ if (nested() && tmustzero(n->decl->ty))
+ decltozero(n);
+/*
+ else if (inloop() && tmustzero(n->decl->ty))
+ decltozero(n);
+*/
+ return top;
+ case Ovardecli:
+ vardecled(left);
+ varcheck(left, 0);
+ echeck(right, 0, 0, nil);
+ if (nested() && tmustzero(left->decl->ty))
+ decltozero(left);
+ return top;
+ case Otypedecl:
+ typedecled(n);
+ bindtypes(n->ty);
+ tcycle(n->ty);
+ return top;
+ case Ocondecl:
+ condecled(n);
+ concheck(n, 0);
+ return top;
+ case Oexdecl:
+ exdecled(n);
+ excheck(n, 0);
+ return top;
+ case Oimport:
+ importdecled(n);
+ importcheck(n, 0);
+ return top;
+ case Ofunc:
+ fatal("scheck func");
+ case Oscope:
+ pushscope(n, kind == Sother ? Sscope : kind);
+ if (left != nil)
+ fatal("Oscope has left field");
+ echeck(left, 0, 0, nil);
+ n->right = scheck(right, ret, Sother);
+ d = popscope();
+ fndecls = appdecls(fndecls, d);
+ return top;
+ case Olabel:
+ echeck(left, 0, 0, nil);
+ n->right = scheck(right, ret, Sother);
+ return top;
+ case Oseq:
+ n->left = scheck(left, ret, Sother);
+ /* next time will check n->right */
+ break;
+ case Oif:
+ rok = echeck(left, 0, 0, nil);
+ if(rok.ok && left->op != Onothing && left->ty != tint)
+ nerror(n, "if conditional must be an int, not %Q", left);
+ right->left = scheck(right->left, ret, Sother);
+ /* next time will check n->right->right */
+ n = right;
+ break;
+ case Ofor:
+ rok = echeck(left, 0, 0, nil);
+ if(rok.ok && left->op != Onothing && left->ty != tint)
+ nerror(n, "for conditional must be an int, not %Q", left);
+ /*
+ * do the continue clause before the body
+ * this reflects the ordering of declarations
+ */
+ pushlabel(n);
+ right->right = scheck(right->right, ret, Sother);
+ right->left = scheck(right->left, ret, Sloop);
+ labdep--;
+ if(n->decl != nil && !n->decl->refs)
+ nwarn(n, "label %s never referenced", n->decl->sym->name);
+ return top;
+ case Odo:
+ rok = echeck(left, 0, 0, nil);
+ if(rok.ok && left->op != Onothing && left->ty != tint)
+ nerror(n, "do conditional must be an int, not %Q", left);
+ pushlabel(n);
+ n->right = scheck(n->right, ret, Sloop);
+ labdep--;
+ if(n->decl != nil && !n->decl->refs)
+ nwarn(n, "label %s never referenced", n->decl->sym->name);
+ return top;
+ case Oalt:
+ case Ocase:
+ case Opick:
+ case Oexcept:
+ pushlabel(n);
+ switch(n->op){
+ case Oalt:
+ altcheck(n, ret);
+ break;
+ case Ocase:
+ casecheck(n, ret);
+ break;
+ case Opick:
+ pickcheck(n, ret);
+ break;
+ case Oexcept:
+ exccheck(n, ret);
+ break;
+ }
+ labdep--;
+ if(n->decl != nil && !n->decl->refs)
+ nwarn(n, "label %s never referenced", n->decl->sym->name);
+ return top;
+ case Oret:
+ rok = echeck(left, 0, 0, nil);
+ if(!rok.ok)
+ return top;
+ if(left == nil){
+ if(ret != tnone)
+ nerror(n, "return of nothing from a fn of %T", ret);
+ }else if(ret == tnone){
+ if(left->ty != tnone)
+ nerror(n, "return %Q from a fn with no return type", left);
+ }else if(!tcompat(ret, left->ty, 0))
+ nerror(n, "return %Q from a fn of %T", left, ret);
+ return top;
+ case Obreak:
+ case Ocont:
+ s = nil;
+ if(n->decl != nil)
+ s = n->decl->sym;
+ for(i = 0; i < labdep; i++){
+ if(s == nil || labstack[i]->decl != nil && labstack[i]->decl->sym == s){
+ if(n->op == Ocont
+ && labstack[i]->op != Ofor && labstack[i]->op != Odo)
+ continue;
+ if(s != nil)
+ labstack[i]->decl->refs++;
+ return top;
+ }
+ }
+ nerror(n, "no appropriate target for %V", n);
+ return top;
+ case Oexit:
+ case Onothing:
+ return top;
+ case Oexstmt:
+ fndec->handler = 1;
+ n->left = scheck(left, ret, Sother);
+ n->right = scheck(right, ret, Sother);
+ return top;
+ default:
+ rok = echeck(n, 0, 0, nil);
+ if(rok.allok)
+ n = checkused(n);
+ if(last == nil)
+ return n;
+ last->right = n;
+ return top;
+ }
+ last = n;
+ }
+ return top;
+}
+
+void
+pushlabel(Node *n)
+{
+ Sym *s;
+ int i;
+
+ if(labdep >= maxlabdep){
+ maxlabdep += MaxScope;
+ labstack = reallocmem(labstack, maxlabdep * sizeof *labstack);
+ }
+ if(n->decl != nil){
+ s = n->decl->sym;
+ n->decl->refs = 0;
+ for(i = 0; i < labdep; i++)
+ if(labstack[i]->decl != nil && labstack[i]->decl->sym == s)
+ nerror(n, "label %s duplicated on line %L", s->name, labstack[i]->decl->src.start);
+ }
+ labstack[labdep++] = n;
+}
+
+void
+varcheck(Node *n, int isglobal)
+{
+ Type *t;
+ Decl *ids, *last;
+
+ t = validtype(n->ty, nil);
+ t = topvartype(t, n->decl, isglobal, 0);
+ last = n->left->decl;
+ for(ids = n->decl; ids != last->next; ids = ids->next){
+ ids->ty = t;
+ shareloc(ids);
+ }
+ if(t->u.eraises)
+ raisescheck(t);
+}
+
+void
+concheck(Node *n, int isglobal)
+{
+ Decl *ids, *last;
+ Type *t;
+ Node *init;
+ Ok rok;
+ int i;
+
+ pushscope(nil, Sother);
+ installids(Dconst, iota);
+ rok = echeck(n->right, 0, isglobal, nil);
+ popscope();
+
+ init = n->right;
+ if(!rok.ok){
+ t = terror;
+ }else{
+ t = init->ty;
+ if(!tattr[t->kind].conable){
+ nerror(init, "cannot have a %T constant", t);
+ rok.allok = 0;
+ }
+ }
+
+ last = n->left->decl;
+ for(ids = n->decl; ids != last->next; ids = ids->next)
+ ids->ty = t;
+
+ if(!rok.allok)
+ return;
+
+ i = 0;
+ for(ids = n->decl; ids != last->next; ids = ids->next){
+ if(rok.ok){
+ iota->init->val = i;
+ ids->init = dupn(0, &nosrc, init);
+ if(!varcom(ids))
+ rok.ok = 0;
+ }
+ i++;
+ }
+}
+
+static char*
+exname(Decl *d)
+{
+ int n;
+ Sym *m;
+ char *s;
+ char buf[16];
+
+ n = 0;
+ sprint(buf, "%d", scope-ScopeGlobal);
+ m = impmods->sym;
+ if(d->dot)
+ m = d->dot->sym;
+ if(m)
+ n += strlen(m->name)+1;
+ if(fndec)
+ n += strlen(fndec->sym->name)+1;
+ n += strlen(buf)+1+strlen(d->sym->name)+1;
+ s = malloc(n);
+ strcpy(s, "");
+ if(m){
+ strcat(s, m->name);
+ strcat(s, ".");
+ }
+ if(fndec){
+ strcat(s, fndec->sym->name);
+ strcat(s, ".");
+ }
+ strcat(s, buf);
+ strcat(s, ".");
+ strcat(s, d->sym->name);
+ return s;
+}
+
+void
+excheck(Node *n, int isglobal)
+{
+ char *nm;
+ Type *t;
+ Decl *ids, *last;
+
+ t = validtype(n->ty, nil);
+ t = topvartype(t, n->decl, isglobal, 0);
+ last = n->left->decl;
+ for(ids = n->decl; ids != last->next; ids = ids->next){
+ ids->ty = t;
+ nm = exname(ids);
+ ids->init = mksconst(&n->src, enterstring(nm, strlen(nm)));
+ /* ids->init = mksconst(&n->src, enterstring(strdup(ids->sym->name), strlen(ids->sym->name))); */
+ }
+}
+
+void
+importcheck(Node *n, int isglobal)
+{
+ Node *m;
+ Decl *id, *last, *v;
+ Type *t;
+ Ok rok;
+
+ rok = echeck(n->right, 1, isglobal, nil);
+ if(!rok.ok)
+ return;
+
+ m = n->right;
+ if(m->ty->kind != Tmodule || m->op != Oname){
+ nerror(n, "cannot import from %Q", m);
+ return;
+ }
+
+ last = n->left->decl;
+ for(id = n->decl; id != last->next; id = id->next){
+ v = namedot(m->ty->ids, id->sym);
+ if(v == nil){
+ error(id->src.start, "%s is not a member of %V", id->sym->name, m);
+ id->store = Dwundef;
+ continue;
+ }
+ id->store = v->store;
+ v->ty = validtype(v->ty, nil);
+ id->ty = t = v->ty;
+ if(id->store == Dtype && t->decl != nil){
+ id->timport = t->decl->timport;
+ t->decl->timport = id;
+ }
+ id->init = v->init;
+ id->importid = v;
+ id->eimport = m;
+ }
+}
+
+static Decl*
+rewcall(Node *n, Decl *d)
+{
+ /* put original function back now we're type checked */
+ while(d->link != nil)
+ d = d->link;
+ if(n->op == Odot)
+ n->right->decl = d;
+ else if(n->op == Omdot){
+ n->right->right->decl = d;
+ n->right->right->ty = d->ty;
+ }
+ else
+ fatal("bad op in Ocall rewcall");
+ n->ty = n->right->ty = d->ty;
+ d->refs++;
+ usetype(d->ty);
+ return d;
+}
+
+/*
+ * annotate the expression with types
+ */
+Ok
+echeck(Node *n, int typeok, int isglobal, Node *par)
+{
+ Type *t, *tt;
+ Node *left, *right, *mod, *nn;
+ Decl *tg, *id, *callee;
+ Sym *s;
+ int max, nocheck;
+ Ok ok, rok, kidsok;
+ static int tagopt;
+
+ ok.ok = ok.allok = 1;
+ if(n == nil)
+ return ok;
+
+ /* avoid deep recursions */
+ if(n->op == Oseq){
+ for( ; n != nil && n->op == Oseq; n = n->right){
+ rok = echeck(n->left, typeok == 2, isglobal, n);
+ ok.ok &= rok.ok;
+ ok.allok &= rok.allok;
+ n->ty = tnone;
+ }
+ if(n == nil)
+ return ok;
+ }
+
+ left = n->left;
+ right = n->right;
+
+ nocheck = 0;
+ if(n->op == Odot || n->op == Omdot || n->op == Ocall || n->op == Oref || n->op == Otagof || n->op == Oindex)
+ nocheck = 1;
+ if(n->op != Odas /* special case */
+ && n->op != Oload) /* can have better error recovery */
+ ok = echeck(left, nocheck, isglobal, n);
+ if(n->op != Odas /* special case */
+ && n->op != Odot /* special check */
+ && n->op != Omdot /* special check */
+ && n->op != Ocall /* can have better error recovery */
+ && n->op != Oindex){
+ rok = echeck(right, 0, isglobal, n);
+ ok.ok &= rok.ok;
+ ok.allok &= rok.allok;
+ }
+ if(!ok.ok){
+ n->ty = terror;
+ ok.allok = 0;
+ return ok;
+ }
+
+ switch(n->op){
+ case Odas:
+ kidsok = echeck(right, 0, isglobal, n);
+ if(!kidsok.ok)
+ right->ty = terror;
+ if(!isglobal && !dasdecl(left)){
+ kidsok.ok = 0;
+ }else if(!specific(right->ty) || !declasinfer(left, right->ty)){
+ nerror(n, "cannot declare %V from %Q", left, right);
+ declaserr(left);
+ kidsok.ok = 0;
+ }
+ if(right->ty->kind == Texception)
+ left->ty = n->ty = mkextuptype(right->ty);
+ else{
+ left->ty = n->ty = right->ty;
+ usedty(n->ty);
+ }
+ kidsok.allok &= kidsok.ok;
+ if (nested() && tmustzero(left->ty))
+ decltozero(left);
+ return kidsok;
+ case Oseq:
+ case Onothing:
+ n->ty = tnone;
+ break;
+ case Owild:
+ n->ty = tint;
+ break;
+ case Ocast:
+ t = usetype(n->ty);
+ n->ty = t;
+ tt = left->ty;
+ if(tcompat(t, tt, 0)){
+ left->ty = t;
+ break;
+ }
+ if(tt->kind == Tarray){
+ if(tt->tof == tbyte && t == tstring)
+ break;
+ }else if(t->kind == Tarray){
+ if(t->tof == tbyte && tt == tstring)
+ break;
+ }else if(casttab[tt->kind][t->kind]){
+ break;
+ }
+ nerror(n, "cannot make a %T from %Q", n->ty, left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ case Ochan:
+ n->ty = usetype(n->ty);
+ if(left && left->ty->kind != Tint){
+ nerror(n, "channel size %Q is not an int", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ break;
+ case Oload:
+ n->ty = usetype(n->ty);
+ kidsok = echeck(left, 0, isglobal, n);
+ if(n->ty->kind != Tmodule){
+ nerror(n, "cannot load a %T, ", n->ty);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(!kidsok.allok){
+ ok.allok = 0;
+ break;
+ }
+ if(left->ty != tstring){
+ nerror(n, "cannot load a module from %Q", left);
+ ok.allok = 0;
+ break;
+ }
+if(n->ty->tof->decl->refs != 0)
+n->ty->tof->decl->refs++;
+n->ty->decl->refs++;
+ usetype(n->ty->tof);
+ break;
+ case Oref:
+ t = left->ty;
+ if(t->kind != Tadt && t->kind != Tadtpick && t->kind != Tfn && t->kind != Ttuple){
+ nerror(n, "cannot make a ref from %Q", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(!tagopt && t->kind == Tadt && t->tags != nil && valistype(left)){
+ nerror(n, "instances of ref %V must be qualified with a pick tag", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(t->kind == Tadtpick)
+ t->tof = usetype(t->tof);
+ n->ty = usetype(mktype(&n->src.start, &n->src.stop, Tref, t, nil));
+ break;
+ case Oarray:
+ max = 0;
+ if(right != nil){
+ max = assignindices(n);
+ if(max < 0){
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(!specific(right->left->ty)){
+ nerror(n, "type for array not specific");
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ n->ty = mktype(&n->src.start, &n->src.stop, Tarray, right->left->ty, nil);
+ }
+ n->ty = usetype(n->ty);
+
+ if(left->op == Onothing)
+ n->left = left = mkconst(&n->left->src, max);
+
+ if(left->ty->kind != Tint){
+ nerror(n, "array size %Q is not an int", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ break;
+ case Oelem:
+ n->ty = right->ty;
+ break;
+ case Orange:
+ if(left->ty != right->ty
+ || left->ty != tint && left->ty != tstring){
+ nerror(left, "range %Q to %Q is not an int or string range", left, right);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ n->ty = left->ty;
+ break;
+ case Oname:
+ id = n->decl;
+ if(id == nil){
+ nerror(n, "name with no declaration");
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(id->store == Dunbound){
+ s = id->sym;
+ id = s->decl;
+ if(id == nil)
+ id = undefed(&n->src, s);
+ /* save a little space */
+ s->unbound = nil;
+ n->decl = id;
+ id->refs++;
+ }
+ n->ty = id->ty = usetype(id->ty);
+ switch(id->store){
+ case Dfn:
+ case Dglobal:
+ case Darg:
+ case Dlocal:
+ case Dimport:
+ case Dfield:
+ case Dtag:
+ break;
+ case Dundef:
+ nerror(n, "%s is not declared", id->sym->name);
+ id->store = Dwundef;
+ ok.ok = ok.allok = 0;
+ return ok;
+ case Dwundef:
+ ok.ok = ok.allok = 0;
+ return ok;
+ case Dconst:
+ if(id->init == nil){
+ nerror(n, "%s's value cannot be determined", id->sym->name);
+ id->store = Dwundef;
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ break;
+ case Dtype:
+ if(typeok)
+ break;
+ nerror(n, "%K is not a variable", id);
+ ok.ok = ok.allok = 0;
+ return ok;
+ default:
+ fatal("echeck: unknown symbol storage");
+ }
+
+ if(n->ty == nil){
+ nerror(n, "%K's type is not fully defined", id);
+ id->store = Dwundef;
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(id->importid != nil && valistype(id->eimport)
+ && id->store != Dconst && id->store != Dtype && id->store != Dfn){
+ nerror(n, "cannot use %V because %V is a module interface", n, id->eimport);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(n->ty->kind == Texception && !n->ty->cons && par != nil && par->op != Oraise && par->op != Odot){
+ nn = mkn(0, nil, nil);
+ *nn = *n;
+ n->op = Ocast;
+ n->left = nn;
+ n->decl = nil;
+ n->ty = usetype(mkextuptype(n->ty));
+ }
+ /* function name as function reference */
+ if(id->store == Dfn && (par == nil || (par->op != Odot && par->op != Omdot && par->op != Ocall && par->op != Ofunc)))
+ fnref(n, id);
+ break;
+ case Oconst:
+ if(n->ty == nil){
+ nerror(n, "no type in %V", n);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ break;
+ case Oas:
+ t = right->ty;
+ if(t->kind == Texception)
+ t = mkextuptype(t);
+ if(!tcompat(left->ty, t, 1)){
+ nerror(n, "type clash in %Q = %Q", left, right);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(t == tany)
+ t = left->ty;
+ n->ty = t;
+ left->ty = t;
+ if(t->kind == Tadt && t->tags != nil || t->kind == Tadtpick)
+ if(left->ty->kind != Tadtpick || right->ty->kind != Tadtpick)
+ nerror(n, "expressions cannot have type %T", t);
+ if(left->ty->kind == Texception){
+ nerror(n, "cannot assign to an exception");
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(islval(left))
+ break;
+ ok.ok = ok.allok = 0;
+ return ok;
+ case Osnd:
+ if(left->ty->kind != Tchan){
+ nerror(n, "cannot send on %Q", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(!tcompat(left->ty->tof, right->ty, 0)){
+ nerror(n, "type clash in %Q <-= %Q", left, right);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ t = right->ty;
+ if(t == tany)
+ t = left->ty->tof;
+ n->ty = t;
+ break;
+ case Orcv:
+ t = left->ty;
+ if(t->kind == Tarray)
+ t = t->tof;
+ if(t->kind != Tchan){
+ nerror(n, "cannot receive on %Q", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(left->ty->kind == Tarray)
+ n->ty = usetype(mktype(&n->src.start, &n->src.stop, Ttuple, nil,
+ mkids(&n->src, nil, tint, mkids(&n->src, nil, t->tof, nil))));
+ else
+ n->ty = t->tof;
+ break;
+ case Ocons:
+ if(right->ty->kind != Tlist && right->ty != tany){
+ nerror(n, "cannot :: to %Q", right);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ n->ty = right->ty;
+ if(right->ty == tany)
+ n->ty = usetype(mktype(&n->src.start, &n->src.stop, Tlist, left->ty, nil));
+ else if(!tcompat(right->ty->tof, left->ty, 0)){
+ t = tparent(right->ty->tof, left->ty);
+ if(!tcompat(t, left->ty, 0)){
+ nerror(n, "type clash in %Q :: %Q", left, right);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ else
+ n->ty = usetype(mktype(&n->src.start, &n->src.stop, Tlist, t, nil));
+ }
+ break;
+ case Ohd:
+ case Otl:
+ if(left->ty->kind != Tlist || left->ty->tof == nil){
+ nerror(n, "cannot %O %Q", n->op, left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(n->op == Ohd)
+ n->ty = left->ty->tof;
+ else
+ n->ty = left->ty;
+ break;
+ case Otuple:
+ n->ty = usetype(mktype(&n->src.start, &n->src.stop, Ttuple, nil, tuplefields(left)));
+ break;
+ case Ospawn:
+ if(left->op != Ocall || left->left->ty->kind != Tfn && !isfnrefty(left->left->ty)){
+ nerror(left, "cannot spawn %V", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(left->ty != tnone){
+ nerror(left, "cannot spawn functions which return values, such as %Q", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ break;
+ case Oraise:
+ if(left->op == Onothing){
+ if(inexcept == nil){
+ nerror(n, "%V: empty raise not in exception handler", n);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ n->left = dupn(1, &n->src, inexcept);
+ break;
+ }
+ if(left->ty != tstring && left->ty->kind != Texception){
+ nerror(n, "%V: raise argument %Q is not a string or exception", n, left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if((left->op != Ocall || left->left->ty->kind == Tfn) && left->ty->ids != nil && left->ty->cons){
+ nerror(n, "too few exception arguments");
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ break;
+ case Ocall:{
+ int pure;
+
+ kidsok = echeck(right, 0, isglobal, nil);
+ t = left->ty;
+ usedty(t);
+ pure = 1;
+ if(t->kind == Tref){
+ pure = 0;
+ t = t->tof;
+ }
+ if(t->kind != Tfn)
+ return callcast(n, kidsok.allok, ok.allok);
+ n->ty = t->tof;
+ if(!kidsok.allok){
+ ok.allok = 0;
+ break;
+ }
+
+ /*
+ * get the name to call and any associated module
+ */
+ mod = nil;
+ callee = nil;
+ id = nil;
+ tt = nil;
+ if(left->op == Odot){
+ Decl *dd;
+ Type *ttt;
+
+ callee = left->right->decl;
+ id = callee->dot;
+ right = passimplicit(left, right);
+ n->right = right;
+ tt = left->left->ty;
+ if(tt->kind == Tref)
+ tt = tt->tof;
+ ttt = tt;
+ if(tt->kind == Tadtpick)
+ ttt = tt->decl->dot->ty;
+ dd = ttt->decl;
+ while(dd != nil && dd->link != nil)
+ dd = dd->link;
+ if(dd != nil && dd->timport != nil)
+ mod = dd->timport->eimport;
+ /*
+ * stash the import module under a rock,
+ * because we won't be able to get it later
+ * after scopes are popped
+ */
+ left->right->left = mod;
+ }else if(left->op == Omdot){
+ if(left->right->op == Odot){
+ callee = left->right->right->decl;
+ right = passimplicit(left->right, right);
+ n->right = right;
+ tt = left->right->left->ty;
+ if(tt->kind == Tref)
+ tt = tt->tof;
+ }else
+ callee = left->right->decl;
+ mod = left->left;
+ }else if(left->op == Oname){
+ callee = left->decl;
+ id = callee;
+ mod = id->eimport;
+ }else if(pure){
+ nerror(left, "%V is not a function name", left);
+ ok.allok = 0;
+ break;
+ }
+ if(pure && callee == nil)
+ fatal("can't find called function: %n", left);
+ if(callee != nil && callee->store != Dfn && !isfnref(callee)){
+ nerror(left, "%V is not a function or function reference", left);
+ ok.allok = 0;
+ break;
+ }
+ if(mod != nil && mod->ty->kind != Tmodule){
+ nerror(left, "cannot call %V", left);
+ ok.allok = 0;
+ break;
+ }
+ if(mod != nil){
+ if(valistype(mod)){
+ nerror(left, "cannot call %V because %V is a module interface", left, mod);
+ ok.allok = 0;
+ break;
+ }
+ }else if(id != nil && id->dot != nil && !isimpmod(id->dot->sym)){
+ nerror(left, "cannot call %V without importing %s from a variable", left, id->sym->name);
+ ok.allok = 0;
+ break;
+ }
+ if(mod != nil)
+ modrefable(left->ty);
+ if(callee != nil && callee->store != Dfn)
+ callee = nil;
+ if(t->varargs != 0){
+ t = mkvarargs(left, right);
+ if(left->ty->kind == Tref)
+ left->ty = usetype(mktype(&t->src.start, &t->src.stop, Tref, t, nil));
+ else
+ left->ty = t;
+ }
+ else if(ispoly(callee) || isfnrefty(left->ty) && left->ty->tof->polys != nil){
+ Tpair *tp;
+
+ unifysrc = n->src;
+ if(!argncompat(n, t->ids, right))
+ ok.allok = 0;
+ else if(!tunify(left->ty, calltype(left->ty, right, n->ty), &tp)){
+ nerror(n, "function call type mismatch");
+ ok.allok = 0;
+ }
+ else{
+ n->ty = usetype(expandtype(n->ty, nil, nil, &tp));
+ if(ispoly(callee) && tt != nil && (tt->kind == Tadt || tt->kind == Tadtpick) && (tt->flags&INST))
+ callee = rewcall(left, callee);
+ n->right = passfns(&n->src, callee, left, right, tt, tp);
+ }
+ }
+ else if(!argcompat(n, t->ids, right))
+ ok.allok = 0;
+ break;
+ }
+ case Odot:
+ t = left->ty;
+ if(t->kind == Tref)
+ t = t->tof;
+ switch(t->kind){
+ case Tadt:
+ case Tadtpick:
+ case Ttuple:
+ case Texception:
+ case Tpoly:
+ id = namedot(t->ids, right->decl->sym);
+ if(id == nil){
+ id = namedot(t->tags, right->decl->sym);
+ if(id != nil && !valistype(left)){
+ nerror(n, "%V is not a type", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ }
+ if(id == nil){
+ id = namedot(t->polys, right->decl->sym);
+ if(id != nil && !valistype(left)){
+ nerror(n, "%V is not a type", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ }
+ if(id == nil && t->kind == Tadtpick)
+ id = namedot(t->decl->dot->ty->ids, right->decl->sym);
+ if(id == nil){
+ for(tg = t->tags; tg != nil; tg = tg->next){
+ id = namedot(tg->ty->ids, right->decl->sym);
+ if(id != nil)
+ break;
+ }
+ if(id != nil){
+ nerror(n, "cannot yet index field %s of %Q", right->decl->sym->name, left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ }
+ if(id == nil)
+ break;
+ if(id->store == Dfield && valistype(left)){
+ nerror(n, "%V is not a value", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ id->ty = validtype(id->ty, t->decl);
+ id->ty = usetype(id->ty);
+ break;
+ default:
+ nerror(left, "%Q cannot be qualified with .", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(id == nil){
+ nerror(n, "%V is not a member of %Q", right, left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(id->ty == tunknown){
+ nerror(n, "illegal forward reference to %V", n);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+
+ increfs(id);
+ right->decl = id;
+ n->ty = id->ty;
+ if((id->store == Dconst || id->store == Dtag) && hasside(left, 1))
+ nwarn(left, "result of expression %Q ignored", left);
+ /* function name as function reference */
+ if(id->store == Dfn && (par == nil || (par->op != Omdot && par->op != Ocall && par->op != Ofunc)))
+ fnref(n, id);
+ break;
+ case Omdot:
+ t = left->ty;
+ if(t->kind != Tmodule){
+ nerror(left, "%Q cannot be qualified with ->", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ id = nil;
+ if(right->op == Oname){
+ id = namedot(t->ids, right->decl->sym);
+ }else if(right->op == Odot){
+ kidsok = echeck(right, 0, isglobal, n);
+ ok.ok = kidsok.ok;
+ ok.allok &= kidsok.allok;
+ if(!ok.ok){
+ ok.allok = 0;
+ return ok;
+ }
+ tt = right->left->ty;
+ if(tt->kind == Tref)
+ tt = tt->tof;
+ if(right->ty->kind == Tfn
+ && tt->kind == Tadt
+ && tt->decl->dot == t->decl)
+ id = right->right->decl;
+ }
+ if(id == nil){
+ nerror(n, "%V is not a member of %Q", right, left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(id->store != Dconst && id->store != Dtype && id->store != Dtag){
+ if(valistype(left)){
+ nerror(n, "%V is not a value", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ }else if(hasside(left, 1))
+ nwarn(left, "result of expression %Q ignored", left);
+ if(!typeok && id->store == Dtype){
+ nerror(n, "%V is a type, not a value", n);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(id->ty == tunknown){
+ nerror(n, "illegal forward reference to %V", n);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ id->refs++;
+ right->decl = id;
+ n->ty = id->ty = usetype(id->ty);
+ if(id->store == Dglobal)
+ modrefable(id->ty);
+ /* function name as function reference */
+ if(id->store == Dfn && (par == nil || (par->op != Ocall && par->op != Ofunc)))
+ fnref(n, id);
+ break;
+ case Otagof:
+ n->ty = tint;
+ t = left->ty;
+ if(t->kind == Tref)
+ t = t->tof;
+ id = nil;
+ switch(left->op){
+ case Oname:
+ id = left->decl;
+ break;
+ case Odot:
+ id = left->right->decl;
+ break;
+ case Omdot:
+ if(left->right->op == Odot)
+ id = left->right->right->decl;
+ break;
+ }
+ if(id != nil && id->store == Dtag
+ || id != nil && id->store == Dtype && t->kind == Tadt && t->tags != nil)
+ n->decl = id;
+ else if(t->kind == Tadt && t->tags != nil || t->kind == Tadtpick)
+ n->decl = nil;
+ else{
+ nerror(n, "cannot get the tag value for %Q", left);
+ ok.ok = 1;
+ ok.allok = 0;
+ return ok;
+ }
+ break;
+ case Oind:
+ t = left->ty;
+ if(t->kind != Tref || (t->tof->kind != Tadt && t->tof->kind != Tadtpick && t->tof->kind != Ttuple)){
+ nerror(n, "cannot * %Q", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ n->ty = t->tof;
+ for(tg = t->tof->tags; tg != nil; tg = tg->next)
+ tg->ty->tof = usetype(tg->ty->tof);
+ break;
+ case Oindex:
+ if(valistype(left)){
+ tagopt = 1;
+ kidsok = echeck(right, 2, isglobal, n);
+ tagopt = 0;
+ if(!kidsok.allok){
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if((t = exptotype(n)) == nil){
+ nerror(n, "%V is not a type list", right);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(!typeok){
+ nerror(n, "%Q is not a variable", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ *n = *(n->left);
+ n->ty = usetype(t);
+ break;
+ }
+ if(0 && right->op == Oseq){ /* a[e1, e2, ...] */
+ /* array creation to do before we allow this */
+ rewind(n);
+ return echeck(n, typeok, isglobal, par);
+ }
+ t = left->ty;
+ kidsok = echeck(right, 0, isglobal, n);
+ if(t->kind != Tarray && t != tstring){
+ nerror(n, "cannot index %Q", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(t == tstring){
+ n->op = Oinds;
+ n->ty = tint;
+ }else{
+ n->ty = t->tof;
+ }
+ if(!kidsok.allok){
+ ok.allok = 0;
+ break;
+ }
+ if(right->ty != tint){
+ nerror(n, "cannot index %Q with %Q", left, right);
+ ok.allok = 0;
+ break;
+ }
+ break;
+ case Oslice:
+ t = n->ty = left->ty;
+ if(t->kind != Tarray && t != tstring){
+ nerror(n, "cannot slice %Q with '%v:%v'", left, right->left, right->right);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(right->left->ty != tint && right->left->op != Onothing
+ || right->right->ty != tint && right->right->op != Onothing){
+ nerror(n, "cannot slice %Q with '%v:%v'", left, right->left, right->right);
+ ok.allok = 0;
+ return ok;
+ }
+ break;
+ case Olen:
+ t = left->ty;
+ n->ty = tint;
+ if(t->kind != Tarray && t->kind != Tlist && t != tstring){
+ nerror(n, "len requires an array, string or list in %Q", left);
+ ok.allok = 0;
+ return ok;
+ }
+ break;
+ case Ocomp:
+ case Onot:
+ case Oneg:
+ n->ty = left->ty;
+ usedty(n->ty);
+ switch(left->ty->kind){
+ case Tint:
+ return ok;
+ case Treal:
+ case Tfix:
+ if(n->op == Oneg)
+ return ok;
+ break;
+ case Tbig:
+ case Tbyte:
+ if(n->op == Oneg || n->op == Ocomp)
+ return ok;
+ break;
+ }
+ nerror(n, "cannot apply %O to %Q", n->op, left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ case Oinc:
+ case Odec:
+ case Opreinc:
+ case Opredec:
+ n->ty = left->ty;
+ switch(left->ty->kind){
+ case Tint:
+ case Tbig:
+ case Tbyte:
+ case Treal:
+ break;
+ default:
+ nerror(n, "cannot apply %O to %Q", n->op, left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(islval(left))
+ break;
+ ok.ok = ok.allok = 0;
+ return ok;
+ case Oadd:
+ case Odiv:
+ case Omul:
+ case Osub:
+ if(mathchk(n, 1))
+ break;
+ ok.ok = ok.allok = 0;
+ return ok;
+ case Oexp:
+ case Oexpas:
+ n->ty = left->ty;
+ if(n->ty != tint && n->ty != tbig && n->ty != treal){
+ nerror(n, "exponend %Q is not int, big or real", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(right->ty != tint){
+ nerror(n, "exponent %Q is not int", right);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(n->op == Oexpas && !islval(left)){
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ break;
+/*
+ if(mathchk(n, 0)){
+ if(n->ty != tint){
+ nerror(n, "exponentiation operands not int");
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ break;
+ }
+ ok.ok = ok.allok = 0;
+ return ok;
+*/
+ case Olsh:
+ case Orsh:
+ if(shiftchk(n))
+ break;
+ ok.ok = ok.allok = 0;
+ return ok;
+ case Oandand:
+ case Ooror:
+ if(left->ty != tint){
+ nerror(n, "%O's left operand is not an int: %Q", n->op, left);
+ ok.allok = 0;
+ }
+ if(right->ty != tint){
+ nerror(n, "%O's right operand is not an int: %Q", n->op, right);
+ ok.allok = 0;
+ }
+ n->ty = tint;
+ break;
+ case Oand:
+ case Omod:
+ case Oor:
+ case Oxor:
+ if(mathchk(n, 0))
+ break;
+ ok.ok = ok.allok = 0;
+ return ok;
+ case Oaddas:
+ case Odivas:
+ case Omulas:
+ case Osubas:
+ if(mathchk(n, 1) && islval(left))
+ break;
+ ok.ok = ok.allok = 0;
+ return ok;
+ case Olshas:
+ case Orshas:
+ if(shiftchk(n) && islval(left))
+ break;
+ ok.ok = ok.allok = 0;
+ return ok;
+ case Oandas:
+ case Omodas:
+ case Oxoras:
+ case Ooras:
+ if(mathchk(n, 0) && islval(left))
+ break;
+ ok.ok = ok.allok = 0;
+ return ok;
+ case Olt:
+ case Oleq:
+ case Ogt:
+ case Ogeq:
+ if(!mathchk(n, 1)){
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ n->ty = tint;
+ break;
+ case Oeq:
+ case Oneq:
+ switch(left->ty->kind){
+ case Tint:
+ case Tbig:
+ case Tbyte:
+ case Treal:
+ case Tstring:
+ case Tref:
+ case Tlist:
+ case Tarray:
+ case Tchan:
+ case Tany:
+ case Tmodule:
+ case Tfix:
+ case Tpoly:
+ if(!tcompat(left->ty, right->ty, 0) && !tcompat(right->ty, left->ty, 0))
+ break;
+ t = left->ty;
+ if(t == tany)
+ t = right->ty;
+ if(t == tany)
+ t = tint;
+ if(left->ty == tany)
+ left->ty = t;
+ if(right->ty == tany)
+ right->ty = t;
+ n->ty = tint;
+ return ok;
+ }
+ nerror(n, "cannot compare %Q to %Q", left, right);
+ usedty(n->ty);
+ ok.ok = ok.allok = 0;
+ return ok;
+ case Otype:
+ if(!typeok){
+ nerror(n, "%Q is not a variable", n);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ n->ty = usetype(n->ty);
+ break;
+ default:
+ fatal("unknown op in typecheck: %O", n->op);
+ }
+ usedty(n->ty);
+ return ok;
+}
+
+/*
+ * n is syntactically a call, but n->left is not a fn
+ * check if it's the contructor for an adt
+ */
+Ok
+callcast(Node *n, int kidsok, int allok)
+{
+ Node *left, *right;
+ Decl *id;
+ Type *t, *tt;
+ Ok ok;
+
+ left = n->left;
+ right = n->right;
+ id = nil;
+ switch(left->op){
+ case Oname:
+ id = left->decl;
+ break;
+ case Omdot:
+ if(left->right->op == Odot)
+ id = left->right->right->decl;
+ else
+ id = left->right->decl;
+ break;
+ case Odot:
+ id = left->right->decl;
+ break;
+ }
+/*
+ (chan of int)(nil) looks awkward since both sets of brackets needed
+ if(id == nil && right != nil && right->right == nil && (t = exptotype(left)) != nil){
+ n->op = Ocast;
+ n->left = right->left;
+ n->right = nil;
+ n->ty = t;
+ return echeck(n, 0, 0, nil);
+ }
+*/
+ if(id == nil || (id->store != Dtype && id->store != Dtag && id->ty->kind != Texception)){
+ nerror(left, "%V is not a function or type name", left);
+ ok.ok = ok.allok = 0;
+ return ok;
+ }
+ if(id->store == Dtag)
+ return tagcast(n, left, right, id, kidsok, allok);
+ t = left->ty;
+ n->ty = t;
+ if(!kidsok){
+ ok.ok = 1;
+ ok.allok = 0;
+ return ok;
+ }
+
+ if(t->kind == Tref)
+ t = t->tof;
+ tt = mktype(&n->src.start, &n->src.stop, Ttuple, nil, tuplefields(right));
+ if(t->kind == Tadt && tcompat(t, tt, 1)){
+ if(right == nil)
+ *n = *n->left;
+ ok.ok = 1;
+ ok.allok = allok;
+ return ok;
+ }
+
+ /* try an exception with args */
+ tt = mktype(&n->src.start, &n->src.stop, Texception, nil, tuplefields(right));
+ tt->cons = 1;
+ if(t->kind == Texception && t->cons && tcompat(t, tt, 1)){
+ if(right == nil)
+ *n = *n->left;
+ ok.ok = 1;
+ ok.allok = allok;
+ return ok;
+ }
+
+ /* try a cast */
+ if(t->kind != Texception && right != nil && right->right == nil){ /* Oseq but single expression */
+ right = right->left;
+ n->op = Ocast;
+ n->left = right;
+ n->right = nil;
+ n->ty = mkidtype(&n->src, id->sym);
+ return echeck(n, 0, 0, nil);
+ }
+
+ nerror(left, "cannot make a %V from '(%v)'", left, right);
+ ok.ok = ok.allok = 0;
+ return ok;
+}
+
+Ok
+tagcast(Node *n, Node *left, Node *right, Decl *id, int kidsok, int allok)
+{
+ Type *tt;
+ Ok ok;
+
+ left->ty = id->ty;
+ if(left->op == Omdot)
+ left->right->ty = id->ty;
+ n->ty = id->ty;
+ if(!kidsok){
+ ok.ok = 1;
+ ok.allok = 0;
+ return ok;
+ }
+ id->ty->tof = usetype(id->ty->tof);
+ if(right != nil)
+ right->ty = id->ty->tof;
+ tt = mktype(&n->src.start, &n->src.stop, Ttuple, nil, mkids(&nosrc, nil, tint, tuplefields(right)));
+ tt->ids->store = Dfield;
+ if(tcompat(id->ty->tof, tt, 1)){
+ ok.ok = 1;
+ ok.allok = allok;
+ return ok;
+ }
+
+ nerror(left, "cannot make a %V from '(%v)'", left, right);
+ ok.ok = ok.allok = 0;
+ return ok;
+}
+
+int
+valistype(Node *n)
+{
+ switch(n->op){
+ case Oname:
+ if(n->decl->store == Dtype)
+ return 1;
+ break;
+ case Omdot:
+ return valistype(n->right);
+ }
+ return 0;
+}
+
+int
+islval(Node *n)
+{
+ int s;
+
+ s = marklval(n);
+ if(s == 1)
+ return 1;
+ if(s == 0)
+ nerror(n, "cannot assign to %V", n);
+ else
+ circlval(n, n);
+ return 0;
+}
+
+/*
+ * check to see if n is an lval
+ * mark the lval name as set
+ */
+int
+marklval(Node *n)
+{
+ Decl *id;
+ Node *nn;
+ int s;
+
+ if(n == nil)
+ return 0;
+ switch(n->op){
+ case Oname:
+ return storespace[n->decl->store] && n->ty->kind != Texception; /*ZZZZ && n->decl->tagged == nil;*/
+ case Odot:
+ if(n->right->decl->store != Dfield)
+ return 0;
+ if(n->right->decl->cycle && !n->right->decl->cyc)
+ return -1;
+ if(n->left->ty->kind != Tref && marklval(n->left) == 0)
+ nwarn(n, "assignment to %Q ignored", n);
+ return 1;
+ case Omdot:
+ if(n->right->decl->store == Dglobal)
+ return 1;
+ return 0;
+ case Oind:
+ for(id = n->ty->ids; id != nil; id = id->next)
+ if(id->cycle && !id->cyc)
+ return -1;
+ return 1;
+ case Oslice:
+ if(n->right->right->op != Onothing || n->ty == tstring)
+ return 0;
+ return 1;
+ case Oinds:
+ /*
+ * make sure we don't change a string constant
+ */
+ switch(n->left->op){
+ case Oconst:
+ return 0;
+ case Oname:
+ return storespace[n->left->decl->store];
+ case Odot:
+ case Omdot:
+ if(n->left->right->decl != nil)
+ return storespace[n->left->right->decl->store];
+ break;
+ }
+ return 1;
+ case Oindex:
+ case Oindx:
+ return 1;
+ case Otuple:
+ for(nn = n->left; nn != nil; nn = nn->right){
+ s = marklval(nn->left);
+ if(s != 1)
+ return s;
+ }
+ return 1;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+/*
+ * n has a circular field assignment.
+ * find it and print an error message.
+ */
+int
+circlval(Node *n, Node *lval)
+{
+ Decl *id;
+ Node *nn;
+ int s;
+
+ if(n == nil)
+ return 0;
+ switch(n->op){
+ case Oname:
+ break;
+ case Odot:
+ if(n->right->decl->cycle && !n->right->decl->cyc){
+ nerror(lval, "cannot assign to %V because field '%s' of %V could complete a cycle to %V",
+ lval, n->right->decl->sym->name, n->left, n->left);
+ return -1;
+ }
+ return 1;
+ case Oind:
+ for(id = n->ty->ids; id != nil; id = id->next){
+ if(id->cycle && !id->cyc){
+ nerror(lval, "cannot assign to %V because field '%s' of %V could complete a cycle to %V",
+ lval, id->sym->name, n, n);
+ return -1;
+ }
+ }
+ return 1;
+ case Oslice:
+ if(n->right->right->op != Onothing || n->ty == tstring)
+ return 0;
+ return 1;
+ case Oindex:
+ case Oinds:
+ case Oindx:
+ return 1;
+ case Otuple:
+ for(nn = n->left; nn != nil; nn = nn->right){
+ s = circlval(nn->left, lval);
+ if(s != 1)
+ return s;
+ }
+ return 1;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+int
+mathchk(Node *n, int realok)
+{
+ Type *tr, *tl;
+
+ tl = n->left->ty;
+ tr = n->right->ty;
+ if(tr != tl && !tequal(tl, tr)){
+ nerror(n, "type clash in %Q %O %Q", n->left, n->op, n->right);
+ return 0;
+ }
+ n->ty = tr;
+ switch(tr->kind){
+ case Tint:
+ case Tbig:
+ case Tbyte:
+ return 1;
+ case Tstring:
+ switch(n->op){
+ case Oadd:
+ case Oaddas:
+ case Ogt:
+ case Ogeq:
+ case Olt:
+ case Oleq:
+ return 1;
+ }
+ break;
+ case Treal:
+ case Tfix:
+ if(realok)
+ return 1;
+ break;
+ }
+ nerror(n, "cannot %O %Q and %Q", n->op, n->left, n->right);
+ return 0;
+}
+
+int
+shiftchk(Node *n)
+{
+ Node *left, *right;
+
+ right = n->right;
+ left = n->left;
+ n->ty = left->ty;
+ switch(n->ty->kind){
+ case Tint:
+ case Tbyte:
+ case Tbig:
+ if(right->ty->kind != Tint){
+ nerror(n, "shift %Q is not an int", right);
+ return 0;
+ }
+ return 1;
+ }
+ nerror(n, "cannot %Q %O %Q", left, n->op, right);
+ return 0;
+}
+
+/*
+ * check for any tany's in t
+ */
+int
+specific(Type *t)
+{
+ Decl *d;
+
+ if(t == nil)
+ return 0;
+ switch(t->kind){
+ case Terror:
+ case Tnone:
+ case Tint:
+ case Tbig:
+ case Tstring:
+ case Tbyte:
+ case Treal:
+ case Tfn:
+ case Tadt:
+ case Tadtpick:
+ case Tmodule:
+ case Tfix:
+ return 1;
+ case Tany:
+ return 0;
+ case Tpoly:
+ return 1;
+ case Tref:
+ case Tlist:
+ case Tarray:
+ case Tchan:
+ return specific(t->tof);
+ case Ttuple:
+ case Texception:
+ for(d = t->ids; d != nil; d = d->next)
+ if(!specific(d->ty))
+ return 0;
+ return 1;
+ }
+ fatal("unknown type %T in specific", t);
+ return 0;
+}
+
+/*
+ * infer the type of all variable in n from t
+ * n is the left-hand exp of a := exp
+ */
+int
+declasinfer(Node *n, Type *t)
+{
+ Decl *ids;
+ int ok;
+
+ if(t->kind == Texception){
+ if(t->cons)
+ return 0;
+ t = mkextuptype(t);
+ }
+ switch(n->op){
+ case Otuple:
+ if(t->kind != Ttuple && t->kind != Tadt && t->kind != Tadtpick)
+ return 0;
+ ok = 1;
+ n->ty = t;
+ n = n->left;
+ ids = t->ids;
+ if(t->kind == Tadtpick)
+ ids = t->tof->ids->next;
+ for(; n != nil && ids != nil; ids = ids->next){
+ if(ids->store != Dfield)
+ continue;
+ ok &= declasinfer(n->left, ids->ty);
+ n = n->right;
+ }
+ for(; ids != nil; ids = ids->next)
+ if(ids->store == Dfield)
+ break;
+ if(n != nil || ids != nil)
+ return 0;
+ return ok;
+ case Oname:
+ topvartype(t, n->decl, 0, 0);
+ if(n->decl == nildecl)
+ return 1;
+ n->decl->ty = t;
+ n->ty = t;
+ shareloc(n->decl);
+ return 1;
+ }
+ fatal("unknown op %n in declasinfer", n);
+ return 0;
+}
+
+/*
+ * an error occured in declaring n;
+ * set all decl identifiers to Dwundef
+ * so further errors are squashed.
+ */
+void
+declaserr(Node *n)
+{
+ switch(n->op){
+ case Otuple:
+ for(n = n->left; n != nil; n = n->right)
+ declaserr(n->left);
+ return;
+ case Oname:
+ if(n->decl != nildecl)
+ n->decl->store = Dwundef;
+ return;
+ }
+ fatal("unknown op %n in declaserr", n);
+}
+
+int
+argcompat(Node *n, Decl *f, Node *a)
+{
+ for(; a != nil; a = a->right){
+ if(f == nil){
+ nerror(n, "%V: too many function arguments", n->left);
+ return 0;
+ }
+ if(!tcompat(f->ty, a->left->ty, 0)){
+ nerror(n, "%V: argument type mismatch: expected %T saw %Q",
+ n->left, f->ty, a->left);
+ return 0;
+ }
+ if(a->left->ty == tany)
+ a->left->ty = f->ty;
+ f = f->next;
+ }
+ if(f != nil){
+ nerror(n, "%V: too few function arguments", n->left);
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * fn is Odot(adt, methid)
+ * pass adt implicitly if needed
+ * if not, any side effect of adt will be ingored
+ */
+Node*
+passimplicit(Node *fn, Node *args)
+{
+ Node *n;
+ Type *t;
+
+ t = fn->ty;
+ if(t->ids == nil || !t->ids->implicit){
+ if(hasside(fn->left, 1))
+ nwarn(fn, "result of expression %V ignored", fn->left);
+ return args;
+ }
+ n = fn->left;
+ if(n->op == Oname && n->decl->store == Dtype){
+ nerror(n, "%V is a type and cannot be a self argument", n);
+ n = mkn(Onothing, nil, nil);
+ n->src = fn->src;
+ n->ty = t->ids->ty;
+ }
+ args = mkn(Oseq, n, args);
+ args->src = n->src;
+ return args;
+}
+
+static int
+mem(Type *t, Decl *d)
+{
+ for( ; d != nil; d = d->next)
+ if(d->ty == t) /* was if(d->ty == t || tequal(d->ty, t)) */
+ return 1;
+ return 0;
+}
+
+static int
+memp(Type *t, Decl *f)
+{
+ return mem(t, f->ty->polys) || mem(t, encpolys(f));
+}
+
+static void
+passfns0(Src *src, Decl *fn, Node *args0, Node **args, Node **a, Tpair *tp, Decl *polys)
+{
+ Decl *id, *idt, *idf, *dot;
+ Type *tt;
+ Sym *sym;
+ Node *n, *na, *mod;
+ Tpair *p;
+
+if(debug['w']){
+ print("polys: ");
+ for(id=polys; id!=nil; id=id->next) print("%s ", id->sym->name);
+ print("\nmap: ");
+ for(p=tp; p!=nil; p=p->nxt) print("%T -> %T ", p->t1, p->t2);
+ print("\n");
+}
+ for(idt = polys; idt != nil; idt = idt->next){
+ tt = valtmap(idt->ty, tp);
+ if(tt->kind == Tpoly && fndec != nil && !memp(tt, fndec))
+ error(src->start, "cannot determine the instantiated type of %T", tt);
+ for(idf = idt->ty->ids; idf != nil; idf = idf->next){
+ sym = idf->sym;
+ id = fnlookup(sym, tt, &mod);
+ while(id != nil && id->link != nil)
+ id = id->link;
+if(debug['v']) print("fnlookup: %p\n", id);
+ if(id == nil) /* error flagged already */
+ continue;
+ id->refs++;
+ id->caninline = -1;
+ if(tt->kind == Tmodule){ /* mod an actual parameter */
+ for(;;){
+ if(args0 != nil && tequal(tt, args0->left->ty)){
+ mod = args0->left;
+ break;
+ }
+ if(args0 != nil)
+ args0 = args0->right;
+ }
+ }
+ if(mod == nil && (dot = module(id)) != nil && !isimpmod(dot->sym))
+ error(src->start, "cannot use %s without importing %s from a variable", id->sym->name, id->dot->sym->name);
+
+if(debug['U']) print("fp: %s %s %s\n", fn->sym->name, mod ? mod->decl->sym->name : "nil", id->sym->name);
+ n = mkn(Ofnptr, mod, mkdeclname(src, id));
+ n->src = *src;
+ n->decl = fn;
+ if(tt->kind == Tpoly)
+ n->flags = FNPTRA;
+ else
+ n->flags = 0;
+ na = mkn(Oseq, n, nil);
+ if(*a == nil)
+ *args = na;
+ else
+ (*a)->right = na;
+
+ n = mkn(Ofnptr, mod, mkdeclname(src, id));
+ n->src = *src;
+ n->decl = fn;
+ if(tt->kind == Tpoly)
+ n->flags = FNPTRA|FNPTR2;
+ else
+ n->flags = FNPTR2;
+ *a = na->right = mkn(Oseq, n, nil);
+ }
+ if(args0 != nil)
+ args0 = args0->right;
+ }
+}
+
+Node*
+passfns(Src *src, Decl *fn, Node *left, Node *args, Type *adt, Tpair *tp)
+{
+ Node *a, *args0;
+
+ a = nil;
+ args0 = args;
+ if(args != nil)
+ for(a = args; a->right != nil; a = a->right)
+ ;
+ passfns0(src, fn, args0, &args, &a, tp, ispoly(fn) ? fn->ty->polys : left->ty->tof->polys);
+ if(adt != nil)
+ passfns0(src, fn, args0, &args, &a, adt->u.tmap, ispoly(fn) ? encpolys(fn) : nil);
+ return args;
+}
+
+/*
+ * check the types for a function with a variable number of arguments
+ * last typed argument must be a constant string, and must use the
+ * print format for describing arguments.
+ */
+Type*
+mkvarargs(Node *n, Node *args)
+{
+ Node *s, *a;
+ Decl *f, *last, *va;
+ Type *nt;
+
+ nt = copytypeids(n->ty);
+ n->ty = nt;
+ f = n->ty->ids;
+ last = nil;
+ if(f == nil){
+ nerror(n, "%V's type is illegal", n);
+ return nt;
+ }
+ s = args;
+ for(a = args; a != nil; a = a->right){
+ if(f == nil)
+ break;
+ if(!tcompat(f->ty, a->left->ty, 0)){
+ nerror(n, "%V: argument type mismatch: expected %T saw %Q",
+ n, f->ty, a->left);
+ return nt;
+ }
+ if(a->left->ty == tany)
+ a->left->ty = f->ty;
+ last = f;
+ f = f->next;
+ s = a;
+ }
+ if(f != nil){
+ nerror(n, "%V: too few function arguments", n);
+ return nt;
+ }
+
+ s->left = fold(s->left);
+ s = s->left;
+ if(s->ty != tstring || s->op != Oconst){
+ nerror(args, "%V: format argument %Q is not a string constant", n, s);
+ return nt;
+ }
+ fmtcheck(n, s, a);
+ va = tuplefields(a);
+ if(last == nil)
+ nt->ids = va;
+ else
+ last->next = va;
+ return nt;
+}
+
+/*
+ * check that a print style format string matches it's arguments
+ */
+void
+fmtcheck(Node *f, Node *fmtarg, Node *va)
+{
+ Sym *fmt;
+ Rune r;
+ char *s, flags[10];
+ int i, c, n1, n2, dot, verb, flag, ns, lens, fmtstart;
+ Type *ty;
+
+ fmt = fmtarg->decl->sym;
+ s = fmt->name;
+ lens = fmt->len;
+ ns = 0;
+ while(ns < lens){
+ c = s[ns++];
+ if(c != '%')
+ continue;
+
+ verb = -1;
+ n1 = 0;
+ n2 = 0;
+ dot = 0;
+ flag = 0;
+ fmtstart = ns - 1;
+ while(ns < lens && verb < 0){
+ c = s[ns++];
+ switch(c){
+ default:
+ chartorune(&r, &s[ns-1]);
+ nerror(f, "%V: invalid character %C in format '%.*s'", f, r, ns-fmtstart, &s[fmtstart]);
+ return;
+ case '.':
+ if(dot){
+ nerror(f, "%V: invalid format '%.*s'", f, ns-fmtstart, &s[fmtstart]);
+ return;
+ }
+ n1 = 1;
+ dot = 1;
+ continue;
+ case '*':
+ if(!n1)
+ n1 = 1;
+ else if(!n2 && dot)
+ n2 = 1;
+ else{
+ nerror(f, "%V: invalid format '%.*s'", f, ns-fmtstart, &s[fmtstart]);
+ return;
+ }
+ if(va == nil){
+ nerror(f, "%V: too few arguments for format '%.*s'",
+ f, ns-fmtstart, &s[fmtstart]);
+ return;
+ }
+ if(va->left->ty->kind != Tint){
+ nerror(f, "%V: format '%.*s' incompatible with argument %Q",
+ f, ns-fmtstart, &s[fmtstart], va->left);
+ return;
+ }
+ va = va->right;
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ while(ns < lens && s[ns] >= '0' && s[ns] <= '9')
+ ns++;
+ if(!n1)
+ n1 = 1;
+ else if(!n2 && dot)
+ n2 = 1;
+ else{
+ nerror(f, "%V: invalid format '%.*s'", f, ns-fmtstart, &s[fmtstart]);
+ return;
+ }
+ break;
+ case '+':
+ case '-':
+ case '#':
+ case ',':
+ case 'b':
+ case 'u':
+ for(i = 0; i < flag; i++){
+ if(flags[i] == c){
+ nerror(f, "%V: duplicate flag %c in format '%.*s'",
+ f, c, ns-fmtstart, &s[fmtstart]);
+ return;
+ }
+ }
+ flags[flag++] = c;
+ if(flag >= sizeof flags){
+ nerror(f, "too many flags in format '%.*s'", ns-fmtstart, &s[fmtstart]);
+ return;
+ }
+ break;
+ case '%':
+ case 'r':
+ verb = Tnone;
+ break;
+ case 'H':
+ verb = Tany;
+ break;
+ case 'c':
+ verb = Tint;
+ break;
+ case 'd':
+ case 'o':
+ case 'x':
+ case 'X':
+ verb = Tint;
+ for(i = 0; i < flag; i++){
+ if(flags[i] == 'b'){
+ verb = Tbig;
+ break;
+ }
+ }
+ break;
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'E':
+ case 'G':
+ verb = Treal;
+ break;
+ case 's':
+ case 'q':
+ verb = Tstring;
+ break;
+ }
+ }
+ if(verb != Tnone){
+ if(verb < 0){
+ nerror(f, "%V: incomplete format '%.*s'", f, ns-fmtstart, &s[fmtstart]);
+ return;
+ }
+ if(va == nil){
+ nerror(f, "%V: too few arguments for format '%.*s'", f, ns-fmtstart, &s[fmtstart]);
+ return;
+ }
+ ty = va->left->ty;
+ if(ty->kind == Texception)
+ ty = mkextuptype(ty);
+ switch(verb){
+ case Tint:
+ switch(ty->kind){
+ case Tstring:
+ case Tarray:
+ case Tref:
+ case Tchan:
+ case Tlist:
+ case Tmodule:
+ if(c == 'x' || c == 'X')
+ verb = ty->kind;
+ break;
+ }
+ break;
+ case Tany:
+ if(tattr[ty->kind].isptr)
+ verb = ty->kind;
+ break;
+ }
+ if(verb != ty->kind){
+ nerror(f, "%V: format '%.*s' incompatible with argument %Q", f, ns-fmtstart, &s[fmtstart], va->left);
+ return;
+ }
+ va = va->right;
+ }
+ }
+ if(va != nil)
+ nerror(f, "%V: more arguments than formats", f);
+}
+
+Decl*
+tuplefields(Node *n)
+{
+ Decl *d, *h, **last;
+
+ h = nil;
+ last = &h;
+ for(; n != nil; n = n->right){
+ d = mkdecl(&n->left->src, Dfield, n->left->ty);
+ *last = d;
+ last = &d->next;
+ }
+ return h;
+}
+
+/*
+ * make explicit indices for every element in an array initializer
+ * return the maximum index
+ * sort the indices and check for duplicates
+ */
+int
+assignindices(Node *ar)
+{
+ Node *wild, *off, *size, *inits, *n, *q;
+ Type *t;
+ Case *c;
+ int amax, max, last, nlab, ok;
+
+ amax = 0x7fffffff;
+ size = dupn(0, &nosrc, ar->left);
+ if(size->ty == tint){
+ size = fold(size);
+ if(size->op == Oconst)
+ amax = size->val;
+ }
+
+ inits = ar->right;
+ max = -1;
+ last = -1;
+ t = inits->left->ty;
+ wild = nil;
+ nlab = 0;
+ ok = 1;
+ for(n = inits; n != nil; n = n->right){
+ if(!tcompat(t, n->left->ty, 0)){
+ t = tparent(t, n->left->ty);
+ if(!tcompat(t, n->left->ty, 0)){
+ nerror(n->left, "inconsistent types %T and %T and in array initializer", t, n->left->ty);
+ return -1;
+ }
+ else
+ inits->left->ty = t;
+ }
+ if(t == tany)
+ t = n->left->ty;
+
+ /*
+ * make up an index if there isn't one
+ */
+ if(n->left->left == nil)
+ n->left->left = mkn(Oseq, mkconst(&n->left->right->src, last + 1), nil);
+
+ for(q = n->left->left; q != nil; q = q->right){
+ off = q->left;
+ if(off->ty != tint){
+ nerror(off, "array index %Q is not an int", off);
+ ok = 0;
+ continue;
+ }
+ off = fold(off);
+ switch(off->op){
+ case Owild:
+ if(wild != nil)
+ nerror(off, "array index * duplicated on line %L", wild->src.start);
+ wild = off;
+ continue;
+ case Orange:
+ if(off->left->op != Oconst || off->right->op != Oconst){
+ nerror(off, "range %V is not constant", off);
+ off = nil;
+ }else if(off->left->val < 0 || off->right->val >= amax){
+ nerror(off, "array index %V out of bounds", off);
+ off = nil;
+ }else
+ last = off->right->val;
+ break;
+ case Oconst:
+ last = off->val;
+ if(off->val < 0 || off->val >= amax){
+ nerror(off, "array index %V out of bounds", off);
+ off = nil;
+ }
+ break;
+ case Onothing:
+ /* get here from a syntax error */
+ off = nil;
+ break;
+ default:
+ nerror(off, "array index %V is not constant", off);
+ off = nil;
+ break;
+ }
+
+ nlab++;
+ if(off == nil){
+ off = mkconst(&n->left->right->src, last);
+ ok = 0;
+ }
+ if(last > max)
+ max = last;
+ q->left = off;
+ }
+ }
+
+ /*
+ * fix up types of nil elements
+ */
+ for(n = inits; n != nil; n = n->right)
+ if(n->left->ty == tany)
+ n->left->ty = t;
+
+ if(!ok)
+ return -1;
+
+
+ c = checklabels(inits, tint, nlab, "array index");
+ t = mktype(&inits->src.start, &inits->src.stop, Tainit, nil, nil);
+ inits->ty = t;
+ t->cse = c;
+
+ return max + 1;
+}
+
+/*
+ * check the labels of a case statment
+ */
+void
+casecheck(Node *cn, Type *ret)
+{
+ Node *n, *q, *wild, *left, *arg;
+ Type *t;
+ Case *c;
+ Ok rok;
+ int nlab, ok, op;
+
+ rok = echeck(cn->left, 0, 0, nil);
+ cn->right = scheck(cn->right, ret, Sother);
+ if(!rok.ok)
+ return;
+ arg = cn->left;
+
+ t = arg->ty;
+ if(t != tint && t != tbig && t != tstring){
+ nerror(cn, "case argument %Q is not an int or big or string", arg);
+ return;
+ }
+
+ wild = nil;
+ nlab= 0;
+ ok = 1;
+ for(n = cn->right; n != nil; n = n->right){
+ q = n->left->left;
+ if(n->left->right->right == nil)
+ nwarn(q, "no body for case qualifier %V", q);
+ for(; q != nil; q = q->right){
+ left = fold(q->left);
+ q->left = left;
+ switch(left->op){
+ case Owild:
+ if(wild != nil)
+ nerror(left, "case qualifier * duplicated on line %L", wild->src.start);
+ wild = left;
+ break;
+ case Orange:
+ if(left->ty != t)
+ nerror(left, "case qualifier %Q clashes with %Q", left, arg);
+ else if(left->left->op != Oconst || left->right->op != Oconst){
+ nerror(left, "case range %V is not constant", left);
+ ok = 0;
+ }
+ nlab++;
+ break;
+ default:
+ if(left->ty != t){
+ nerror(left, "case qualifier %Q clashes with %Q", left, arg);
+ ok = 0;
+ }else if(left->op != Oconst){
+ nerror(left, "case qualifier %V is not constant", left);
+ ok = 0;
+ }
+ nlab++;
+ break;
+ }
+ }
+ }
+
+ if(!ok)
+ return;
+
+ c = checklabels(cn->right, t, nlab, "case qualifier");
+ op = Tcase;
+ if(t == tbig)
+ op = Tcasel;
+ else if(t == tstring)
+ op = Tcasec;
+ t = mktype(&cn->src.start, &cn->src.stop, op, nil, nil);
+ cn->ty = t;
+ t->cse = c;
+}
+
+/*
+ * check the labels and bodies of a pick statment
+ */
+void
+pickcheck(Node *n, Type *ret)
+{
+ Node *w, *arg, *qs, *q, *qt, *left, **tags;
+ Decl *id, *d;
+ Type *t, *argty;
+ Case *c;
+ Ok rok;
+ int ok, nlab;
+
+ arg = n->left->right;
+ rok = echeck(arg, 0, 0, nil);
+ if(!rok.allok)
+ return;
+ t = arg->ty;
+ if(t->kind == Tref)
+ t = t->tof;
+ if(arg->ty->kind != Tref || t->kind != Tadt || t->tags == nil){
+ nerror(arg, "pick argument %Q is not a ref adt with pick tags", arg);
+ return;
+ }
+ argty = usetype(mktype(&arg->ty->src.start, &arg->ty->src.stop, Tref, t, nil));
+
+ arg = n->left->left;
+ pushscope(nil, Sother);
+ dasdecl(arg);
+ arg->decl->ty = argty;
+ arg->ty = argty;
+
+ tags = allocmem(t->decl->tag * sizeof *tags);
+ memset(tags, 0, t->decl->tag * sizeof *tags);
+ w = nil;
+ ok = 1;
+ nlab = 0;
+ for(qs = n->right; qs != nil; qs = qs->right){
+ qt = nil;
+ for(q = qs->left->left; q != nil; q = q->right){
+ left = q->left;
+ switch(left->op){
+ case Owild:
+ /* left->ty = tnone; */
+ left->ty = t;
+ if(w != nil)
+ nerror(left, "pick qualifier * duplicated on line %L", w->src.start);
+ w = left;
+ break;
+ case Oname:
+ id = namedot(t->tags, left->decl->sym);
+ if(id == nil){
+ nerror(left, "pick qualifier %V is not a member of %Q", left, arg);
+ ok = 0;
+ continue;
+ }
+
+ left->decl = id;
+ left->ty = id->ty;
+
+ if(tags[id->tag] != nil){
+ nerror(left, "pick qualifier %V duplicated on line %L",
+ left, tags[id->tag]->src.start);
+ ok = 0;
+ }
+ tags[id->tag] = left;
+ nlab++;
+ break;
+ default:
+ fatal("pickcheck can't handle %n", q);
+ break;
+ }
+
+ if(qt == nil)
+ qt = left;
+ else if(!tequal(qt->ty, left->ty))
+ nerror(left, "type clash in pick qualifiers %Q and %Q", qt, left);
+ }
+
+ argty->tof = t;
+ if(qt != nil)
+ argty->tof = qt->ty;
+ qs->left->right = scheck(qs->left->right, ret, Sother);
+ if(qs->left->right == nil)
+ nwarn(qs->left->left, "no body for pick qualifier %V", qs->left->left);
+ }
+ argty->tof = t;
+ for(qs = n->right; qs != nil; qs = qs->right)
+ for(q = qs->left->left; q != nil; q = q->right)
+ q->left = fold(q->left);
+
+ d = popscope();
+ d->refs++;
+ if(d->next != nil)
+ fatal("pickcheck: installing more than one id");
+ fndecls = appdecls(fndecls, d);
+
+ if(!ok)
+ return;
+
+ c = checklabels(n->right, tint, nlab, "pick qualifier");
+ t = mktype(&n->src.start, &n->src.stop, Tcase, nil, nil);
+ n->ty = t;
+ t->cse = c;
+}
+
+void
+exccheck(Node *en, Type *ret)
+{
+ Decl *ed;
+ Node *n, *q, *wild, *left, *oinexcept;
+ Type *t, *qt;
+ Case *c;
+ int nlab, ok;
+ Ok rok;
+ char buf[32];
+ static int nexc;
+
+ pushscope(nil, Sother);
+ if(en->left == nil){
+ seprint(buf, buf+sizeof(buf), ".ex%d", nexc++);
+ en->left = mkdeclname(&en->src, mkids(&en->src, enter(buf, 0), texception, nil));
+ }
+ oinexcept = inexcept;
+ inexcept = en->left;
+ dasdecl(en->left);
+ en->left->ty = en->left->decl->ty = texception;
+ ed = en->left->decl;
+ /* en->right = scheck(en->right, ret, Sother); */
+ t = tstring;
+ wild = nil;
+ nlab = 0;
+ ok = 1;
+ for(n = en->right; n != nil; n = n->right){
+ qt = nil;
+ for(q = n->left->left; q != nil; q = q->right){
+ left = q->left;
+ switch(left->op){
+ case Owild:
+ left->ty = texception;
+ if(wild != nil)
+ nerror(left, "exception qualifier * duplicated on line %L", wild->src.start);
+ wild = left;
+ break;
+ case Orange:
+ left->ty = tnone;
+ nerror(left, "exception qualifier %V is illegal", left);
+ ok = 0;
+ break;
+ default:
+ rok = echeck(left, 0, 0, nil);
+ if(!rok.ok){
+ ok = 0;
+ break;
+ }
+ left = q->left = fold(left);
+ if(left->ty != t && left->ty->kind != Texception){
+ nerror(left, "exception qualifier %Q is not a string or exception", left);
+ ok = 0;
+ }else if(left->op != Oconst){
+ nerror(left, "exception qualifier %V is not constant", left);
+ ok = 0;
+ }
+ else if(left->ty != t)
+ left->ty = mkextype(left->ty);
+ nlab++;
+ break;
+ }
+
+ if(qt == nil)
+ qt = left->ty;
+ else if(!tequal(qt, left->ty))
+ qt = texception;
+ }
+
+ if(qt != nil)
+ ed->ty = qt;
+ n->left->right = scheck(n->left->right, ret, Sother);
+ if(n->left->right->right == nil)
+ nwarn(n->left->left, "no body for exception qualifier %V", n->left->left);
+ }
+ ed->ty = texception;
+ inexcept = oinexcept;
+ if(!ok)
+ return;
+ c = checklabels(en->right, texception, nlab, "exception qualifier");
+ t = mktype(&en->src.start, &en->src.stop, Texcept, nil, nil);
+ en->ty = t;
+ t->cse = c;
+ ed = popscope();
+ fndecls = appdecls(fndecls, ed);
+}
+
+/*
+ * check array and case labels for validity
+ */
+Case *
+checklabels(Node *inits, Type *ctype, int nlab, char *title)
+{
+ Node *n, *p, *q, *wild;
+ Label *labs, *aux;
+ Case *c;
+ char buf[256], buf1[256];
+ int i, e;
+
+ labs = allocmem(nlab * sizeof *labs);
+ i = 0;
+ wild = nil;
+ for(n = inits; n != nil; n = n->right){
+ for(q = n->left->left; q != nil; q = q->right){
+ switch(q->left->op){
+ case Oconst:
+ labs[i].start = q->left;
+ labs[i].stop = q->left;
+ labs[i++].node = n->left;
+ break;
+ case Orange:
+ labs[i].start = q->left->left;
+ labs[i].stop = q->left->right;
+ labs[i++].node = n->left;
+ break;
+ case Owild:
+ wild = n->left;
+ break;
+ default:
+ fatal("bogus index in checklabels");
+ break;
+ }
+ }
+ }
+
+ if(i != nlab)
+ fatal("bad label count: %d then %d", nlab, i);
+
+ aux = allocmem(nlab * sizeof *aux);
+ casesort(ctype, aux, labs, 0, nlab);
+ for(i = 0; i < nlab; i++){
+ p = labs[i].stop;
+ if(casecmp(ctype, labs[i].start, p) > 0)
+ nerror(labs[i].start, "unmatchable %s %V", title, labs[i].node);
+ for(e = i + 1; e < nlab; e++){
+ if(casecmp(ctype, labs[e].start, p) <= 0){
+ eprintlist(buf, buf+sizeof(buf), labs[e].node->left, " or ");
+ eprintlist(buf1, buf1+sizeof(buf1), labs[e-1].node->left, " or ");
+ nerror(labs[e].start,"%s '%s' overlaps with '%s' on line %L",
+ title, buf, buf1, p->src.start);
+ }
+
+ /*
+ * check for merging case labels
+ */
+ if(ctype != tint
+ || labs[e].start->val != p->val+1
+ || labs[e].node != labs[i].node)
+ break;
+ p = labs[e].stop;
+ }
+ if(e != i + 1){
+ labs[i].stop = p;
+ memmove(&labs[i+1], &labs[e], (nlab-e) * sizeof *labs);
+ nlab -= e - (i + 1);
+ }
+ }
+ free(aux);
+
+ c = allocmem(sizeof *c);
+ c->nlab = nlab;
+ c->nsnd = 0;
+ c->labs = labs;
+ c->wild = wild;
+
+ return c;
+}
+
+static int
+matchcmp(Node *na, Node *nb)
+{
+ Sym *a, *b;
+ int sa, sb;
+
+ a = na->decl->sym;
+ b = nb->decl->sym;
+ sa = a->len > 0 && a->name[a->len-1] == '*';
+ sb = b->len > 0 && b->name[b->len-1] == '*';
+ if(sa){
+ if(sb){
+ if(a->len == b->len)
+ return symcmp(a, b);
+ return b->len-a->len;
+ }
+ else
+ return 1;
+ }
+ else{
+ if(sb)
+ return -1;
+ else{
+ if(na->ty == tstring){
+ if(nb->ty == tstring)
+ return symcmp(a, b);
+ else
+ return 1;
+ }
+ else{
+ if(nb->ty == tstring)
+ return -1;
+ else
+ return symcmp(a, b);
+ }
+ }
+ }
+}
+
+int
+casecmp(Type *ty, Node *a, Node *b)
+{
+ if(ty == tint || ty == tbig){
+ if(a->val < b->val)
+ return -1;
+ if(a->val > b->val)
+ return 1;
+ return 0;
+ }
+ if(ty == texception)
+ return matchcmp(a, b);
+ return symcmp(a->decl->sym, b->decl->sym);
+}
+
+void
+casesort(Type *t, Label *aux, Label *labs, int start, int stop)
+{
+ int n, top, mid, base;
+
+ n = stop - start;
+ if(n <= 1)
+ return;
+ top = mid = start + n / 2;
+
+ casesort(t, aux, labs, start, top);
+ casesort(t, aux, labs, mid, stop);
+
+ /*
+ * merge together two sorted label arrays, yielding a sorted array
+ */
+ n = 0;
+ base = start;
+ while(base < top && mid < stop){
+ if(casecmp(t, labs[base].start, labs[mid].start) <= 0)
+ aux[n++] = labs[base++];
+ else
+ aux[n++] = labs[mid++];
+ }
+ if(base < top)
+ memmove(&aux[n], &labs[base], (top-base) * sizeof *aux);
+ else if(mid < stop)
+ memmove(&aux[n], &labs[mid], (stop-mid) * sizeof *aux);
+ memmove(&labs[start], &aux[0], (stop-start) * sizeof *labs);
+}
+
+/*
+ * binary search for the label corresponding to a given value
+ */
+int
+findlab(Type *ty, Node *v, Label *labs, int nlab)
+{
+ int l, r, m;
+
+ if(nlab <= 1)
+ return 0;
+ l = 1;
+ r = nlab - 1;
+ while(l <= r){
+ m = (r + l) / 2;
+ if(casecmp(ty, labs[m].start, v) <= 0)
+ l = m + 1;
+ else
+ r = m - 1;
+ }
+ m = l - 1;
+ if(casecmp(ty, labs[m].start, v) > 0
+ || casecmp(ty, labs[m].stop, v) < 0)
+ fatal("findlab out of range");
+ return m;
+}
+
+void
+altcheck(Node *an, Type *ret)
+{
+ Node *n, *q, *left, *op, *wild;
+ Case *c;
+ int ok, nsnd, nrcv;
+
+ an->left = scheck(an->left, ret, Sother);
+
+ ok = 1;
+ nsnd = 0;
+ nrcv = 0;
+ wild = nil;
+ for(n = an->left; n != nil; n = n->right){
+ q = n->left->right->left;
+ if(n->left->right->right == nil)
+ nwarn(q, "no body for alt guard %V", q);
+ for(; q != nil; q = q->right){
+ left = q->left;
+ switch(left->op){
+ case Owild:
+ if(wild != nil)
+ nerror(left, "alt guard * duplicated on line %L", wild->src.start);
+ wild = left;
+ break;
+ case Orange:
+ nerror(left, "alt guard %V is illegal", left);
+ ok = 0;
+ break;
+ default:
+ op = hascomm(left);
+ if(op == nil){
+ nerror(left, "alt guard %V has no communication", left);
+ ok = 0;
+ break;
+ }
+ if(op->op == Osnd)
+ nsnd++;
+ else
+ nrcv++;
+ break;
+ }
+ }
+ }
+
+ if(!ok)
+ return;
+
+ c = allocmem(sizeof *c);
+ c->nlab = nsnd + nrcv;
+ c->nsnd = nsnd;
+ c->wild = wild;
+
+ an->ty = mktalt(c);
+}
+
+Node*
+hascomm(Node *n)
+{
+ Node *r;
+
+ if(n == nil)
+ return nil;
+ if(n->op == Osnd || n->op == Orcv)
+ return n;
+ r = hascomm(n->left);
+ if(r != nil)
+ return r;
+ return hascomm(n->right);
+}
+
+void
+raisescheck(Type *t)
+{
+ Node *n, *nn;
+ Ok ok;
+
+ if(t->kind != Tfn)
+ return;
+ n = t->u.eraises;
+ for(nn = n->left; nn != nil; nn = nn->right){
+ ok = echeck(nn->left, 0, 0, nil);
+ if(ok.ok && nn->left->ty->kind != Texception)
+ nerror(n, "%V: illegal raises expression", nn->left);
+ }
+}
+
+typedef struct Elist Elist;
+
+struct Elist{
+ Decl *d;
+ Elist *nxt;
+};
+
+static Elist*
+emerge(Elist *el1, Elist *el2)
+{
+ int f;
+ Elist *el, *nxt;
+
+ for( ; el1 != nil; el1 = nxt){
+ f = 0;
+ for(el = el2; el != nil; el = el->nxt){
+ if(el1->d == el->d){
+ f = 1;
+ break;
+ }
+ }
+ nxt = el1->nxt;
+ if(!f){
+ el1->nxt = el2;
+ el2 = el1;
+ }
+ }
+ return el2;
+}
+
+static Elist*
+equals(Node *n)
+{
+ Node *q, *nn;
+ Elist *e, *el;
+
+ el = nil;
+ for(q = n->left->left; q != nil; q = q->right){
+ nn = q->left;
+ if(nn->op == Owild)
+ return nil;
+ if(nn->ty->kind != Texception)
+ continue;
+ e = (Elist*)malloc(sizeof(Elist));
+ e->d = nn->decl;
+ e->nxt = el;
+ el = e;
+ }
+ return el;
+}
+
+static int
+caught(Decl *d, Node *n)
+{
+ Node *q, *nn;
+
+ for(n = n->right; n != nil; n = n->right){
+ for(q = n->left->left; q != nil; q = q->right){
+ nn = q->left;
+ if(nn->op == Owild)
+ return 1;
+ if(nn->ty->kind != Texception)
+ continue;
+ if(d == nn->decl)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static Elist*
+raisecheck(Node *n, Elist *ql)
+{
+ int exc;
+ Node *e;
+ Elist *el, *nel, *nxt;
+
+ if(n == nil)
+ return nil;
+ el = nil;
+ for(; n != nil; n = n->right){
+ switch(n->op){
+ case Oscope:
+ return raisecheck(n->right, ql);
+ case Olabel:
+ case Odo:
+ return raisecheck(n->right, ql);
+ case Oif:
+ case Ofor:
+ return emerge(raisecheck(n->right->left, ql),
+ raisecheck(n->right->right, ql));
+ case Oalt:
+ case Ocase:
+ case Opick:
+ case Oexcept:
+ exc = n->op == Oexcept;
+ for(n = n->right; n != nil; n = n->right){
+ ql = nil;
+ if(exc)
+ ql = equals(n);
+ el = emerge(raisecheck(n->left->right, ql), el);
+ }
+ return el;
+ case Oseq:
+ el = emerge(raisecheck(n->left, ql), el);
+ break;
+ case Oexstmt:
+ el = raisecheck(n->left, ql);
+ nel = nil;
+ for( ; el != nil; el = nxt){
+ nxt = el->nxt;
+ if(!caught(el->d, n->right)){
+ el->nxt = nel;
+ nel = el;
+ }
+ }
+ return emerge(nel, raisecheck(n->right, ql));
+ case Oraise:
+ e = n->left;
+ if(e->ty && e->ty->kind == Texception){
+ if(!e->ty->cons)
+ return ql;
+ if(e->op == Ocall)
+ e = e->left;
+ if(e->op == Omdot)
+ e = e->right;
+ if(e->op != Oname)
+ fatal("exception %n not a name", e);
+ el = (Elist*)malloc(sizeof(Elist));
+ el->d = e->decl;
+ el->nxt = nil;
+ return el;
+ }
+ return nil;
+ default:
+ return nil;
+ }
+ }
+ return el;
+}
+
+void
+checkraises(Node *n)
+{
+ int f;
+ Decl *d;
+ Elist *e, *el;
+ Node *es, *nn;
+
+ el = raisecheck(n->right, nil);
+ es = n->ty->u.eraises;
+ if(es != nil){
+ for(nn = es->left; nn != nil; nn = nn->right){
+ d = nn->left->decl;
+ f = 0;
+ for(e = el; e != nil; e = e->nxt){
+ if(d == e->d){
+ f = 1;
+ e->d = nil;
+ break;
+ }
+ }
+ if(!f)
+ nwarn(n, "function %V does not raise %s but declared", n->left, d->sym->name);
+ }
+ }
+ for(e = el; e != nil; e = e->nxt)
+ if(e->d != nil)
+ nwarn(n, "function %V raises %s but not declared", n->left, e->d->sym->name);
+}
+
+/* sort all globals in modules now that we've finished with 'last' pointers
+ * and before any code generation
+ */
+void
+gsort(Node *n)
+{
+ for(;;){
+ if(n == nil)
+ return;
+ if(n->op != Oseq)
+ break;
+ gsort(n->left);
+ n = n->right;
+ }
+ if(n->op == Omoddecl && n->ty->ok & OKverify){
+ n->ty->ids = namesort(n->ty->ids);
+ sizeids(n->ty->ids, 0);
+ }
+}