summaryrefslogtreecommitdiff
path: root/appl/cmd/limbo/typecheck.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/limbo/typecheck.b')
-rw-r--r--appl/cmd/limbo/typecheck.b3223
1 files changed, 3223 insertions, 0 deletions
diff --git a/appl/cmd/limbo/typecheck.b b/appl/cmd/limbo/typecheck.b
new file mode 100644
index 00000000..fc0d43e4
--- /dev/null
+++ b/appl/cmd/limbo/typecheck.b
@@ -0,0 +1,3223 @@
+fndecls: ref Decl;
+labstack: array of ref Node;
+maxlabdep: int;
+inexcept: ref Node;
+nexc: int;
+fndec: ref Decl;
+
+increfs(id: ref Decl)
+{
+ for( ; id != nil; id = id.link)
+ id.refs++;
+}
+
+fninline(d: ref Decl): int
+{
+ left, right: ref Node;
+
+ n := d.init;
+ if(dontinline || d.inline < byte 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;
+ #
+ case(n.op){
+ Oret =>
+ break;
+ 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;
+ 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;
+ * =>
+ 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;
+}
+
+rewind(n: ref Node)
+{
+ r, nn: ref Node;
+
+ 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);
+ }
+}
+
+ckmod(n: ref Node, id: ref Decl)
+{
+ t: ref Type;
+ d, idc: ref Decl;
+ mod: ref Node;
+
+ if(id == nil)
+ fatal("can't find function: " + nodeconv(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 " + expconv(n) + " as a function reference");
+ return;
+ }
+ if(mod != nil){
+ if(valistype(mod)){
+ nerror(n, "cannot use " + expconv(n) + " as a function reference because " + expconv(mod) + " is a module interface");
+ return;
+ }
+ }else if(idc != nil && idc.dot != nil && !isimpmod(idc.dot.sym)){
+ nerror(n, "cannot use " + expconv(n) + " without importing " + idc.sym.name + " from a variable");
+ return;
+ }
+ if(mod != nil)
+ modrefable(n.ty);
+}
+
+addref(n: ref Node)
+{
+ nn: ref Node;
+
+ 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));
+}
+
+fnref(n: ref Node, id: ref Decl)
+{
+ id.inline = byte -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 " + id.sym.name);
+}
+
+typecheck(checkimp: int): ref Decl
+{
+ entry, d, m: ref Decl;
+
+ if(errors)
+ return nil;
+
+ #
+ # generate the set of all functions
+ # compile one function at a time
+ #
+ gdecl(tree);
+ gbind(tree);
+ fns = array[nfns] of ref 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){
+ im: ref Decl;
+ dm: ref Dlist;
+
+ 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 "+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 "+declconv(im));
+ return nil;
+ }
+ }
+
+ # now check any multiple implementations
+ impdecl = modimp(impdecls, impmods);
+
+ s := enter("init", 0);
+ 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
+#
+gdecl(n: ref Node)
+{
+ for(;;){
+ if(n == nil)
+ return;
+ if(n.op != Oseq)
+ break;
+ gdecl(n.left);
+ n = n.right;
+ }
+ case n.op{
+ Oimport =>
+ importdecled(n);
+ gdasdecl(n.right);
+ Oadtdecl =>
+ adtdecled(n);
+ Ocondecl =>
+ condecled(n);
+ gdasdecl(n.right);
+ Oexdecl =>
+ exdecled(n);
+ Omoddecl =>
+ moddecled(n);
+ Otypedecl =>
+ typedecled(n);
+ Ovardecl =>
+ vardecled(n);
+ Ovardecli =>
+ vardecled(n.left);
+ gdasdecl(n.right);
+ Ofunc =>
+ fndecled(n);
+ Oas or
+ Odas or
+ Onothing =>
+ gdasdecl(n);
+ * =>
+ fatal("can't deal with "+opconv(n.op)+" in gdecl");
+ }
+}
+
+#
+# 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
+#
+gbind(n: ref Node)
+{
+ ids: ref Decl;
+
+ for(;;){
+ if(n == nil)
+ return;
+ if(n.op != Oseq)
+ break;
+ gbind(n.left);
+ n = n.right;
+ }
+ case n.op{
+ Oas or
+ Ocondecl or
+ Odas or
+ Oexdecl or
+ Ofunc or
+ Oimport or
+ Onothing or
+ Ovardecl or
+ Ovardecli =>
+ break;
+ Ofielddecl =>
+ bindtypes(n.decl.ty);
+ Otypedecl =>
+ bindtypes(n.decl.ty);
+ if(n.left != nil)
+ gbind(n.left);
+ 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);
+ Oadtdecl or
+ 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);
+ * =>
+ fatal("can't deal with "+opconv(n.op)+" in gbind");
+ }
+}
+
+#
+# check all of the global declarations
+# bind all type ids referred to within types at the global level
+# record decls for defined functions
+#
+gcheck(n: ref Node, fns: array of ref Decl, nfns: int): int
+{
+ ok, allok: int;
+
+ for(;;){
+ if(n == nil)
+ return nfns;
+ if(n.op != Oseq)
+ break;
+ nfns = gcheck(n.left, fns, nfns);
+ n = n.right;
+ }
+
+ case n.op{
+ Ofielddecl =>
+ if(n.decl.ty.eraises != nil)
+ raisescheck(n.decl.ty);
+ Onothing or
+ Opickdecl =>
+ break;
+ Otypedecl =>
+ tcycle(n.ty);
+ Oadtdecl or
+ 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);
+ Ovardecl =>
+ varcheck(n, 1);
+ Ocondecl =>
+ concheck(n, 1);
+ Oexdecl =>
+ excheck(n, 1);
+ Oimport =>
+ importcheck(n, 1);
+ Ovardecli =>
+ varcheck(n.left, 1);
+ (ok, allok) = echeck(n.right, 0, 1, nil);
+ if(ok){
+ if(allok)
+ n.right = fold(n.right);
+ globalas(n.right.left, n.right.right, allok);
+ }
+ Oas or
+ Odas =>
+ (ok, allok) = echeck(n, 0, 1, nil);
+ if(ok){
+ if(allok)
+ n = fold(n);
+ globalas(n.left, n.right, allok);
+ }
+ Ofunc =>
+ (ok, allok) = echeck(n.left, 0, 1, n);
+ if(ok && n.ty.eraises != nil)
+ raisescheck(n.ty);
+ d : ref Decl = nil;
+ if(ok)
+ d = fnchk(n);
+ fns[nfns++] = d;
+ * =>
+ fatal("can't deal with "+opconv(n.op)+" in gcheck");
+ }
+ return nfns;
+}
+
+#
+# check for unused expression results
+# make sure the any calculated expression has
+# a destination
+#
+checkused(n: ref Node): ref Node
+{
+ #
+ # 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 "+lineconv(n.src.start)+" checkused "+nodeconv(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 "+expconv(n)+" not called");
+ else if(t.kind == Tadt && t.tags != nil || t.kind == Tadtpick)
+ nerror(n, "expressions cannot have type "+typeconv(t));
+ else if(n.op == Otuple){
+ for(nn := n.left; nn != nil; nn = nn.right)
+ checkused(nn.left);
+ }
+ else
+ nwarn(n, "result of expression "+expconv(n)+" not used");
+ n = mkunary(Oused, n);
+ n.ty = n.left.ty;
+ return n;
+}
+
+fncheck(d: ref Decl)
+{
+ n := d.init;
+ if(debug['t'])
+ print("typecheck tree: %s\n", nodeconv(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 = array[maxlabdep] of ref Node;
+ n.right = scheck(n.right, d.ty.tof, Sother);
+ if(labdep != 0)
+ fatal("unbalanced label stack in fncheck");
+ labstack = nil;
+
+ 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.inline = byte fninline(d);
+}
+
+scheck(n: ref Node, ret: ref Type, kind : int): ref Node
+{
+ s: ref Sym;
+ rok: int;
+
+ top := n;
+ last: ref Node = nil;
+ for(; n != nil; n = n.right){
+ left := n.left;
+ right := n.right;
+ case n.op{
+ 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;
+ Ovardecli =>
+ vardecled(left);
+ varcheck(left, 0);
+ echeck(right, 0, 0, nil);
+ if (nested() && tmustzero(left.decl.ty))
+ decltozero(left);
+ return top;
+ Otypedecl =>
+ typedecled(n);
+ bindtypes(n.ty);
+ tcycle(n.ty);
+ return top;
+ Ocondecl =>
+ condecled(n);
+ concheck(n, 0);
+ return top;
+ Oexdecl =>
+ exdecled(n);
+ excheck(n, 0);
+ return top;
+ Oimport =>
+ importdecled(n);
+ importcheck(n, 0);
+ return top;
+ Ofunc =>
+ fatal("scheck func");
+ Oscope =>
+ if (kind == Sother)
+ kind = Sscope;
+ pushscope(n, 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;
+ Olabel =>
+ echeck(left, 0, 0, nil);
+ n.right = scheck(right, ret, Sother);
+ return top;
+ Oseq =>
+ n.left = scheck(left, ret, Sother);
+ # next time will check n.right
+ Oif =>
+ (rok, nil) = echeck(left, 0, 0, nil);
+ if(rok && left.op != Onothing && left.ty != tint)
+ nerror(n, "if conditional must be an int, not "+etconv(left));
+ right.left = scheck(right.left, ret, Sother);
+ # next time will check n.right.right
+ n = right;
+ Ofor =>
+ (rok, nil) = echeck(left, 0, 0, nil);
+ if(rok && left.op != Onothing && left.ty != tint)
+ nerror(n, "for conditional must be an int, not "+etconv(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 "+n.decl.sym.name+" never referenced");
+ return top;
+ Odo =>
+ (rok, nil) = echeck(left, 0, 0, nil);
+ if(rok && left.op != Onothing && left.ty != tint)
+ nerror(n, "do conditional must be an int, not "+etconv(left));
+ pushlabel(n);
+ n.right = scheck(n.right, ret, Sloop);
+ labdep--;
+ if(n.decl != nil && !n.decl.refs)
+ nwarn(n, "label "+n.decl.sym.name+" never referenced");
+ return top;
+ Oalt or
+ Ocase or
+ Opick or
+ Oexcept =>
+ pushlabel(n);
+ case n.op{
+ Oalt =>
+ altcheck(n, ret);
+ Ocase =>
+ casecheck(n, ret);
+ Opick =>
+ pickcheck(n, ret);
+ Oexcept =>
+ exccheck(n, ret);
+ }
+ labdep--;
+ if(n.decl != nil && !n.decl.refs)
+ nwarn(n, "label "+n.decl.sym.name+" never referenced");
+ return top;
+ Oret =>
+ (rok, nil) = echeck(left, 0, 0, nil);
+ if(!rok)
+ return top;
+ if(left == nil){
+ if(ret != tnone)
+ nerror(n, "return of nothing from a fn of "+typeconv(ret));
+ }else if(ret == tnone){
+ if(left.ty != tnone)
+ nerror(n, "return "+etconv(left)+" from a fn with no return type");
+ }else if(!tcompat(ret, left.ty, 0))
+ nerror(n, "return "+etconv(left)+" from a fn of "+typeconv(ret));
+ return top;
+ Obreak or
+ 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 "+expconv(n));
+ return top;
+ Oexit or
+ Onothing =>
+ return top;
+ Oexstmt =>
+ fndec.handler = byte 1;
+ n.left = scheck(left, ret, Sother);
+ n.right = scheck(right, ret, Sother);
+ return top;
+ * =>
+ (nil, rok) = echeck(n, 0, 0, nil);
+ if(rok)
+ n = checkused(n);
+ if(last == nil)
+ return n;
+ last.right = n;
+ return top;
+ }
+ last = n;
+ }
+ return top;
+}
+
+pushlabel(n: ref Node)
+{
+ s: ref Sym;
+
+ if(labdep >= maxlabdep){
+ maxlabdep += MaxScope;
+ labs := array[maxlabdep] of ref Node;
+ labs[:] = labstack;
+ labstack = labs;
+ }
+ 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.name + " duplicated on line " + lineconv(labstack[i].decl.src.start));
+ }
+ labstack[labdep++] = n;
+}
+
+varcheck(n: ref Node, isglobal: int)
+{
+ 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.eraises != nil)
+ raisescheck(t);
+}
+
+concheck(n: ref Node, isglobal: int)
+{
+ t: ref Type;
+ init: ref Node;
+
+ pushscope(nil, Sother);
+ installids(Dconst, iota);
+ (ok, allok) := echeck(n.right, 0, isglobal, nil);
+ popscope();
+
+ init = n.right;
+ if(!ok){
+ t = terror;
+ }else{
+ t = init.ty;
+ if(!tattr[t.kind].conable){
+ nerror(init, "cannot have a "+typeconv(t)+" constant");
+ allok = 0;
+ }
+ }
+
+ last := n.left.decl;
+ for(ids := n.decl; ids != last.next; ids = ids.next)
+ ids.ty = t;
+
+ if(!allok)
+ return;
+
+ i := 0;
+ for(ids = n.decl; ids != last.next; ids = ids.next){
+ if(ok){
+ iota.init.c.val = big i;
+ ids.init = dupn(0, nosrc, init);
+ if(!varcom(ids))
+ ok = 0;
+ }
+ i++;
+ }
+}
+
+exname(d: ref Decl): string
+{
+ s := "";
+ m := impmods.sym;
+ if(d.dot != nil)
+ m = d.dot.sym;
+ if(m != nil)
+ s += m.name+".";
+ if(fndec != nil)
+ s += fndec.sym.name+".";
+ s += string (scope-ScopeGlobal)+"."+d.sym.name;
+ return s;
+}
+
+excheck(n: ref Node, isglobal: int)
+{
+ t: ref Type;
+ ids, last: ref Decl;
+
+ 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;
+ ids.init = mksconst(n.src, enterstring(exname(ids)));
+ # ids.init = mksconst(n.src, enterstring(ids.sym.name));
+ }
+}
+
+importcheck(n: ref Node, isglobal: int)
+{
+ (ok, nil) := echeck(n.right, 1, isglobal, nil);
+ if(!ok)
+ return;
+
+ m := n.right;
+ if(m.ty.kind != Tmodule || m.op != Oname){
+ nerror(n, "cannot import from "+etconv(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, id.sym.name+" is not a member of "+expconv(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;
+ }
+}
+
+rewcall(n: ref Node, d: ref Decl): ref Decl
+{
+ # 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;
+}
+
+isfnrefty(t: ref Type): int
+{
+ return t.kind == Tref && t.tof.kind == Tfn;
+}
+
+isfnref(d: ref Decl): int
+{
+ case(d.store){
+ Dglobal or
+ Darg or
+ Dlocal or
+ Dfield or
+ Dimport =>
+ return isfnrefty(d.ty);
+ }
+ return 0;
+}
+
+tagopt: int;
+
+#
+# annotate the expression with types
+#
+echeck(n: ref Node, typeok, isglobal: int, par: ref Node): (int, int)
+{
+ tg, id, callee: ref Decl;
+ t, tt: ref Type;
+ ok, allok, max, nocheck, kidsok: int;
+
+ ok = allok = 1;
+ if(n == nil)
+ return (1, 1);
+
+ if(n.op == Oseq){
+ for( ; n != nil && n.op == Oseq; n = n.right){
+ (okl, allokl) := echeck(n.left, typeok == 2, isglobal, n);
+ ok &= okl;
+ allok &= allokl;
+ n.ty = tnone;
+ }
+ if(n == nil)
+ return (ok, allok);
+ }
+
+ 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, allok) = 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){
+ (okr, allokr) := echeck(right, 0, isglobal, n);
+ ok &= okr;
+ allok &= allokr;
+ }
+ if(!ok){
+ n.ty = terror;
+ return (0, 0);
+ }
+
+ case n.op{
+ Odas =>
+ (ok, allok) = echeck(right, 0, isglobal, n);
+ if(!ok)
+ right.ty = terror;
+ if(!isglobal && !dasdecl(left)){
+ ok = 0;
+ }else if(!specific(right.ty) || !declasinfer(left, right.ty)){
+ nerror(n, "cannot declare "+expconv(left)+" from "+etconv(right));
+ declaserr(left);
+ ok = 0;
+ }
+ if(right.ty.kind == Texception)
+ left.ty = n.ty = mkextuptype(right.ty);
+ else{
+ left.ty = n.ty = right.ty;
+ usedty(n.ty);
+ }
+ if (nested() && tmustzero(left.ty))
+ decltozero(left);
+ return (ok, allok & ok);
+ Oseq or
+ Onothing =>
+ n.ty = tnone;
+ Owild =>
+ n.ty = tint;
+ 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 "+typeconv(n.ty)+" from "+etconv(left));
+ return (0, 0);
+ Ochan =>
+ n.ty = usetype(n.ty);
+ if(left != nil && left.ty.kind != Tint){
+ nerror(n, "channel size "+etconv(left)+" is not an int");
+ return (0, 0);
+ }
+ Oload =>
+ n.ty = usetype(n.ty);
+ (nil, kidsok) = echeck(left, 0, isglobal, n);
+ if(n.ty.kind != Tmodule){
+ nerror(n, "cannot load a "+typeconv(n.ty));
+ return (0, 0);
+ }
+ if(!kidsok){
+ allok = 0;
+ break;
+ }
+ if(left.ty != tstring){
+ nerror(n, "cannot load a module from "+etconv(left));
+ allok = 0;
+ break;
+ }
+if(n.ty.tof.decl.refs != 0)
+n.ty.tof.decl.refs++;
+n.ty.decl.refs++;
+ usetype(n.ty.tof);
+ Oref =>
+ t = left.ty;
+ if(t.kind != Tadt && t.kind != Tadtpick && t.kind != Tfn && t.kind != Ttuple){
+ nerror(n, "cannot make a ref from "+etconv(left));
+ return (0, 0);
+ }
+ if(!tagopt && t.kind == Tadt && t.tags != nil && valistype(left)){
+ nerror(n, "instances of ref "+expconv(left)+" must be qualified with a pick tag");
+ return (0, 0);
+ }
+ if(t.kind == Tadtpick)
+ t.tof = usetype(t.tof);
+ n.ty = usetype(mktype(n.src.start, n.src.stop, Tref, t, nil));
+ Oarray =>
+ max = 0;
+ if(right != nil){
+ max = assignindices(n);
+ if(max < 0)
+ return (0, 0);
+ if(!specific(right.left.ty)){
+ nerror(n, "type for array not specific");
+ return (0, 0);
+ }
+ 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, big max);
+
+ if(left.ty.kind != Tint){
+ nerror(n, "array size "+etconv(left)+" is not an int");
+ return (0, 0);
+ }
+ Oelem =>
+ n.ty = right.ty;
+ Orange =>
+ if(left.ty != right.ty
+ || left.ty != tint && left.ty != tstring){
+ nerror(left, "range "+etconv(left)+" to "+etconv(right)+" is not an int or string range");
+ return (0, 0);
+ }
+ n.ty = left.ty;
+ Oname =>
+ id = n.decl;
+ if(id == nil){
+ nerror(n, "name with no declaration");
+ return (0, 0);
+ }
+ 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);
+ case id.store{
+ Dfn or
+ Dglobal or
+ Darg or
+ Dlocal or
+ Dimport or
+ Dfield or
+ Dtag =>
+ break;
+ Dunbound =>
+ fatal("unbound symbol found in echeck");
+ Dundef =>
+ nerror(n, id.sym.name+" is not declared");
+ id.store = Dwundef;
+ return (0, 0);
+ Dwundef =>
+ return (0, 0);
+ Dconst =>
+ if(id.init == nil){
+ nerror(n, id.sym.name+"'s value cannot be determined");
+ id.store = Dwundef;
+ return (0, 0);
+ }
+ Dtype =>
+ if(typeok)
+ break;
+ nerror(n, declconv(id)+" is not a variable");
+ return (0, 0);
+ * =>
+ fatal("echeck: unknown symbol storage");
+ }
+
+ if(n.ty == nil){
+ nerror(n, declconv(id)+"'s type is not fully defined");
+ id.store = Dwundef;
+ return (0, 0);
+ }
+ if(id.importid != nil && valistype(id.eimport)
+ && id.store != Dconst && id.store != Dtype && id.store != Dfn){
+ nerror(n, "cannot use "+expconv(n)+" because "+expconv(id.eimport)+" is a module interface");
+ return (0, 0);
+ }
+ if(n.ty.kind == Texception && !int 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);
+ Oconst =>
+ if(n.ty == nil){
+ nerror(n, "no type in "+expconv(n));
+ return (0, 0);
+ }
+ Oas =>
+ t = right.ty;
+ if(t.kind == Texception)
+ t = mkextuptype(t);
+ if(!tcompat(left.ty, t, 1)){
+ nerror(n, "type clash in "+etconv(left)+" = "+etconv(right));
+ return (0, 0);
+ }
+ 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 "+typeconv(t));
+ if(left.ty.kind == Texception){
+ nerror(n, "cannot assign to an exception");
+ return (0, 0);
+ }
+ if(islval(left))
+ break;
+ return (0, 0);
+ Osnd =>
+ if(left.ty.kind != Tchan){
+ nerror(n, "cannot send on "+etconv(left));
+ return (0, 0);
+ }
+ if(!tcompat(left.ty.tof, right.ty, 0)){
+ nerror(n, "type clash in "+etconv(left)+" <-= "+etconv(right));
+ return (0, 0);
+ }
+ t = right.ty;
+ if(t == tany)
+ t = left.ty.tof;
+ n.ty = t;
+ Orcv =>
+ t = left.ty;
+ if(t.kind == Tarray)
+ t = t.tof;
+ if(t.kind != Tchan){
+ nerror(n, "cannot receive on "+etconv(left));
+ return (0, 0);
+ }
+ 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;
+ Ocons =>
+ if(right.ty.kind != Tlist && right.ty != tany){
+ nerror(n, "cannot :: to "+etconv(right));
+ return (0, 0);
+ }
+ 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 "+etconv(left)+" :: "+etconv(right));
+ return (0, 0);
+ }
+ else
+ n.ty = usetype(mktype(n.src.start, n.src.stop, Tlist, t, nil));
+ }
+ Ohd or
+ Otl =>
+ if(left.ty.kind != Tlist || left.ty.tof == nil){
+ nerror(n, "cannot "+opconv(n.op)+" "+etconv(left));
+ return (0, 0);
+ }
+ if(n.op == Ohd)
+ n.ty = left.ty.tof;
+ else
+ n.ty = left.ty;
+ Otuple =>
+ n.ty = usetype(mktype(n.src.start, n.src.stop, Ttuple, nil, tuplefields(left)));
+ Ospawn =>
+ if(left.op != Ocall || left.left.ty.kind != Tfn && !isfnrefty(left.left.ty)){
+ nerror(left, "cannot spawn "+expconv(left));
+ return (0, 0);
+ }
+ if(left.ty != tnone){
+ nerror(left, "cannot spawn functions which return values, such as "+etconv(left));
+ return (0, 0);
+ }
+ Oraise =>
+ if(left.op == Onothing){
+ if(inexcept == nil){
+ nerror(n, expconv(n)+": empty raise not in exception handler");
+ return (0, 0);
+ }
+ n.left = dupn(1, n.src, inexcept);
+ break;
+ }
+ if(left.ty != tstring && left.ty.kind != Texception){
+ nerror(n, expconv(n)+": raise argument "+etconv(left)+" is not a string or exception");
+ return (0, 0);
+ }
+ if((left.op != Ocall || left.left.ty.kind == Tfn) && left.ty.ids != nil && int left.ty.cons){
+ nerror(n, "too few exception arguments");
+ return (0, 0);
+ }
+ Ocall =>
+ (nil, 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);
+ n.ty = t.tof;
+ if(!kidsok){
+ allok = 0;
+ break;
+ }
+
+ #
+ # get the name to call and any associated module
+ #
+ mod: ref Node = nil;
+ callee = nil;
+ id = nil;
+ tt = nil;
+ if(left.op == Odot){
+ 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, expconv(left)+" is not a function name");
+ allok = 0;
+ break;
+ }
+ if(pure && callee == nil)
+ fatal("can't find called function: "+nodeconv(left));
+ if(callee != nil && callee.store != Dfn && !isfnref(callee)){
+ nerror(left, expconv(left)+" is not a function");
+ allok = 0;
+ break;
+ }
+ if(mod != nil && mod.ty.kind != Tmodule){
+ nerror(left, "cannot call "+expconv(left));
+ allok = 0;
+ break;
+ }
+ if(mod != nil){
+ if(valistype(mod)){
+ nerror(left, "cannot call "+expconv(left)+" because "+expconv(mod)+" is a module interface");
+ allok = 0;
+ break;
+ }
+ }else if(id != nil && id.dot != nil && !isimpmod(id.dot.sym)){
+ nerror(left, "cannot call "+expconv(left)+" without importing "+id.sym.name+" from a variable");
+ allok = 0;
+ break;
+ }
+ if(mod != nil)
+ modrefable(left.ty);
+ if(callee != nil && callee.store != Dfn)
+ callee = nil;
+ if(t.varargs != byte 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){
+ unifysrc = n.src;
+ if(!argncompat(n, t.ids, right)){
+ allok = 0;
+ break;
+ }
+ (okp, tp) := tunify(left.ty, calltype(left.ty, right, n.ty));
+ if(!okp){
+ nerror(n, "function call type mismatch (" + typeconv(left.ty)+" vs "+typeconv(calltype(left.ty, right, n.ty))+")");
+ allok = 0;
+ }
+ else{
+ (n.ty, tp) = expandtype(n.ty, nil, nil, tp);
+ n.ty = usetype(n.ty);
+ if(ispoly(callee) && tt != nil && (tt.kind == Tadt || tt.kind == Tadtpick) && int (tt.flags&INST))
+ callee = rewcall(left, callee);
+ n.right = passfns(n.src, callee, left, right, tt, tp);
+ }
+ }
+ else if(!argcompat(n, t.ids, right))
+ allok = 0;
+ Odot =>
+ t = left.ty;
+ if(t.kind == Tref)
+ t = t.tof;
+ case t.kind{
+ Tadt or
+ Tadtpick or
+ Ttuple or
+ Texception or
+ 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, expconv(left)+" is not a type");
+ return (0, 0);
+ }
+ }
+ if(id == nil){
+ id = namedot(t.polys, right.decl.sym);
+ if(id != nil && !valistype(left)){
+ nerror(n, expconv(left)+" is not a type");
+ return (0, 0);
+ }
+ }
+ 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 "+right.decl.sym.name+" of "+etconv(left));
+ return (0, 0);
+ }
+ }
+ if(id == nil)
+ break;
+ if(id.store == Dfield && valistype(left)){
+ nerror(n, expconv(left)+" is not a value");
+ return (0, 0);
+ }
+ id.ty = validtype(id.ty, t.decl);
+ id.ty = usetype(id.ty);
+ break;
+ * =>
+ nerror(left, etconv(left)+" cannot be qualified with .");
+ return (0, 0);
+ }
+ if(id == nil){
+ nerror(n, expconv(right)+" is not a member of "+etconv(left));
+ return (0, 0);
+ }
+ if(id.ty == tunknown){
+ nerror(n, "illegal forward reference to "+expconv(n));
+ return (0, 0);
+ }
+
+ increfs(id);
+ right.decl = id;
+ n.ty = id.ty;
+ if((id.store == Dconst || id.store == Dtag) && hasside(left, 1))
+ nwarn(left, "result of expression "+etconv(left)+" ignored");
+ # function name as function reference
+ if(id.store == Dfn && (par == nil || (par.op != Omdot && par.op != Ocall && par.op != Ofunc)))
+ fnref(n, id);
+ Omdot =>
+ t = left.ty;
+ if(t.kind != Tmodule){
+ nerror(left, etconv(left)+" cannot be qualified with ->");
+ return (0, 0);
+ }
+ id = nil;
+ if(right.op == Oname){
+ id = namedot(t.ids, right.decl.sym);
+ }else if(right.op == Odot){
+ (ok, kidsok) = echeck(right, 0, isglobal, n);
+ allok &= kidsok;
+ if(!ok)
+ return (0, 0);
+ 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, expconv(right)+" is not a member of "+etconv(left));
+ return (0, 0);
+ }
+ if(id.store != Dconst && id.store != Dtype && id.store != Dtag){
+ if(valistype(left)){
+ nerror(n, expconv(left)+" is not a value");
+ return (0, 0);
+ }
+ }else if(hasside(left, 1))
+ nwarn(left, "result of expression "+etconv(left)+" ignored");
+ if(!typeok && id.store == Dtype){
+ nerror(n, expconv(n)+" is a type, not a value");
+ return (0, 0);
+ }
+ if(id.ty == tunknown){
+ nerror(n, "illegal forward reference to "+expconv(n));
+ return (0, 0);
+ }
+ 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);
+ Otagof =>
+ n.ty = tint;
+ t = left.ty;
+ if(t.kind == Tref)
+ t = t.tof;
+ id = nil;
+ case left.op{
+ Oname =>
+ id = left.decl;
+ Odot =>
+ id = left.right.decl;
+ Omdot =>
+ if(left.right.op == Odot)
+ id = left.right.right.decl;
+ }
+ 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 "+etconv(left));
+ return (1, 0);
+ }
+ Oind =>
+ t = left.ty;
+ if(t.kind != Tref || (t.tof.kind != Tadt && t.tof.kind != Tadtpick && t.tof.kind != Ttuple)){
+ nerror(n, "cannot * "+etconv(left));
+ return (0, 0);
+ }
+ n.ty = t.tof;
+ for(tg = t.tof.tags; tg != nil; tg = tg.next)
+ tg.ty.tof = usetype(tg.ty.tof);
+ Oindex =>
+ if(valistype(left)){
+ tagopt = 1;
+ (nil, kidsok) = echeck(right, 2, isglobal, n);
+ tagopt = 0;
+ if(!kidsok)
+ return (0, 0);
+ if((t = exptotype(n)) == nil){
+ nerror(n, expconv(right) + " is not a type list");
+ return (0, 0);
+ }
+ if(!typeok){
+ nerror(n, expconv(left) + " is not a variable");
+ return (0, 0);
+ }
+ *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;
+ (nil, kidsok) = echeck(right, 0, isglobal, n);
+ if(t.kind != Tarray && t != tstring){
+ nerror(n, "cannot index "+etconv(left));
+ return (0, 0);
+ }
+ if(t == tstring){
+ n.op = Oinds;
+ n.ty = tint;
+ }else{
+ n.ty = t.tof;
+ }
+ if(!kidsok){
+ allok = 0;
+ break;
+ }
+ if(right.ty != tint){
+ nerror(n, "cannot index "+etconv(left)+" with "+etconv(right));
+ allok = 0;
+ break;
+ }
+ Oslice =>
+ t = n.ty = left.ty;
+ if(t.kind != Tarray && t != tstring){
+ nerror(n, "cannot slice "+etconv(left)+" with '"+subexpconv(right.left)+":"+subexpconv(right.right)+"'");
+ return (0, 0);
+ }
+ if(right.left.ty != tint && right.left.op != Onothing
+ || right.right.ty != tint && right.right.op != Onothing){
+ nerror(n, "cannot slice "+etconv(left)+" with '"+subexpconv(right.left)+":"+subexpconv(right.right)+"'");
+ return (1, 0);
+ }
+ 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 "+etconv(left));
+ return (1, 0);
+ }
+ Ocomp or
+ Onot or
+ Oneg =>
+ n.ty = left.ty;
+usedty(n.ty);
+ case left.ty.kind{
+ Tint =>
+ return (1, allok);
+ Treal or
+ Tfix =>
+ if(n.op == Oneg)
+ return (1, allok);
+ Tbig or
+ Tbyte =>
+ if(n.op == Oneg || n.op == Ocomp)
+ return (1, allok);
+ }
+ nerror(n, "cannot apply "+opconv(n.op)+" to "+etconv(left));
+ return (0, 0);
+ Oinc or
+ Odec or
+ Opreinc or
+ Opredec =>
+ n.ty = left.ty;
+ case left.ty.kind{
+ Tint or
+ Tbig or
+ Tbyte or
+ Treal =>
+ break;
+ * =>
+ nerror(n, "cannot apply "+opconv(n.op)+" to "+etconv(left));
+ return (0, 0);
+ }
+ if(islval(left))
+ break;
+ return(0, 0);
+ Oadd or
+ Odiv or
+ Omul or
+ Osub =>
+ if(mathchk(n, 1))
+ break;
+ return (0, 0);
+ Oexp or
+ Oexpas =>
+ n.ty = left.ty;
+ if(n.ty != tint && n.ty != tbig && n.ty != treal){
+ nerror(n, "exponend " + etconv(left) + " is not int or real");
+ return (0, 0);
+ }
+ if(right.ty != tint){
+ nerror(n, "exponent " + etconv(right) + " is not int");
+ return (0, 0);
+ }
+ if(n.op == Oexpas && !islval(left))
+ return (0, 0);
+ break;
+ # if(mathchk(n, 0)){
+ # if(n.ty != tint){
+ # nerror(n, "exponentiation operands not int");
+ # return (0, 0);
+ # }
+ # break;
+ # }
+ # return (0, 0);
+ Olsh or
+ Orsh =>
+ if(shiftchk(n))
+ break;
+ return (0, 0);
+ Oandand or
+ Ooror =>
+ if(left.ty != tint){
+ nerror(n, opconv(n.op)+"'s left operand is not an int: "+etconv(left));
+ allok = 0;
+ }
+ if(right.ty != tint){
+ nerror(n, opconv(n.op)+"'s right operand is not an int: "+etconv(right));
+ allok = 0;
+ }
+ n.ty = tint;
+ Oand or
+ Omod or
+ Oor or
+ Oxor =>
+ if(mathchk(n, 0))
+ break;
+ return (0, 0);
+ Oaddas or
+ Odivas or
+ Omulas or
+ Osubas =>
+ if(mathchk(n, 1) && islval(left))
+ break;
+ return (0, 0);
+ Olshas or
+ Orshas =>
+ if(shiftchk(n) && islval(left))
+ break;
+ return (0, 0);
+ Oandas or
+ Omodas or
+ Oxoras or
+ Ooras =>
+ if(mathchk(n, 0) && islval(left))
+ break;
+ return (0, 0);
+ Olt or
+ Oleq or
+ Ogt or
+ Ogeq =>
+ if(!mathchk(n, 1))
+ return (0, 0);
+ n.ty = tint;
+ Oeq or
+ Oneq =>
+ case left.ty.kind{
+ Tint or
+ Tbig or
+ Tbyte or
+ Treal or
+ Tstring or
+ Tref or
+ Tlist or
+ Tarray or
+ Tchan or
+ Tany or
+ Tmodule or
+ Tfix or
+ 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;
+usedty(n.ty);
+ return (1, allok);
+ }
+ nerror(n, "cannot compare "+etconv(left)+" to "+etconv(right));
+ return (0, 0);
+ Otype =>
+ if(!typeok){
+ nerror(n, expconv(n) + " is not a variable");
+ return (0, 0);
+ }
+ n.ty = usetype(n.ty);
+ * =>
+ fatal("unknown op in typecheck: "+opconv(n.op));
+ }
+usedty(n.ty);
+ return (1, allok);
+}
+
+#
+# n is syntactically a call, but n.left is not a fn
+# check if it's the contructor for an adt
+#
+callcast(n: ref Node, kidsok, allok: int): (int, int)
+{
+ id: ref Decl;
+
+ left := n.left;
+ right := n.right;
+ id = nil;
+ case left.op{
+ Oname =>
+ id = left.decl;
+ Omdot =>
+ if(left.right.op == Odot)
+ id = left.right.right.decl;
+ else
+ id = left.right.decl;
+ Odot =>
+ id = left.right.decl;
+ }
+ if(id == nil || (id.store != Dtype && id.store != Dtag && id.ty.kind != Texception)){
+ nerror(left, expconv(left)+" is not a function or type name");
+ return (0, 0);
+ }
+ if(id.store == Dtag)
+ return tagcast(n, left, right, id, kidsok, allok);
+ t := left.ty;
+ n.ty = t;
+ if(!kidsok)
+ return (1, 0);
+
+ 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;
+ return (1, allok);
+ }
+
+ # try an exception with args
+ tt = mktype(n.src.start, n.src.stop, Texception, nil, tuplefields(right));
+ tt.cons = byte 1;
+ if(t.kind == Texception && t.cons == byte 1 && tcompat(t, tt, 1)){
+ if(right == nil)
+ *n = *n.left;
+ return (1, allok);
+ }
+
+ # 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 "+expconv(left)+" from '("+subexpconv(right)+")'");
+ return (0, 0);
+}
+
+tagcast(n, left, right: ref Node, id: ref Decl, kidsok, allok: int): (int, int)
+{
+ left.ty = id.ty;
+ if(left.op == Omdot)
+ left.right.ty = id.ty;
+ n.ty = id.ty;
+ if(!kidsok)
+ return (1, 0);
+ 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))
+ return (1, allok);
+
+ nerror(left, "cannot make a "+expconv(left)+" from '("+subexpconv(right)+")'");
+ return (0, 0);
+}
+
+valistype(n: ref Node): int
+{
+ case n.op{
+ Oname =>
+ if(n.decl.store == Dtype)
+ return 1;
+ Omdot =>
+ return valistype(n.right);
+ }
+ return 0;
+}
+
+islval(n: ref Node): int
+{
+ s := marklval(n);
+ if(s == 1)
+ return 1;
+ if(s == 0)
+ nerror(n, "cannot assign to "+expconv(n));
+ else
+ circlval(n, n);
+ return 0;
+}
+
+#
+# check to see if n is an lval
+#
+marklval(n: ref Node): int
+{
+ if(n == nil)
+ return 0;
+ case n.op{
+ Oname =>
+ return storespace[n.decl.store] && n.ty.kind != Texception; #ZZZZ && n.decl.tagged == nil;
+ Odot =>
+ if(n.right.decl.store != Dfield)
+ return 0;
+ if(n.right.decl.cycle != byte 0 && n.right.decl.cyc == byte 0)
+ return -1;
+ if(n.left.ty.kind != Tref && marklval(n.left) == 0)
+ nwarn(n, "assignment to "+etconv(n)+" ignored");
+ return 1;
+ Omdot =>
+ if(n.right.decl.store == Dglobal)
+ return 1;
+ return 0;
+ Oind =>
+ for(id := n.ty.ids; id != nil; id = id.next)
+ if(id.cycle != byte 0 && id.cyc == byte 0)
+ return -1;
+ return 1;
+ Oslice =>
+ if(n.right.right.op != Onothing || n.ty == tstring)
+ return 0;
+ return 1;
+ Oinds =>
+ #
+ # make sure we don't change a string constant
+ #
+ case n.left.op{
+ Oconst =>
+ return 0;
+ Oname =>
+ return storespace[n.left.decl.store];
+ Odot or
+ Omdot =>
+ if(n.left.right.decl != nil)
+ return storespace[n.left.right.decl.store];
+ }
+ return 1;
+ Oindex or
+ Oindx =>
+ return 1;
+ Otuple =>
+ for(nn := n.left; nn != nil; nn = nn.right){
+ s := marklval(nn.left);
+ if(s != 1)
+ return s;
+ }
+ return 1;
+ * =>
+ return 0;
+ }
+ return 0;
+}
+
+#
+# n has a circular field assignment.
+# find it and print an error message.
+#
+circlval(n, lval: ref Node): int
+{
+ if(n == nil)
+ return 0;
+ case n.op{
+ Oname =>
+ break;
+ Odot =>
+ if(n.right.decl.cycle != byte 0 && n.right.decl.cyc == byte 0){
+ nerror(lval, "cannot assign to "+expconv(lval)+" because field '"+n.right.decl.sym.name
+ +"' of "+expconv(n.left)+" could complete a cycle to "+expconv(n.left));
+ return -1;
+ }
+ return 1;
+ Oind =>
+ for(id := n.ty.ids; id != nil; id = id.next){
+ if(id.cycle != byte 0 && id.cyc == byte 0){
+ nerror(lval, "cannot assign to "+expconv(lval)+" because field '"+id.sym.name
+ +"' of "+expconv(n)+" could complete a cycle to "+expconv(n));
+ return -1;
+ }
+ }
+ return 1;
+ Oslice =>
+ if(n.right.right.op != Onothing || n.ty == tstring)
+ return 0;
+ return 1;
+ Oindex or
+ Oinds or
+ Oindx =>
+ return 1;
+ Otuple =>
+ for(nn := n.left; nn != nil; nn = nn.right){
+ s := circlval(nn.left, lval);
+ if(s != 1)
+ return s;
+ }
+ return 1;
+ * =>
+ return 0;
+ }
+ return 0;
+}
+
+mathchk(n: ref Node, realok: int): int
+{
+ lt := n.left.ty;
+ rt := n.right.ty;
+ if(rt != lt && !tequal(lt, rt)){
+ nerror(n, "type clash in "+etconv(n.left)+" "+opconv(n.op)+" "+etconv(n.right));
+ return 0;
+ }
+ n.ty = rt;
+ case rt.kind{
+ Tint or
+ Tbig or
+ Tbyte =>
+ return 1;
+ Tstring =>
+ case n.op{
+ Oadd or
+ Oaddas or
+ Ogt or
+ Ogeq or
+ Olt or
+ Oleq =>
+ return 1;
+ }
+ Treal or
+ Tfix =>
+ if(realok)
+ return 1;
+ }
+ nerror(n, "cannot "+opconv(n.op)+" "+etconv(n.left)+" and "+etconv(n.right));
+ return 0;
+}
+
+shiftchk(n: ref Node): int
+{
+ right := n.right;
+ left := n.left;
+ n.ty = left.ty;
+ case n.ty.kind{
+ Tint or
+ Tbyte or
+ Tbig =>
+ if(right.ty.kind != Tint){
+ nerror(n, "shift "+etconv(right)+" is not an int");
+ return 0;
+ }
+ return 1;
+ }
+ nerror(n, "cannot "+opconv(n.op)+" "+etconv(left)+" by "+etconv(right));
+ return 0;
+}
+
+#
+# check for any tany's in t
+#
+specific(t: ref Type): int
+{
+ if(t == nil)
+ return 0;
+ case t.kind{
+ Terror or
+ Tnone or
+ Tint or
+ Tbig or
+ Tstring or
+ Tbyte or
+ Treal or
+ Tfn or
+ Tadt or
+ Tadtpick or
+ Tmodule or
+ Tfix =>
+ return 1;
+ Tany =>
+ return 0;
+ Tpoly =>
+ return 1;
+ Tref or
+ Tlist or
+ Tarray or
+ Tchan =>
+ return specific(t.tof);
+ Ttuple or
+ Texception =>
+ for(d := t.ids; d != nil; d = d.next)
+ if(!specific(d.ty))
+ return 0;
+ return 1;
+ }
+ fatal("unknown type in specific: "+typeconv(t));
+ return 0;
+}
+
+#
+# infer the type of all variable in n from t
+# n is the left-hand exp of a := exp
+#
+declasinfer(n: ref Node, t: ref Type): int
+{
+ if(t.kind == Texception){
+ if(int t.cons)
+ return 0;
+ t = mkextuptype(t);
+ }
+ case n.op{
+ 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;
+ 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 in declasinfer: "+nodeconv(n));
+ return 0;
+}
+
+#
+# an error occured in declaring n;
+# set all decl identifiers to Dwundef
+# so further errors are squashed.
+#
+declaserr(n: ref Node)
+{
+ case n.op{
+ Otuple =>
+ for(n = n.left; n != nil; n = n.right)
+ declaserr(n.left);
+ return;
+ Oname =>
+ if(n.decl != nildecl)
+ n.decl.store = Dwundef;
+ return;
+ }
+ fatal("unknown op in declaserr: "+nodeconv(n));
+}
+
+argcompat(n: ref Node, f: ref Decl, a: ref Node): int
+{
+ for(; a != nil; a = a.right){
+ if(f == nil){
+ nerror(n, expconv(n.left)+": too many function arguments");
+ return 0;
+ }
+ if(!tcompat(f.ty, a.left.ty, 0)){
+ nerror(n, expconv(n.left)+": argument type mismatch: expected "+typeconv(f.ty)+" saw "+etconv(a.left));
+ return 0;
+ }
+ if(a.left.ty == tany)
+ a.left.ty = f.ty;
+ f = f.next;
+ }
+ if(f != nil){
+ nerror(n, expconv(n.left)+": too few function arguments");
+ return 0;
+ }
+ return 1;
+}
+
+argncompat(n: ref Node, f: ref Decl, a: ref Node): int
+{
+ for(; a != nil; a = a.right){
+ if(f == nil){
+ nerror(n, expconv(n.left)+": too many function arguments");
+ return 0;
+ }
+ f = f.next;
+ }
+ if(f != nil){
+ nerror(n, expconv(n.left)+": too few function arguments");
+ return 0;
+ }
+ return 1;
+}
+
+#
+# fn is Odot(adt, methid)
+# pass adt implicitly if needed
+# if not, any side effect of adt will be ingored
+#
+passimplicit(fname, args: ref Node): ref Node
+{
+ t := fname.ty;
+ if(t.ids == nil || t.ids.implicit == byte 0){
+ if(hasside(fname.left, 1))
+ nwarn(fname, "result of expression "+expconv(fname.left)+" ignored");
+ return args;
+ }
+ n := fname.left;
+ if(n.op == Oname && n.decl.store == Dtype){
+ nerror(n, expconv(n)+" is a type and cannot be a self argument");
+ n = mkn(Onothing, nil, nil);
+ n.src = fname.src;
+ n.ty = t.ids.ty;
+ }
+ args = mkn(Oseq, n, args);
+ args.src = n.src;
+ return args;
+}
+
+mem(t: ref Type, d: ref Decl): int
+{
+ for( ; d != nil; d = d.next)
+ if(d.ty == t) # was if(d.ty == t || tequal(d.ty, t))
+ return 1;
+ return 0;
+}
+
+memp(t: ref Type, f: ref Decl): int
+{
+ return mem(t, f.ty.polys) || mem(t, encpolys(f));
+}
+
+passfns0(src: Src, fun: ref Decl, args0: ref Node, args: ref Node, a: ref Node, tp: ref Tpair, polys: ref Decl): (ref Node, ref Node)
+{
+ id, idt, idf: ref Decl;
+ sym: ref Sym;
+ tt: ref Type;
+ na, mod: ref Node;
+
+ 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 " + typeconv(tt));
+ for(idf = idt.ty.ids; idf != nil; idf = idf.next){
+ sym = idf.sym;
+ (id, mod) = fnlookup(sym, tt);
+ while(id != nil && id.link != nil)
+ id = id.link;
+ if(id == nil) # error flagged already
+ continue;
+ id.refs++;
+ id.inline = byte -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 := lmodule(id)) != nil && !isimpmod(dot.sym))
+ error(src.start, "cannot use " + id.sym.name + " without importing " + id.dot.sym.name + " from a variable");
+
+ n := mkn(Ofnptr, mod, mkdeclname(src, id));
+ n.src = src;
+ n.decl = fun;
+ if(tt.kind == Tpoly)
+ n.flags = byte FNPTRA;
+ else
+ n.flags = byte 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 = fun;
+ if(tt.kind == Tpoly)
+ n.flags = byte (FNPTRA|FNPTR2);
+ else
+ n.flags = byte FNPTR2;
+ a = na.right = mkn(Oseq, n, nil);
+ }
+ if(args0 != nil)
+ args0 = args0.right;
+ }
+ return (args, a);
+}
+
+passfns(src: Src, fun: ref Decl, left: ref Node, args: ref Node, adtt: ref Type, tp: ref Tpair): ref Node
+{
+ a, args0: ref Node;
+
+ a = nil;
+ args0 = args;
+ if(args != nil)
+ for(a = args; a.right != nil; a = a.right)
+ ;
+ if(ispoly(fun))
+ polys := fun.ty.polys;
+ else
+ polys = left.ty.tof.polys;
+ (args, a) = passfns0(src, fun, args0, args, a, tp, polys);
+ if(adtt != nil){
+ if(ispoly(fun))
+ polys = encpolys(fun);
+ else
+ polys = nil;
+ (args, a) = passfns0(src, fun, args0, args, a, adtt.tmap, polys);
+ }
+ 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.
+#
+mkvarargs(n, args: ref Node): ref Type
+{
+ last: ref Decl;
+
+ nt := copytypeids(n.ty);
+ n.ty = nt;
+ f := n.ty.ids;
+ last = nil;
+ if(f == nil){
+ nerror(n, expconv(n)+"'s type is illegal");
+ 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, expconv(n)+": argument type mismatch: expected "+typeconv(f.ty)+" saw "+etconv(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, expconv(n)+": too few function arguments");
+ return nt;
+ }
+ s.left = fold(s.left);
+ s = s.left;
+ if(s.ty != tstring || s.op != Oconst){
+ nerror(args, expconv(n)+": format argument "+etconv(s)+" is not a string constant");
+ 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
+#
+fmtcheck(f, fmtarg, va: ref Node)
+{
+ fmt := fmtarg.decl.sym;
+ s := fmt.name;
+ ns := 0;
+ while(ns < len s){
+ c := s[ns++];
+ if(c != '%')
+ continue;
+
+ verb := -1;
+ n1 := 0;
+ n2 := 0;
+ dot := 0;
+ flag := 0;
+ flags := "";
+ fmtstart := ns - 1;
+ while(ns < len s && verb < 0){
+ c = s[ns++];
+ case c{
+ * =>
+ nerror(f, expconv(f)+": invalid character "+s[ns-1:ns]+" in format '"+s[fmtstart:ns]+"'");
+ return;
+ '.' =>
+ if(dot){
+ nerror(f, expconv(f)+": invalid format '"+s[fmtstart:ns]+"'");
+ return;
+ }
+ n1 = 1;
+ dot = 1;
+ continue;
+ '*' =>
+ if(!n1)
+ n1 = 1;
+ else if(!n2 && dot)
+ n2 = 1;
+ else{
+ nerror(f, expconv(f)+": invalid format '"+s[fmtstart:ns]+"'");
+ return;
+ }
+ if(va == nil){
+ nerror(f, expconv(f)+": too few arguments for format '"+s[fmtstart:ns]+"'");
+ return;
+ }
+ if(va.left.ty.kind != Tint){
+ nerror(f, expconv(f)+": format '"+s[fmtstart:ns]+"' incompatible with argument "+etconv(va.left));
+ return;
+ }
+ va = va.right;
+ '0' to '9' =>
+ while(ns < len s && s[ns] >= '0' && s[ns] <= '9')
+ ns++;
+ if(!n1)
+ n1 = 1;
+ else if(!n2 && dot)
+ n2 = 1;
+ else{
+ nerror(f, expconv(f)+": invalid format '"+s[fmtstart:ns]+"'");
+ return;
+ }
+ '+' or
+ '-' or
+ '#' or
+ ',' or
+ 'b' or
+ 'u' =>
+ for(i := 0; i < flag; i++){
+ if(flags[i] == c){
+ nerror(f, expconv(f)+": duplicate flag "+s[ns-1:ns]+" in format '"+s[fmtstart:ns]+"'");
+ return;
+ }
+ }
+ flags[flag++] = c;
+ '%' or
+ 'r' =>
+ verb = Tnone;
+ 'H' =>
+ verb = Tany;
+ 'c' =>
+ verb = Tint;
+ 'd' or
+ 'o' or
+ 'x' or
+ 'X' =>
+ verb = Tint;
+ for(i := 0; i < flag; i++){
+ if(flags[i] == 'b'){
+ verb = Tbig;
+ break;
+ }
+ }
+ 'e' or
+ 'f' or
+ 'g' or
+ 'E' or
+ 'G' =>
+ verb = Treal;
+ 's' or
+ 'q' =>
+ verb = Tstring;
+ }
+ }
+ if(verb != Tnone){
+ if(verb < 0){
+ nerror(f, expconv(f)+": incomplete format '"+s[fmtstart:ns]+"'");
+ return;
+ }
+ if(va == nil){
+ nerror(f, expconv(f)+": too few arguments for format '"+s[fmtstart:ns]+"'");
+ return;
+ }
+ ty := va.left.ty;
+ if(ty.kind == Texception)
+ ty = mkextuptype(ty);
+ case verb{
+ Tint =>
+ case ty.kind{
+ Tstring or
+ Tarray or
+ Tref or
+ Tchan or
+ Tlist or
+ Tmodule =>
+ if(c == 'x' || c == 'X')
+ verb = ty.kind;
+ }
+ Tany =>
+ if(tattr[ty.kind].isptr)
+ verb = ty.kind;
+ }
+ if(verb != ty.kind){
+ nerror(f, expconv(f)+": format '"+s[fmtstart:ns]+"' incompatible with argument "+etconv(va.left));
+ return;
+ }
+ va = va.right;
+ }
+ }
+ if(va != nil)
+ nerror(f, expconv(f)+": more arguments than formats");
+}
+
+tuplefields(n: ref Node): ref Decl
+{
+ h, last: ref Decl;
+
+ for(; n != nil; n = n.right){
+ d := mkdecl(n.left.src, Dfield, n.left.ty);
+ if(h == nil)
+ h = d;
+ else
+ last.next = d;
+ last = d;
+ }
+ return h;
+}
+
+#
+# make explicit indices for every element in an array initializer
+# return the maximum index
+# sort the indices and check for duplicates
+#
+assignindices(ar: ref Node): int
+{
+ wild, off, q: ref Node;
+
+ amax := 16r7fffffff;
+ size := dupn(0, nosrc, ar.left);
+ if(size.ty == tint){
+ size = fold(size);
+ if(size.op == Oconst)
+ amax = int size.c.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 "+typeconv(t)+" and "+typeconv(n.left.ty)+" in array initializer");
+ 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, big(last + 1)), nil);
+
+ for(q = n.left.left; q != nil; q = q.right){
+ off = q.left;
+ if(off.ty != tint){
+ nerror(off, "array index "+etconv(off)+" is not an int");
+ ok = 0;
+ continue;
+ }
+ off = fold(off);
+ case off.op{
+ Owild =>
+ if(wild != nil)
+ nerror(off, "array index * duplicated on line "+lineconv(wild.src.start));
+ wild = off;
+ continue;
+ Orange =>
+ if(off.left.op != Oconst || off.right.op != Oconst){
+ nerror(off, "range "+expconv(off)+" is not constant");
+ off = nil;
+ }else if(off.left.c.val < big 0 || off.right.c.val >= big amax){
+ nerror(off, "array index "+expconv(off)+" out of bounds");
+ off = nil;
+ }else
+ last = int off.right.c.val;
+ Oconst =>
+ last = int off.c.val;
+ if(off.c.val < big 0 || off.c.val >= big amax){
+ nerror(off, "array index "+expconv(off)+" out of bounds");
+ off = nil;
+ }
+ Onothing =>
+ # get here from a syntax error
+ off = nil;
+ * =>
+ nerror(off, "array index "+expconv(off)+" is not constant");
+ off = nil;
+ }
+
+ nlab++;
+ if(off == nil){
+ off = mkconst(n.left.right.src, big(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
+#
+casecheck(cn: ref Node, ret: ref Type)
+{
+ wild: ref Node;
+
+ (rok, nil) := echeck(cn.left, 0, 0, nil);
+ cn.right = scheck(cn.right, ret, Sother);
+ if(!rok)
+ return;
+ arg := cn.left;
+
+ t := arg.ty;
+ if(t != tint && t != tbig && t != tstring){
+ nerror(cn, "case argument "+etconv(arg)+" is not an int or big or string");
+ 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 "+expconv(q));
+ for(; q != nil; q = q.right){
+ left := fold(q.left);
+ q.left = left;
+ case left.op{
+ Owild =>
+ if(wild != nil)
+ nerror(left, "case qualifier * duplicated on line "+lineconv(wild.src.start));
+ wild = left;
+ Orange =>
+ if(left.ty != t)
+ nerror(left, "case qualifier "+etconv(left)+" clashes with "+etconv(arg));
+ else if(left.left.op != Oconst || left.right.op != Oconst){
+ nerror(left, "case range "+expconv(left)+" is not constant");
+ ok = 0;
+ }
+ nlab++;
+ * =>
+ if(left.ty != t){
+ nerror(left, "case qualifier "+etconv(left)+" clashes with "+etconv(arg));
+ ok = 0;
+ }else if(left.op != Oconst){
+ nerror(left, "case qualifier "+expconv(left)+" is not constant");
+ ok = 0;
+ }
+ nlab++;
+ }
+ }
+ }
+
+ 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
+#
+pickcheck(n: ref Node, ret: ref Type)
+{
+ qs, q, w: ref Node;
+
+ arg := n.left.right;
+ (nil, allok) := echeck(arg, 0, 0, nil);
+ if(!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 "+etconv(arg)+" is not a ref adt with pick tags");
+ 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 := array[t.decl.tag] of ref Node;
+ w = nil;
+ ok := 1;
+ nlab := 0;
+ for(qs = n.right; qs != nil; qs = qs.right){
+ qt : ref Node = nil;
+ for(q = qs.left.left; q != nil; q = q.right){
+ left := q.left;
+ case left.op{
+ Owild =>
+ # left.ty = tnone;
+ left.ty = t;
+ if(w != nil)
+ nerror(left, "pick qualifier * duplicated on line "+lineconv(w.src.start));
+ w = left;
+ Oname =>
+ id := namedot(t.tags, left.decl.sym);
+ if(id == nil){
+ nerror(left, "pick qualifier "+expconv(left)+" is not a member of "+etconv(arg));
+ ok = 0;
+ continue;
+ }
+
+ left.decl = id;
+ left.ty = id.ty;
+
+ if(tags[id.tag] != nil){
+ nerror(left, "pick qualifier "+expconv(left)+" duplicated on line "+lineconv(tags[id.tag].src.start));
+ ok = 0;
+ }
+ tags[id.tag] = left;
+ nlab++;
+ * =>
+ fatal("pickcheck can't handle "+nodeconv(q));
+ }
+
+ if(qt == nil)
+ qt = left;
+ else if(!tequal(qt.ty, left.ty))
+ nerror(left, "type clash in pick qualifiers "+etconv(qt)+" and "+etconv(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 "+expconv(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;
+}
+
+exccheck(en: ref Node, ret: ref Type)
+{
+ ed: ref Decl;
+ wild: ref Node;
+ qt: ref Type;
+
+ pushscope(nil, Sother);
+ if(en.left == nil)
+ en.left = mkdeclname(en.src, mkids(en.src, enter(".ex"+string nexc++, 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;
+ case left.op{
+ Owild =>
+ left.ty = texception;
+ if(wild != nil)
+ nerror(left, "exception qualifier * duplicated on line "+lineconv(wild.src.start));
+ wild = left;
+ Orange =>
+ left.ty = tnone;
+ nerror(left, "exception qualifier "+expconv(left)+" is illegal");
+ ok = 0;
+ * =>
+ (rok, nil) := echeck(left, 0, 0, nil);
+ if(!rok){
+ ok = 0;
+ break;
+ }
+ left = q.left = fold(left);
+ if(left.ty != t && left.ty.kind != Texception){
+ nerror(left, "exception qualifier "+etconv(left)+" is not a string or exception");
+ ok = 0;
+ }else if(left.op != Oconst){
+ nerror(left, "exception qualifier "+expconv(left)+" is not constant");
+ ok = 0;
+ }
+ else if(left.ty != t)
+ left.ty = mkextype(left.ty);
+ nlab++;
+ }
+
+ 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 " + expconv(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
+#
+checklabels(inits: ref Node, ctype: ref Type, nlab: int, title: string): ref Case
+{
+ n, q, wild: ref Node;
+
+ labs := array[nlab] of Label;
+ i := 0;
+ wild = nil;
+ for(n = inits; n != nil; n = n.right){
+ for(q = n.left.left; q != nil; q = q.right){
+ case q.left.op{
+ Oconst =>
+ labs[i].start = q.left;
+ labs[i].stop = q.left;
+ labs[i++].node = n.left;
+ Orange =>
+ labs[i].start = q.left.left;
+ labs[i].stop = q.left.right;
+ labs[i++].node = n.left;
+ Owild =>
+ wild = n.left;
+ * =>
+ fatal("bogus index in checklabels");
+ }
+ }
+ }
+
+ if(i != nlab)
+ fatal("bad label count: "+string nlab+" then "+string i);
+
+ casesort(ctype, array[nlab] of Label, 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 "+title+" "+expconv(labs[i].node));
+ for(e := i + 1; e < nlab; e++){
+ if(casecmp(ctype, labs[e].start, p) <= 0)
+ nerror(labs[e].start, title+" '"+eprintlist(labs[e].node.left, " or ")
+ +"' overlaps with '"+eprintlist(labs[e-1].node.left, " or ")+"' on line "
+ +lineconv(p.src.start));
+
+ #
+ # check for merging case labels
+ #
+ if(ctype != tint
+ || labs[e].start.c.val != p.c.val+big 1
+ || labs[e].node != labs[i].node)
+ break;
+ p = labs[e].stop;
+ }
+ if(e != i + 1){
+ labs[i].stop = p;
+ labs[i+1:] = labs[e:nlab];
+ nlab -= e - (i + 1);
+ }
+ }
+
+ c := ref Case;
+ c.nlab = nlab;
+ c.nsnd = 0;
+ c.labs = labs;
+ c.wild = wild;
+
+ return c;
+}
+
+symcmp(a: ref Sym, b: ref Sym): int
+{
+ if(a.name < b.name)
+ return -1;
+ if(a.name > b.name)
+ return 1;
+ return 0;
+}
+
+matchcmp(na: ref Node, nb: ref Node): int
+{
+ a := na.decl.sym;
+ b := nb.decl.sym;
+ la := len a.name;
+ lb := len b.name;
+ sa := la > 0 && a.name[la-1] == '*';
+ sb := lb > 0 && b.name[lb-1] == '*';
+ if(sa){
+ if(sb){
+ if(la == lb)
+ return symcmp(a, b);
+ return lb-la;
+ }
+ 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);
+ }
+ }
+ }
+}
+
+casecmp(ty: ref Type, a, b: ref Node): int
+{
+ if(ty == tint || ty == tbig){
+ if(a.c.val < b.c.val)
+ return -1;
+ if(a.c.val > b.c.val)
+ return 1;
+ return 0;
+ }
+ if(ty == texception)
+ return matchcmp(a, b);
+ return symcmp(a.decl.sym, b.decl.sym);
+}
+
+casesort(t: ref Type, aux, labs: array of Label, start, stop: int)
+{
+ 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)
+ aux[n:] = labs[base:top];
+ else if(mid < stop)
+ aux[n:] = labs[mid:stop];
+ labs[start:] = aux[:stop-start];
+}
+
+#
+# binary search for the label corresponding to a given value
+#
+findlab(ty: ref Type, v: ref Node, labs: array of Label, nlab: int): int
+{
+ if(nlab <= 1)
+ return 0;
+ m : int;
+ 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;
+}
+
+altcheck(an: ref Node, ret: ref Type)
+{
+ n, q, left, op, wild: ref Node;
+
+ 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 "+expconv(q));
+ for(; q != nil; q = q.right){
+ left = q.left;
+ case left.op{
+ Owild =>
+ if(wild != nil)
+ nerror(left, "alt guard * duplicated on line "+lineconv(wild.src.start));
+ wild = left;
+ Orange =>
+ nerror(left, "alt guard "+expconv(left)+" is illegal");
+ ok = 0;
+ * =>
+ op = hascomm(left);
+ if(op == nil){
+ nerror(left, "alt guard "+expconv(left)+" has no communication");
+ ok = 0;
+ break;
+ }
+ if(op.op == Osnd)
+ nsnd++;
+ else
+ nrcv++;
+ }
+ }
+ }
+
+ if(!ok)
+ return;
+
+ c := ref Case;
+ c.nlab = nsnd + nrcv;
+ c.nsnd = nsnd;
+ c.wild = wild;
+
+ an.ty = mktalt(c);
+}
+
+hascomm(n: ref Node): ref Node
+{
+ 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);
+}
+
+raisescheck(t: ref Type)
+{
+ if(t.kind != Tfn)
+ return;
+ n := t.eraises;
+ for(nn := n.left; nn != nil; nn = nn.right){
+ (ok, nil) := echeck(nn.left, 0, 0, nil);
+ if(ok && nn.left.ty.kind != Texception)
+ nerror(n, expconv(nn.left) + ": illegal raises expression");
+ }
+}
+
+Elist: adt{
+ d: ref Decl;
+ nxt: cyclic ref Elist;
+};
+
+emerge(el1: ref Elist, el2: ref Elist): ref Elist
+{
+ f: int;
+ el, nxt: ref Elist;
+
+ 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;
+}
+
+equals(n: ref Node): ref Elist
+{
+ q, nn: ref Node;
+ e, el: ref Elist;
+
+ 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 = ref Elist(nn.decl, el);
+ el = e;
+ }
+ return el;
+}
+
+caught(d: ref Decl, n: ref Node): int
+{
+ q, nn: ref Node;
+
+ 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;
+}
+
+raisecheck(n: ref Node, ql: ref Elist): ref Elist
+{
+ exc: int;
+ e: ref Node;
+ el, nel, nxt: ref Elist;
+
+ if(n == nil)
+ return nil;
+ el = nil;
+ for(; n != nil; n = n.right){
+ case(n.op){
+ Oscope =>
+ return raisecheck(n.right, ql);
+ Olabel or
+ Odo =>
+ return raisecheck(n.right, ql);
+ Oif or
+ Ofor =>
+ return emerge(raisecheck(n.right.left, ql),
+ raisecheck(n.right.right, ql));
+ Oalt or
+ Ocase or
+ Opick or
+ 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;
+ Oseq =>
+ el = emerge(raisecheck(n.left, ql), el);
+ break;
+ 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));
+ Oraise =>
+ e = n.left;
+ if(e.ty != nil && e.ty.kind == Texception){
+ if(e.ty.cons == byte 0)
+ return ql;
+ if(e.op == Ocall)
+ e = e.left;
+ if(e.op == Omdot)
+ e = e.right;
+ if(e.op != Oname)
+ fatal("exception " + nodeconv(e) + " not a name");
+ el = ref Elist(e.decl, nil);
+ return el;
+ }
+ return nil;
+ * =>
+ return nil;
+ }
+ }
+ return el;
+}
+
+checkraises(n: ref Node)
+{
+ f: int;
+ d: ref Decl;
+ e, el: ref Elist;
+ es, nn: ref Node;
+
+ el = raisecheck(n.right, nil);
+ es = n.ty.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 " + expconv(n.left) + " does not raise " + d.sym.name + " but declared");
+ }
+ }
+ for(e = el; e != nil; e = e.nxt)
+ if(e.d != nil)
+ nwarn(n, "function " + expconv(n.left) + " raises " + e.d.sym.name + " but not declared");
+}
+
+# sort all globals in modules now that we've finished with 'last' pointers
+# and before any code generation
+#
+gsort(n: ref Node)
+{
+ for(;;){
+ if(n == nil)
+ return;
+ if(n.op != Oseq)
+ break;
+ gsort(n.left);
+ n = n.right;
+ }
+ if(n.op == Omoddecl && int (n.ty.ok & OKverify)){
+ n.ty.ids = namesort(n.ty.ids);
+ sizeids(n.ty.ids, 0);
+ }
+}