summaryrefslogtreecommitdiff
path: root/appl/cmd/limbo/types.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/limbo/types.b')
-rw-r--r--appl/cmd/limbo/types.b4234
1 files changed, 4234 insertions, 0 deletions
diff --git a/appl/cmd/limbo/types.b b/appl/cmd/limbo/types.b
new file mode 100644
index 00000000..8be8f16d
--- /dev/null
+++ b/appl/cmd/limbo/types.b
@@ -0,0 +1,4234 @@
+
+kindname := array [Tend] of
+{
+ Tnone => "no type",
+ Tadt => "adt",
+ Tadtpick => "adt",
+ Tarray => "array",
+ Tbig => "big",
+ Tbyte => "byte",
+ Tchan => "chan",
+ Treal => "real",
+ Tfn => "fn",
+ Tint => "int",
+ Tlist => "list",
+ Tmodule => "module",
+ Tref => "ref",
+ Tstring => "string",
+ Ttuple => "tuple",
+ Texception => "exception",
+ Tfix => "fixed point",
+ Tpoly => "polymorphic",
+
+ Tainit => "array initializers",
+ Talt => "alt channels",
+ Tany => "polymorphic type",
+ Tarrow => "->",
+ Tcase => "case int labels",
+ Tcasel => "case big labels",
+ Tcasec => "case string labels",
+ Tdot => ".",
+ Terror => "type error",
+ Tgoto => "goto labels",
+ Tid => "id",
+ Tiface => "module interface",
+ Texcept => "exception handler table",
+ Tinst => "instantiated type",
+};
+
+tattr = array[Tend] of
+{
+ # isptr refable conable big vis
+ Tnone => Tattr(0, 0, 0, 0, 0),
+ Tadt => Tattr(0, 1, 1, 1, 1),
+ Tadtpick => Tattr(0, 1, 0, 1, 1),
+ Tarray => Tattr(1, 0, 0, 0, 1),
+ Tbig => Tattr(0, 0, 1, 1, 1),
+ Tbyte => Tattr(0, 0, 1, 0, 1),
+ Tchan => Tattr(1, 0, 0, 0, 1),
+ Treal => Tattr(0, 0, 1, 1, 1),
+ Tfn => Tattr(0, 1, 0, 0, 1),
+ Tint => Tattr(0, 0, 1, 0, 1),
+ Tlist => Tattr(1, 0, 0, 0, 1),
+ Tmodule => Tattr(1, 0, 0, 0, 1),
+ Tref => Tattr(1, 0, 0, 0, 1),
+ Tstring => Tattr(1, 0, 1, 0, 1),
+ Ttuple => Tattr(0, 1, 1, 1, 1),
+ Texception => Tattr(0, 0, 0, 1, 1),
+ Tfix => Tattr(0, 0, 1, 0, 1),
+ Tpoly => Tattr(1, 0, 0, 0, 1),
+
+ Tainit => Tattr(0, 0, 0, 1, 0),
+ Talt => Tattr(0, 0, 0, 1, 0),
+ Tany => Tattr(1, 0, 0, 0, 0),
+ Tarrow => Tattr(0, 0, 0, 0, 1),
+ Tcase => Tattr(0, 0, 0, 1, 0),
+ Tcasel => Tattr(0, 0, 0, 1, 0),
+ Tcasec => Tattr(0, 0, 0, 1, 0),
+ Tdot => Tattr(0, 0, 0, 0, 1),
+ Terror => Tattr(0, 1, 1, 0, 0),
+ Tgoto => Tattr(0, 0, 0, 1, 0),
+ Tid => Tattr(0, 0, 0, 0, 1),
+ Tiface => Tattr(0, 0, 0, 1, 0),
+ Texcept => Tattr(0, 0, 0, 1, 0),
+ Tinst => Tattr(0, 1, 1, 1, 1),
+};
+
+eqclass: array of ref Teq;
+
+ztype: Type;
+eqrec: int;
+eqset: int;
+adts: array of ref Decl;
+nadts: int;
+anontupsym: ref Sym;
+unifysrc: Src;
+
+addtmap(t1: ref Type, t2: ref Type, tph: ref Tpair): ref Tpair
+{
+ tp: ref Tpair;
+
+ tp = ref Tpair;
+ tp.t1 = t1;
+ tp.t2 = t2;
+ tp.nxt = tph;
+ return tp;
+}
+
+valtmap(t: ref Type, tp: ref Tpair): ref Type
+{
+ for( ; tp != nil; tp = tp.nxt)
+ if(tp.t1 == t)
+ return tp.t2;
+ return t;
+}
+
+addtype(t: ref Type, hdl: ref Typelist): ref Typelist
+{
+ tll := ref Typelist;
+ tll.t = t;
+ tll.nxt = nil;
+ if(hdl == nil)
+ return tll;
+ for(p := hdl; p.nxt != nil; p = p.nxt)
+ ;
+ p.nxt = tll;
+ return hdl;
+}
+
+typeinit()
+{
+ anontupsym = enter(".tuple", 0);
+
+ ztype.sbl = -1;
+ ztype.ok = byte 0;
+ ztype.rec = byte 0;
+
+ tbig = mktype(noline, noline, Tbig, nil, nil);
+ tbig.size = IBY2LG;
+ tbig.align = IBY2LG;
+ tbig.ok = OKmask;
+
+ tbyte = mktype(noline, noline, Tbyte, nil, nil);
+ tbyte.size = 1;
+ tbyte.align = 1;
+ tbyte.ok = OKmask;
+
+ tint = mktype(noline, noline, Tint, nil, nil);
+ tint.size = IBY2WD;
+ tint.align = IBY2WD;
+ tint.ok = OKmask;
+
+ treal = mktype(noline, noline, Treal, nil, nil);
+ treal.size = IBY2FT;
+ treal.align = IBY2FT;
+ treal.ok = OKmask;
+
+ tstring = mktype(noline, noline, Tstring, nil, nil);
+ tstring.size = IBY2WD;
+ tstring.align = IBY2WD;
+ tstring.ok = OKmask;
+
+ texception = mktype(noline, noline, Texception, nil, nil);
+ texception.size = IBY2WD;
+ texception.align = IBY2WD;
+ texception.ok = OKmask;
+
+ tany = mktype(noline, noline, Tany, nil, nil);
+ tany.size = IBY2WD;
+ tany.align = IBY2WD;
+ tany.ok = OKmask;
+
+ tnone = mktype(noline, noline, Tnone, nil, nil);
+ tnone.size = 0;
+ tnone.align = 1;
+ tnone.ok = OKmask;
+
+ terror = mktype(noline, noline, Terror, nil, nil);
+ terror.size = 0;
+ terror.align = 1;
+ terror.ok = OKmask;
+
+ tunknown = mktype(noline, noline, Terror, nil, nil);
+ tunknown.size = 0;
+ tunknown.align = 1;
+ tunknown.ok = OKmask;
+
+ tfnptr = mktype(noline, noline, Ttuple, nil, nil);
+ id := tfnptr.ids = mkids(nosrc, nil, tany, nil);
+ id.store = Dfield;
+ id.offset = 0;
+ id.sym = enter("t0", 0);
+ id.src = Src(0, 0);
+ id = tfnptr.ids.next = mkids(nosrc, nil, tint, nil);
+ id.store = Dfield;
+ id.offset = IBY2WD;
+ id.sym = enter("t1", 0);
+ id.src = Src(0, 0);
+
+ rtexception = mktype(noline, noline, Tref, texception, nil);
+ rtexception.size = IBY2WD;
+ rtexception.align = IBY2WD;
+ rtexception.ok = OKmask;
+}
+
+typestart()
+{
+ descriptors = nil;
+ nfns = 0;
+ adts = nil;
+ nadts = 0;
+ selfdecl = nil;
+ if(tfnptr.decl != nil)
+ tfnptr.decl.desc = nil;
+
+ eqclass = array[Tend] of ref Teq;
+
+ typebuiltin(mkids(nosrc, enter("int", 0), nil, nil), tint);
+ typebuiltin(mkids(nosrc, enter("big", 0), nil, nil), tbig);
+ typebuiltin(mkids(nosrc, enter("byte", 0), nil, nil), tbyte);
+ typebuiltin(mkids(nosrc, enter("string", 0), nil, nil), tstring);
+ typebuiltin(mkids(nosrc, enter("real", 0), nil, nil), treal);
+}
+
+modclass(): ref Teq
+{
+ return eqclass[Tmodule];
+}
+
+mktype(start: Line, stop: Line, kind: int, tof: ref Type, args: ref Decl): ref Type
+{
+ t := ref ztype;
+ t.src.start = start;
+ t.src.stop = stop;
+ t.kind = kind;
+ t.tof = tof;
+ t.ids = args;
+ return t;
+}
+
+nalt: int;
+mktalt(c: ref Case): ref Type
+{
+ t := mktype(noline, noline, Talt, nil, nil);
+ t.decl = mkdecl(nosrc, Dtype, t);
+ t.decl.sym = enter(".a"+string nalt++, 0);
+ t.cse = c;
+ return usetype(t);
+}
+
+#
+# copy t and the top level of ids
+#
+copytypeids(t: ref Type): ref Type
+{
+ last: ref Decl;
+
+ nt := ref *t;
+ for(id := t.ids; id != nil; id = id.next){
+ new := ref *id;
+ if(last == nil)
+ nt.ids = new;
+ else
+ last.next = new;
+ last = new;
+ }
+ return nt;
+}
+
+#
+# make each of the ids have type t
+#
+typeids(ids: ref Decl, t: ref Type): ref Decl
+{
+ if(ids == nil)
+ return nil;
+
+ ids.ty = t;
+ for(id := ids.next; id != nil; id = id.next)
+ id.ty = t;
+ return ids;
+}
+
+typebuiltin(d: ref Decl, t: ref Type)
+{
+ d.ty = t;
+ t.decl = d;
+ installids(Dtype, d);
+}
+
+fielddecl(store: int, ids: ref Decl): ref Node
+{
+ n := mkn(Ofielddecl, nil, nil);
+ n.decl = ids;
+ for(; ids != nil; ids = ids.next)
+ ids.store = store;
+ return n;
+}
+
+typedecl(ids: ref Decl, t: ref Type): ref Node
+{
+ if(t.decl == nil)
+ t.decl = ids;
+ n := mkn(Otypedecl, nil, nil);
+ n.decl = ids;
+ n.ty = t;
+ for(; ids != nil; ids = ids.next)
+ ids.ty = t;
+ return n;
+}
+
+typedecled(n: ref Node)
+{
+ installids(Dtype, n.decl);
+}
+
+adtdecl(ids: ref Decl, fields: ref Node): ref Node
+{
+ n := mkn(Oadtdecl, nil, nil);
+ t := mktype(ids.src.start, ids.src.stop, Tadt, nil, nil);
+ n.decl = ids;
+ n.left = fields;
+ n.ty = t;
+ t.decl = ids;
+ for(; ids != nil; ids = ids.next)
+ ids.ty = t;
+ return n;
+}
+
+adtdecled(n: ref Node)
+{
+ d := n.ty.decl;
+ installids(Dtype, d);
+ if(n.ty.polys != nil){
+ pushscope(nil, Sother);
+ installids(Dtype, n.ty.polys);
+ }
+ pushscope(nil, Sother);
+ fielddecled(n.left);
+ n.ty.ids = popscope();
+ if(n.ty.polys != nil)
+ n.ty.polys = popscope();
+ for(ids := n.ty.ids; ids != nil; ids = ids.next)
+ ids.dot = d;
+}
+
+fielddecled(n: ref Node)
+{
+ for(; n != nil; n = n.right){
+ case n.op{
+ Oseq =>
+ fielddecled(n.left);
+ Oadtdecl =>
+ adtdecled(n);
+ return;
+ Otypedecl =>
+ typedecled(n);
+ return;
+ Ofielddecl =>
+ installids(Dfield, n.decl);
+ return;
+ Ocondecl =>
+ condecled(n);
+ gdasdecl(n.right);
+ return;
+ Oexdecl =>
+ exdecled(n);
+ return;
+ Opickdecl =>
+ pickdecled(n);
+ return;
+ * =>
+ fatal("can't deal with "+opname[n.op]+" in fielddecled");
+ }
+ }
+}
+
+pickdecled(n: ref Node): int
+{
+ if(n == nil)
+ return 0;
+ tag := pickdecled(n.left);
+ pushscope(nil, Sother);
+ fielddecled(n.right.right);
+ d := n.right.left.decl;
+ d.ty.ids = popscope();
+ installids(Dtag, d);
+ for(; d != nil; d = d.next)
+ d.tag = tag++;
+ return tag;
+}
+
+#
+# make the tuple type used to initialize adt t
+#
+mkadtcon(t: ref Type): ref Type
+{
+ last: ref Decl;
+
+ nt := ref *t;
+ nt.ids = nil;
+ nt.kind = Ttuple;
+ for(id := t.ids; id != nil; id = id.next){
+ if(id.store != Dfield)
+ continue;
+ new := ref *id;
+ new.cyc = byte 0;
+ if(last == nil)
+ nt.ids = new;
+ else
+ last.next = new;
+ last = new;
+ }
+ last.next = nil;
+ return nt;
+}
+
+#
+# make the tuple type used to initialize t,
+# an adt with pick fields tagged by tg
+#
+mkadtpickcon(t, tgt: ref Type): ref Type
+{
+ last := mkids(tgt.decl.src, nil, tint, nil);
+ last.store = Dfield;
+ nt := mktype(t.src.start, t.src.stop, Ttuple, nil, last);
+ for(id := t.ids; id != nil; id = id.next){
+ if(id.store != Dfield)
+ continue;
+ new := ref *id;
+ new.cyc = byte 0;
+ last.next = new;
+ last = new;
+ }
+ for(id = tgt.ids; id != nil; id = id.next){
+ if(id.store != Dfield)
+ continue;
+ new := ref *id;
+ new.cyc = byte 0;
+ last.next = new;
+ last = new;
+ }
+ last.next = nil;
+ return nt;
+}
+
+#
+# make an identifier type
+#
+mkidtype(src: Src, s: ref Sym): ref Type
+{
+ t := mktype(src.start, src.stop, Tid, nil, nil);
+ if(s.unbound == nil){
+ s.unbound = mkdecl(src, Dunbound, nil);
+ s.unbound.sym = s;
+ }
+ t.decl = s.unbound;
+ return t;
+}
+
+#
+# make a qualified type for t->s
+#
+mkarrowtype(start: Line, stop: Line, t: ref Type, s: ref Sym): ref Type
+{
+ t = mktype(start, stop, Tarrow, t, nil);
+ if(s.unbound == nil){
+ s.unbound = mkdecl(Src(start, stop), Dunbound, nil);
+ s.unbound.sym = s;
+ }
+ t.decl = s.unbound;
+ return t;
+}
+
+#
+# make a qualified type for t.s
+#
+mkdottype(start: Line, stop: Line, t: ref Type, s: ref Sym): ref Type
+{
+ t = mktype(start, stop, Tdot, t, nil);
+ if(s.unbound == nil){
+ s.unbound = mkdecl(Src(start, stop), Dunbound, nil);
+ s.unbound.sym = s;
+ }
+ t.decl = s.unbound;
+ return t;
+}
+
+mkinsttype(src: Src, tt: ref Type, tyl: ref Typelist): ref Type
+{
+ t := mktype(src.start, src.stop, Tinst, tt, nil);
+ t.tlist = tyl;
+ return t;
+}
+
+#
+# look up the name f in the fields of a module, adt, or tuple
+#
+namedot(ids: ref Decl, s: ref Sym): ref Decl
+{
+ for(; ids != nil; ids = ids.next)
+ if(ids.sym == s)
+ return ids;
+ return nil;
+}
+
+#
+# complete the declaration of an adt
+# methods frames get sized in module definition or during function definition
+# place the methods at the end of the field list
+#
+adtdefd(t: ref Type)
+{
+ next, aux, store, auxhd, tagnext: ref Decl;
+
+ if(debug['x'])
+ print("adt %s defd\n", typeconv(t));
+ d := t.decl;
+ tagnext = nil;
+ store = nil;
+ for(id := t.polys; id != nil; id = id.next){
+ id.store = Dtype;
+ id.ty = verifytypes(id.ty, d, nil);
+ }
+ for(id = t.ids; id != nil; id = next){
+ if(id.store == Dtag){
+ if(t.tags != nil)
+ error(id.src.start, "only one set of pick fields allowed");
+ tagnext = pickdefd(t, id);
+ next = tagnext;
+ if(store != nil)
+ store.next = next;
+ else
+ t.ids = next;
+ continue;
+ }else{
+ id.dot = d;
+ next = id.next;
+ store = id;
+ }
+ }
+ aux = nil;
+ store = nil;
+ auxhd = nil;
+ seentags := 0;
+ for(id = t.ids; id != nil; id = next){
+ if(id == tagnext)
+ seentags = 1;
+
+ next = id.next;
+ id.dot = d;
+ id.ty = topvartype(verifytypes(id.ty, d, nil), id, 1, 1);
+ if(id.store == Dfield && id.ty.kind == Tfn)
+ id.store = Dfn;
+ if(id.store == Dfn || id.store == Dconst){
+ if(store != nil)
+ store.next = next;
+ else
+ t.ids = next;
+ if(aux != nil)
+ aux.next = id;
+ else
+ auxhd = id;
+ aux = id;
+ }else{
+ if(seentags)
+ error(id.src.start, "pick fields must be the last data fields in an adt");
+ store = id;
+ }
+ }
+ if(aux != nil)
+ aux.next = nil;
+ if(store != nil)
+ store.next = auxhd;
+ else
+ t.ids = auxhd;
+
+ for(id = t.tags; id != nil; id = id.next){
+ id.ty = verifytypes(id.ty, d, nil);
+ if(id.ty.tof == nil)
+ id.ty.tof = mkadtpickcon(t, id.ty);
+ }
+}
+
+#
+# assemble the data structure for an adt with a pick clause.
+# since the scoping rules for adt pick fields are strange,
+# we have a cutomized check for overlapping defitions.
+#
+pickdefd(t: ref Type, tg: ref Decl): ref Decl
+{
+ lasttg : ref Decl = nil;
+ d := t.decl;
+ t.tags = tg;
+ tag := 0;
+ while(tg != nil){
+ tt := tg.ty;
+ if(tt.kind != Tadtpick || tg.tag != tag)
+ break;
+ tt.decl = tg;
+ lasttg = tg;
+ for(; tg != nil; tg = tg.next){
+ if(tg.ty != tt)
+ break;
+ tag++;
+ lasttg = tg;
+ tg.dot = d;
+ }
+ for(id := tt.ids; id != nil; id = id.next){
+ xid := namedot(t.ids, id.sym);
+ if(xid != nil)
+ error(id.src.start, "redeclaration of "+declconv(id)+
+ " previously declared as "+storeconv(xid)+" on line "+lineconv(xid.src.start));
+ id.dot = d;
+ }
+ }
+ if(lasttg == nil){
+ error(t.src.start, "empty pick field declaration in "+typeconv(t));
+ t.tags = nil;
+ }else
+ lasttg.next = nil;
+ d.tag = tag;
+ return tg;
+}
+
+moddecl(ids: ref Decl, fields: ref Node): ref Node
+{
+ n := mkn(Omoddecl, mkn(Oseq, nil, nil), nil);
+ t := mktype(ids.src.start, ids.src.stop, Tmodule, nil, nil);
+ n.decl = ids;
+ n.left = fields;
+ n.ty = t;
+ return n;
+}
+
+moddecled(n: ref Node)
+{
+ d := n.decl;
+ installids(Dtype, d);
+ isimp := 0;
+ for(ids := d; ids != nil; ids = ids.next){
+ for(im := impmods; im != nil; im = im.next){
+ if(ids.sym == im.sym){
+ isimp = 1;
+ d = ids;
+ dm := ref Dlist;
+ dm.d = ids;
+ dm.next = nil;
+ if(impdecls == nil)
+ impdecls = dm;
+ else{
+ for(dl := impdecls; dl.next != nil; dl = dl.next)
+ ;
+ dl.next = dm;
+ }
+ }
+ }
+ ids.ty = n.ty;
+ }
+ pushscope(nil, Sother);
+ fielddecled(n.left);
+
+ d.ty.ids = popscope();
+
+ #
+ # make the current module the . parent of all contained decls.
+ #
+ for(ids = d.ty.ids; ids != nil; ids = ids.next)
+ ids.dot = d;
+
+ t := d.ty;
+ t.decl = d;
+ if(debug['m'])
+ print("declare module %s\n", d.sym.name);
+
+ #
+ # add the iface declaration in case it's needed later
+ #
+ installids(Dglobal, mkids(d.src, enter(".m."+d.sym.name, 0), tnone, nil));
+
+ if(isimp){
+ for(ids = d.ty.ids; ids != nil; ids = ids.next){
+ s := ids.sym;
+ if(s.decl != nil && s.decl.scope >= scope){
+ dot := s.decl.dot;
+ if(s.decl.store != Dwundef && dot != nil && dot != d && isimpmod(dot.sym) && dequal(ids, s.decl, 0))
+ continue;
+ redecl(ids);
+ ids.old = s.decl.old;
+ }else
+ ids.old = s.decl;
+ s.decl = ids;
+ ids.scope = scope;
+ }
+ }
+}
+
+#
+# for each module in id,
+# link by field ext all of the decls for
+# functions needed in external linkage table
+# collect globals and make a tuple for all of them
+#
+mkiface(m: ref Decl): ref Type
+{
+ iface := last := ref Decl;
+ globals := glast := mkdecl(m.src, Dglobal, mktype(m.src.start, m.src.stop, Tadt, nil, nil));
+ for(id := m.ty.ids; id != nil; id = id.next){
+ case id.store{
+ Dglobal =>
+ glast = glast.next = dupdecl(id);
+ id.iface = globals;
+ glast.iface = id;
+ Dfn =>
+ id.iface = last = last.next = dupdecl(id);
+ last.iface = id;
+ Dtype =>
+ if(id.ty.kind != Tadt)
+ break;
+ for(d := id.ty.ids; d != nil; d = d.next){
+ if(d.store == Dfn){
+ d.iface = last = last.next = dupdecl(d);
+ last.iface = d;
+ }
+ }
+ }
+ }
+ last.next = nil;
+ iface = namesort(iface.next);
+
+ if(globals.next != nil){
+ glast.next = nil;
+ globals.ty.ids = namesort(globals.next);
+ globals.ty.decl = globals;
+ globals.sym = enter(".mp", 0);
+ globals.dot = m;
+ globals.next = iface;
+ iface = globals;
+ }
+
+ #
+ # make the interface type and install an identifier for it
+ # the iface has a ref count if it is loaded
+ #
+ t := mktype(m.src.start, m.src.stop, Tiface, nil, iface);
+ id = enter(".m."+m.sym.name, 0).decl;
+ t.decl = id;
+ id.ty = t;
+
+ #
+ # dummy node so the interface is initialized
+ #
+ id.init = mkn(Onothing, nil, nil);
+ id.init.ty = t;
+ id.init.decl = id;
+ return t;
+}
+
+joiniface(mt, t: ref Type)
+{
+ iface := t.ids;
+ globals := iface;
+ if(iface != nil && iface.store == Dglobal)
+ iface = iface.next;
+ for(id := mt.tof.ids; id != nil; id = id.next){
+ case id.store{
+ Dglobal =>
+ for(d := id.ty.ids; d != nil; d = d.next)
+ d.iface.iface = globals;
+ Dfn =>
+ id.iface.iface = iface;
+ iface = iface.next;
+ * =>
+ fatal("unknown store "+storeconv(id)+" in joiniface");
+ }
+ }
+ if(iface != nil)
+ fatal("join iface not matched");
+ mt.tof = t;
+}
+
+addiface(m: ref Decl, d: ref Decl)
+{
+ t: ref Type;
+ id, last, dd, lastorig: ref Decl;
+
+ if(d == nil || !local(d))
+ return;
+ modrefable(d.ty);
+ if(m == nil){
+ if(impdecls.next != nil)
+ for(dl := impdecls; dl != nil; dl = dl.next)
+ if(dl.d.ty.tof != impdecl.ty.tof) # impdecl last
+ addiface(dl.d, d);
+ addiface(impdecl, d);
+ return;
+ }
+ t = m.ty.tof;
+ last = nil;
+ lastorig = nil;
+ for(id = t.ids; id != nil; id = id.next){
+ if(d == id || d == id.iface)
+ return;
+ last = id;
+ if(id.tag == 0)
+ lastorig = id;
+ }
+ dd = dupdecl(d);
+ if(d.dot == nil)
+ d.dot = dd.dot = m;
+ d.iface = dd;
+ dd.iface = d;
+ if(last == nil)
+ t.ids = dd;
+ else
+ last.next = dd;
+ dd.tag = 1; # mark so not signed
+ if(lastorig == nil)
+ t.ids = namesort(t.ids);
+ else
+ lastorig.next = namesort(lastorig.next);
+}
+
+#
+# eliminate unused declarations from interfaces
+# label offset within interface
+#
+narrowmods()
+{
+ id: ref Decl;
+ for(eq := modclass(); eq != nil; eq = eq.eq){
+ t := eq.ty.tof;
+
+ if(t.linkall == byte 0){
+ last : ref Decl = nil;
+ for(id = t.ids; id != nil; id = id.next){
+ if(id.refs == 0){
+ if(last == nil)
+ t.ids = id.next;
+ else
+ last.next = id.next;
+ }else
+ last = id;
+ }
+
+ #
+ # need to resize smaller interfaces
+ #
+ resizetype(t);
+ }
+
+ offset := 0;
+ for(id = t.ids; id != nil; id = id.next)
+ id.offset = offset++;
+
+ #
+ # rathole to stuff number of entries in interface
+ #
+ t.decl.init.c = ref Const;
+ t.decl.init.c.val = big offset;
+ }
+}
+
+#
+# check to see if any data field of module m if referenced.
+# if so, mark all data in m
+#
+moddataref()
+{
+ for(eq := modclass(); eq != nil; eq = eq.eq){
+ id := eq.ty.tof.ids;
+ if(id != nil && id.store == Dglobal && id.refs)
+ for(id = eq.ty.ids; id != nil; id = id.next)
+ if(id.store == Dglobal)
+ modrefable(id.ty);
+ }
+}
+
+#
+# move the global declarations in interface to the front
+#
+modglobals(mod, globals: ref Decl): ref Decl
+{
+ #
+ # make a copy of all the global declarations
+ # used for making a type descriptor for globals ONLY
+ # note we now have two declarations for the same variables,
+ # which is apt to cause problems if code changes
+ #
+ # here we fix up the offsets for the real declarations
+ #
+ idoffsets(mod.ty.ids, 0, 1);
+
+ last := head := ref Decl;
+ for(id := mod.ty.ids; id != nil; id = id.next)
+ if(id.store == Dglobal)
+ last = last.next = dupdecl(id);
+
+ last.next = globals;
+ return head.next;
+}
+
+#
+# snap all id type names to the actual type
+# check that all types are completely defined
+# verify that the types look ok
+#
+validtype(t: ref Type, inadt: ref Decl): ref Type
+{
+ if(t == nil)
+ return t;
+ bindtypes(t);
+ t = verifytypes(t, inadt, nil);
+ cycsizetype(t);
+ teqclass(t);
+ return t;
+}
+
+usetype(t: ref Type): ref Type
+{
+ if(t == nil)
+ return t;
+ t = validtype(t, nil);
+ reftype(t);
+ return t;
+}
+
+internaltype(t: ref Type): ref Type
+{
+ bindtypes(t);
+ t.ok = OKverify;
+ sizetype(t);
+ t.ok = OKmask;
+ return t;
+}
+
+#
+# checks that t is a valid top-level type
+#
+topvartype(t: ref Type, id: ref Decl, tyok: int, polyok: int): ref Type
+{
+ if(t.kind == Tadt && t.tags != nil || t.kind == Tadtpick)
+ error(id.src.start, "cannot declare "+id.sym.name+" with type "+typeconv(t));
+ if(!tyok && t.kind == Tfn)
+ error(id.src.start, "cannot declare "+id.sym.name+" to be a function");
+ if(!polyok && (t.kind == Tadt || t.kind == Tadtpick) && ispolyadt(t))
+ error(id.src.start, "cannot declare " + id.sym.name + " of a polymorphic type");
+ return t;
+}
+
+toptype(src: Src, t: ref Type): ref Type
+{
+ if(t.kind == Tadt && t.tags != nil || t.kind == Tadtpick)
+ error(src.start, typeconv(t)+", an adt with pick fields, must be used with ref");
+ if(t.kind == Tfn)
+ error(src.start, "data cannot have a fn type like "+typeconv(t));
+ return t;
+}
+
+comtype(src: Src, t: ref Type, adtd: ref Decl): ref Type
+{
+ if(adtd == nil && (t.kind == Tadt || t.kind == Tadtpick) && ispolyadt(t))
+ error(src.start, "polymorphic type " + typeconv(t) + " illegal here");
+ return t;
+}
+
+usedty(t: ref Type)
+{
+ if(t != nil && (t.ok | OKmodref) != OKmask)
+ fatal("used ty " + stypeconv(t) + " " + hex(int t.ok, 2));
+}
+
+bindtypes(t: ref Type)
+{
+ id: ref Decl;
+
+ if(t == nil)
+ return;
+ if((t.ok & OKbind) == OKbind)
+ return;
+ t.ok |= OKbind;
+ case t.kind{
+ Tadt =>
+ if(t.polys != nil){
+ pushscope(nil, Sother);
+ installids(Dtype, t.polys);
+ }
+ if(t.val != nil)
+ mergepolydecs(t);
+ if(t.polys != nil){
+ popscope();
+ for(id = t.polys; id != nil; id = id.next)
+ bindtypes(id.ty);
+ }
+ Tadtpick or
+ Tmodule or
+ Terror or
+ Tint or
+ Tbig or
+ Tstring or
+ Treal or
+ Tbyte or
+ Tnone or
+ Tany or
+ Tiface or
+ Tainit or
+ Talt or
+ Tcase or
+ Tcasel or
+ Tcasec or
+ Tgoto or
+ Texcept or
+ Tfix or
+ Tpoly =>
+ break;
+ Tarray or
+ Tarrow or
+ Tchan or
+ Tdot or
+ Tlist or
+ Tref =>
+ bindtypes(t.tof);
+ Tid =>
+ id = t.decl.sym.decl;
+ if(id == nil)
+ id = undefed(t.src, t.decl.sym);
+ # save a little space
+ id.sym.unbound = nil;
+ t.decl = id;
+ Ttuple or
+ Texception =>
+ for(id = t.ids; id != nil; id = id.next)
+ bindtypes(id.ty);
+ Tfn =>
+ if(t.polys != nil){
+ pushscope(nil, Sother);
+ installids(Dtype, t.polys);
+ }
+ for(id = t.ids; id != nil; id = id.next)
+ bindtypes(id.ty);
+ bindtypes(t.tof);
+ if(t.val != nil)
+ mergepolydecs(t);
+ if(t.polys != nil){
+ popscope();
+ for(id = t.polys; id != nil; id = id.next)
+ bindtypes(id.ty);
+ }
+ Tinst =>
+ bindtypes(t.tof);
+ for(tyl := t.tlist; tyl != nil; tyl = tyl.nxt)
+ bindtypes(tyl.t);
+ * =>
+ fatal("bindtypes: unknown type kind "+string t.kind);
+ }
+}
+
+#
+# walk the type checking for validity
+#
+verifytypes(t: ref Type, adtt: ref Decl, poly: ref Decl): ref Type
+{
+ id: ref Decl;
+
+ if(t == nil)
+ return nil;
+ if((t.ok & OKverify) == OKverify)
+ return t;
+ t.ok |= OKverify;
+if((t.ok & (OKverify|OKbind)) != (OKverify|OKbind))
+fatal("verifytypes bogus ok for " + stypeconv(t));
+ cyc := t.flags&CYCLIC;
+ case t.kind{
+ Terror or
+ Tint or
+ Tbig or
+ Tstring or
+ Treal or
+ Tbyte or
+ Tnone or
+ Tany or
+ Tiface or
+ Tainit or
+ Talt or
+ Tcase or
+ Tcasel or
+ Tcasec or
+ Tgoto or
+ Texcept =>
+ break;
+ Tfix =>
+ n := t.val;
+ ok: int;
+ max := 0.0;
+ if(n.op == Oseq){
+ (ok, nil) = echeck(n.left, 0, 0, n);
+ (ok1, nil) := echeck(n.right, 0, 0, n);
+ if(!ok || !ok1)
+ return terror;
+ if(n.left.ty != treal || n.right.ty != treal){
+ error(t.src.start, "fixed point scale/maximum not real");
+ return terror;
+ }
+ n.right = fold(n.right);
+ if(n.right.op != Oconst){
+ error(t.src.start, "fixed point maximum not constant");
+ return terror;
+ }
+ if((max = n.right.c.rval) <= 0.0){
+ error(t.src.start, "non-positive fixed point maximum");
+ return terror;
+ }
+ n = n.left;
+ }
+ else{
+ (ok, nil) = echeck(n, 0, 0, nil);
+ if(!ok)
+ return terror;
+ if(n.ty != treal){
+ error(t.src.start, "fixed point scale not real");
+ return terror;
+ }
+ }
+ n = t.val = fold(n);
+ if(n.op != Oconst){
+ error(t.src.start, "fixed point scale not constant");
+ return terror;
+ }
+ if(n.c.rval <= 0.0){
+ error(t.src.start, "non-positive fixed point scale");
+ return terror;
+ }
+ ckfix(t, max);
+ Tref =>
+ t.tof = comtype(t.src, verifytypes(t.tof, adtt, nil), adtt);
+ if(t.tof != nil && !tattr[t.tof.kind].refable){
+ error(t.src.start, "cannot have a ref " + typeconv(t.tof));
+ return terror;
+ }
+ if(0 && t.tof.kind == Tfn && t.tof.ids != nil && int t.tof.ids.implicit)
+ error(t.src.start, "function references cannot have a self argument");
+ if(0 && t.tof.kind == Tfn && t.polys != nil)
+ error(t.src.start, "function references cannot be polymorphic");
+ Tchan or
+ Tarray or
+ Tlist =>
+ t.tof = comtype(t.src, toptype(t.src, verifytypes(t.tof, adtt, nil)), adtt);
+ Tid =>
+ t.ok &= ~OKverify;
+ t = verifytypes(idtype(t), adtt, nil);
+ Tarrow =>
+ t.ok &= ~OKverify;
+ t = verifytypes(arrowtype(t, adtt), adtt, nil);
+ Tdot =>
+ #
+ # verify the parent adt & lookup the tag fields
+ #
+ t.ok &= ~OKverify;
+ t = verifytypes(dottype(t, adtt), adtt, nil);
+ Tadt =>
+ #
+ # this is where Tadt may get tag fields added
+ #
+ adtdefd(t);
+ Tadtpick =>
+ for(id = t.ids; id != nil; id = id.next){
+ id.ty = topvartype(verifytypes(id.ty, id.dot, nil), id, 0, 1);
+ if(id.store == Dconst)
+ error(t.src.start, "cannot declare a con like "+id.sym.name+" within a pick");
+ }
+ verifytypes(t.decl.dot.ty, nil, nil);
+ Tmodule =>
+ for(id = t.ids; id != nil; id = id.next){
+ id.ty = verifytypes(id.ty, nil, nil);
+ if(id.store == Dglobal && id.ty.kind == Tfn)
+ id.store = Dfn;
+ if(id.store != Dtype && id.store != Dfn)
+ topvartype(id.ty, id, 0, 0);
+ }
+ Ttuple or
+ Texception =>
+ if(t.decl == nil){
+ t.decl = mkdecl(t.src, Dtype, t);
+ t.decl.sym = anontupsym;
+ }
+ i := 0;
+ for(id = t.ids; id != nil; id = id.next){
+ id.store = Dfield;
+ if(id.sym == nil)
+ id.sym = enter("t"+string i, 0);
+ i++;
+ id.ty = toptype(id.src, verifytypes(id.ty, adtt, nil));
+ }
+ Tfn =>
+ last : ref Decl = nil;
+ for(id = t.ids; id != nil; id = id.next){
+ id.store = Darg;
+ id.ty = topvartype(verifytypes(id.ty, adtt, nil), id, 0, 1);
+ if(id.implicit != byte 0){
+ if(poly != nil)
+ selfd := poly;
+ else
+ selfd = adtt;
+ if(selfd == nil)
+ error(t.src.start, "function is not a member of an adt, so can't use self");
+ else if(id != t.ids)
+ error(id.src.start, "only the first argument can use self");
+ else if(id.ty != selfd.ty && (id.ty.kind != Tref || id.ty.tof != selfd.ty))
+ error(id.src.start, "self argument's type must be "+selfd.sym.name+" or ref "+selfd.sym.name);
+ }
+ last = id;
+ }
+ for(id = t.polys; id != nil; id = id.next){
+ if(adtt != nil){
+ for(id1 := adtt.ty.polys; id1 != nil; id1 = id1.next){
+ if(id1.sym == id.sym)
+ id.ty = id1.ty;
+ }
+ }
+ id.store = Dtype;
+ id.ty = verifytypes(id.ty, adtt, nil);
+ }
+ t.tof = comtype(t.src, toptype(t.src, verifytypes(t.tof, adtt, nil)), adtt);
+ if(t.varargs != byte 0 && (last == nil || last.ty != tstring))
+ error(t.src.start, "variable arguments must be preceded by a string");
+ if(t.varargs != byte 0 && t.polys != nil)
+ error(t.src.start, "polymorphic functions must not have variable arguments");
+ Tpoly =>
+ for(id = t.ids; id != nil; id = id.next){
+ id.store = Dfn;
+ id.ty = verifytypes(id.ty, adtt, t.decl);
+ }
+ Tinst =>
+ t.ok &= ~OKverify;
+ t.tof = verifytypes(t.tof, adtt, nil);
+ for(tyl := t.tlist; tyl != nil; tyl = tyl.nxt)
+ tyl.t = verifytypes(tyl.t, adtt, nil);
+ (t, nil) = insttype(t, adtt, nil);
+ t = verifytypes(t, adtt, nil);
+ * =>
+ fatal("verifytypes: unknown type kind "+string t.kind);
+ }
+ if(int cyc)
+ t.flags |= CYCLIC;
+ return t;
+}
+
+#
+# resolve an id type
+#
+idtype(t: ref Type): ref Type
+{
+ id := t.decl;
+ if(id.store == Dunbound)
+ fatal("idtype: unbound decl");
+ tt := id.ty;
+ if(id.store != Dtype && id.store != Dtag){
+ if(id.store == Dundef){
+ id.store = Dwundef;
+ error(t.src.start, id.sym.name+" is not declared");
+ }else if(id.store == Dimport){
+ id.store = Dwundef;
+ error(t.src.start, id.sym.name+"'s type cannot be determined");
+ }else if(id.store != Dwundef)
+ error(t.src.start, id.sym.name+" is not a type");
+ return terror;
+ }
+ if(tt == nil){
+ error(t.src.start, stypeconv(t)+" not fully defined");
+ return terror;
+ }
+ return tt;
+}
+
+#
+# resolve a -> qualified type
+#
+arrowtype(t: ref Type, adtt: ref Decl): ref Type
+{
+ id := t.decl;
+ if(id.ty != nil){
+ if(id.store == Dunbound)
+ fatal("arrowtype: unbound decl has a type");
+ return id.ty;
+ }
+
+ #
+ # special hack to allow module variables to derive other types
+ #
+ tt := t.tof;
+ if(tt.kind == Tid){
+ id = tt.decl;
+ if(id.store == Dunbound)
+ fatal("arrowtype: Tid's decl unbound");
+ if(id.store == Dimport){
+ id.store = Dwundef;
+ error(t.src.start, id.sym.name+"'s type cannot be determined");
+ return terror;
+ }
+
+ #
+ # forward references to module variables can't be resolved
+ #
+ if(id.store != Dtype && (id.ty.ok & OKbind) != OKbind){
+ error(t.src.start, id.sym.name+"'s type cannot be determined");
+ return terror;
+ }
+
+ if(id.store == Dwundef)
+ return terror;
+ tt = id.ty = verifytypes(id.ty, adtt, nil);
+ if(tt == nil){
+ error(t.tof.src.start, typeconv(t.tof)+" is not a module");
+ return terror;
+ }
+ }else
+ tt = verifytypes(t.tof, adtt, nil);
+ t.tof = tt;
+ if(tt == terror)
+ return terror;
+ if(tt.kind != Tmodule){
+ error(t.src.start, typeconv(tt)+" is not a module");
+ return terror;
+ }
+ id = namedot(tt.ids, t.decl.sym);
+ if(id == nil){
+ error(t.src.start, t.decl.sym.name+" is not a member of "+typeconv(tt));
+ return terror;
+ }
+ if(id.store == Dtype && id.ty != nil){
+ t.decl = id;
+ return id.ty;
+ }
+ error(t.src.start, typeconv(t)+" is not a type");
+ return terror;
+}
+
+#
+# resolve a . qualified type
+#
+dottype(t: ref Type, adtt: ref Decl): ref Type
+{
+ if(t.decl.ty != nil){
+ if(t.decl.store == Dunbound)
+ fatal("dottype: unbound decl has a type");
+ return t.decl.ty;
+ }
+ t.tof = tt := verifytypes(t.tof, adtt, nil);
+ if(tt == terror)
+ return terror;
+ if(tt.kind != Tadt){
+ error(t.src.start, typeconv(tt)+" is not an adt");
+ return terror;
+ }
+ id := namedot(tt.tags, t.decl.sym);
+ if(id != nil && id.ty != nil){
+ t.decl = id;
+ return id.ty;
+ }
+ error(t.src.start, t.decl.sym.name+" is not a pick tag of "+typeconv(tt));
+ return terror;
+}
+
+insttype(t: ref Type, adtt: ref Decl, tp: ref Tpair): (ref Type, ref Tpair)
+{
+ src := t.src;
+ if(t.tof.kind != Tadt && t.tof.kind != Tadtpick){
+ error(src.start, typeconv(t.tof) + " is not an adt");
+ return (terror, nil);
+ }
+ if(t.tof.kind == Tadt)
+ ids := t.tof.polys;
+ else
+ ids = t.tof.decl.dot.ty.polys;
+ if(ids == nil){
+ error(src.start, typeconv(t.tof) + " is not a polymorphic adt");
+ return (terror, nil);
+ }
+ for(tyl := t.tlist; tyl != nil && ids != nil; tyl = tyl.nxt){
+ tt := tyl.t;
+ if(!tattr[tt.kind].isptr){
+ error(src.start, typeconv(tt) + " is not a pointer type");
+ return (terror, nil);
+ }
+ unifysrc = src;
+ (ok, nil) := tunify(ids.ty, tt);
+ if(!ok){
+ error(src.start, "type " + typeconv(tt) + " does not match " + typeconv(ids.ty));
+ return (terror, nil);
+ }
+ # usetype(tt);
+ tt = verifytypes(tt, adtt, nil);
+ tp = addtmap(ids.ty, tt, tp);
+ ids = ids.next;
+ }
+ if(tyl != nil){
+ error(src.start, "too many actual types in instantiation");
+ return (terror, nil);
+ }
+ if(ids != nil){
+ error(src.start, "too few actual types in instantiation");
+ return (terror, nil);
+ }
+ tt := t.tof;
+ (t, nil) = expandtype(tt, t, adtt, tp);
+ if(t == tt && adtt == nil)
+ t = duptype(t);
+ if(t != tt)
+ t.tmap = tp;
+ t.src = src;
+ return (t, tp);
+}
+
+#
+# walk a type, putting all adts, modules, and tuples into equivalence classes
+#
+teqclass(t: ref Type)
+{
+ id: ref Decl;
+
+ if(t == nil || (t.ok & OKclass) == OKclass)
+ return;
+ t.ok |= OKclass;
+ case t.kind{
+ Terror or
+ Tint or
+ Tbig or
+ Tstring or
+ Treal or
+ Tbyte or
+ Tnone or
+ Tany or
+ Tiface or
+ Tainit or
+ Talt or
+ Tcase or
+ Tcasel or
+ Tcasec or
+ Tgoto or
+ Texcept or
+ Tfix or
+ Tpoly =>
+ return;
+ Tref =>
+ teqclass(t.tof);
+ return;
+ Tchan or
+ Tarray or
+ Tlist =>
+ teqclass(t.tof);
+#ZZZ elim return to fix recursive chans, etc
+ if(!debug['Z'])
+ return;
+ Tadt or
+ Tadtpick or
+ Ttuple or
+ Texception =>
+ for(id = t.ids; id != nil; id = id.next)
+ teqclass(id.ty);
+ for(tg := t.tags; tg != nil; tg = tg.next)
+ teqclass(tg.ty);
+ for(id = t.polys; id != nil; id = id.next)
+ teqclass(id.ty);
+ Tmodule =>
+ t.tof = mkiface(t.decl);
+ for(id = t.ids; id != nil; id = id.next)
+ teqclass(id.ty);
+ Tfn =>
+ for(id = t.ids; id != nil; id = id.next)
+ teqclass(id.ty);
+ for(id = t.polys; id != nil; id = id.next)
+ teqclass(id.ty);
+ teqclass(t.tof);
+ return;
+ * =>
+ fatal("teqclass: unknown type kind "+string t.kind);
+ }
+
+ #
+ # find an equivalent type
+ # stupid linear lookup could be made faster
+ #
+ if((t.ok & OKsized) != OKsized)
+ fatal("eqclass type not sized: " + stypeconv(t));
+
+ for(teq := eqclass[t.kind]; teq != nil; teq = teq.eq){
+ if(t.size == teq.ty.size && tequal(t, teq.ty)){
+ t.eq = teq;
+ if(t.kind == Tmodule)
+ joiniface(t, t.eq.ty.tof);
+ return;
+ }
+ }
+
+ #
+ # if no equiv type, make one
+ #
+ eqclass[t.kind] = t.eq = ref Teq(0, t, eqclass[t.kind]);
+}
+
+#
+# record that we've used the type
+# using a type uses all types reachable from that type
+#
+reftype(t: ref Type)
+{
+ id: ref Decl;
+
+ if(t == nil || (t.ok & OKref) == OKref)
+ return;
+ t.ok |= OKref;
+ if(t.decl != nil && t.decl.refs == 0)
+ t.decl.refs++;
+ case t.kind{
+ Terror or
+ Tint or
+ Tbig or
+ Tstring or
+ Treal or
+ Tbyte or
+ Tnone or
+ Tany or
+ Tiface or
+ Tainit or
+ Talt or
+ Tcase or
+ Tcasel or
+ Tcasec or
+ Tgoto or
+ Texcept or
+ Tfix or
+ Tpoly =>
+ break;
+ Tref or
+ Tchan or
+ Tarray or
+ Tlist =>
+ if(t.decl != nil){
+ if(nadts >= len adts){
+ a := array[nadts + 32] of ref Decl;
+ a[0:] = adts;
+ adts = a;
+ }
+ adts[nadts++] = t.decl;
+ }
+ reftype(t.tof);
+ Tadt or
+ Tadtpick or
+ Ttuple or
+ Texception =>
+ if(t.kind == Tadt || t.kind == Ttuple && t.decl.sym != anontupsym){
+ if(nadts >= len adts){
+ a := array[nadts + 32] of ref Decl;
+ a[0:] = adts;
+ adts = a;
+ }
+ adts[nadts++] = t.decl;
+ }
+ for(id = t.ids; id != nil; id = id.next)
+ if(id.store != Dfn)
+ reftype(id.ty);
+ for(tg := t.tags; tg != nil; tg = tg.next)
+ reftype(tg.ty);
+ for(id = t.polys; id != nil; id = id.next)
+ reftype(id.ty);
+ if(t.kind == Tadtpick)
+ reftype(t.decl.dot.ty);
+ Tmodule =>
+ #
+ # a module's elements should get used individually
+ # but do the globals for any sbl file
+ #
+ if(bsym != nil)
+ for(id = t.ids; id != nil; id = id.next)
+ if(id.store == Dglobal)
+ reftype(id.ty);
+ break;
+ Tfn =>
+ for(id = t.ids; id != nil; id = id.next)
+ reftype(id.ty);
+ for(id = t.polys; id != nil; id = id.next)
+ reftype(id.ty);
+ reftype(t.tof);
+ * =>
+ fatal("reftype: unknown type kind "+string t.kind);
+ }
+}
+
+#
+# check all reachable types for cycles and illegal forward references
+# find the size of all the types
+#
+cycsizetype(t: ref Type)
+{
+ id: ref Decl;
+
+ if(t == nil || (t.ok & (OKcycsize|OKcyc|OKsized)) == (OKcycsize|OKcyc|OKsized))
+ return;
+ t.ok |= OKcycsize;
+ case t.kind{
+ Terror or
+ Tint or
+ Tbig or
+ Tstring or
+ Treal or
+ Tbyte or
+ Tnone or
+ Tany or
+ Tiface or
+ Tainit or
+ Talt or
+ Tcase or
+ Tcasel or
+ Tcasec or
+ Tgoto or
+ Texcept or
+ Tfix or
+ Tpoly =>
+ t.ok |= OKcyc;
+ sizetype(t);
+ Tref or
+ Tchan or
+ Tarray or
+ Tlist =>
+ cyctype(t);
+ sizetype(t);
+ cycsizetype(t.tof);
+ Tadt or
+ Ttuple or
+ Texception =>
+ cyctype(t);
+ sizetype(t);
+ for(id = t.ids; id != nil; id = id.next)
+ cycsizetype(id.ty);
+ for(tg := t.tags; tg != nil; tg = tg.next){
+ if((tg.ty.ok & (OKcycsize|OKcyc|OKsized)) == (OKcycsize|OKcyc|OKsized))
+ continue;
+ tg.ty.ok |= (OKcycsize|OKcyc|OKsized);
+ for(id = tg.ty.ids; id != nil; id = id.next)
+ cycsizetype(id.ty);
+ }
+ for(id = t.polys; id != nil; id = id.next)
+ cycsizetype(id.ty);
+ Tadtpick =>
+ t.ok &= ~OKcycsize;
+ cycsizetype(t.decl.dot.ty);
+ Tmodule =>
+ cyctype(t);
+ sizetype(t);
+ for(id = t.ids; id != nil; id = id.next)
+ cycsizetype(id.ty);
+ sizeids(t.ids, 0);
+ Tfn =>
+ cyctype(t);
+ sizetype(t);
+ for(id = t.ids; id != nil; id = id.next)
+ cycsizetype(id.ty);
+ for(id = t.polys; id != nil; id = id.next)
+ cycsizetype(id.ty);
+ cycsizetype(t.tof);
+ sizeids(t.ids, MaxTemp);
+#ZZZ need to align?
+ * =>
+ fatal("cycsizetype: unknown type kind "+string t.kind);
+ }
+}
+
+# check for circularity in type declarations
+# - has to be called before verifytypes
+#
+tcycle(t: ref Type)
+{
+ id: ref Decl;
+ tt: ref Type;
+ tll: ref Typelist;
+
+ if(t == nil)
+ return;
+ case(t.kind){
+ * =>
+ ;
+ Tchan or
+ Tarray or
+ Tref or
+ Tlist or
+ Tdot =>
+ tcycle(t.tof);
+ Tfn or
+ Ttuple =>
+ tcycle(t.tof);
+ for(id = t.ids; id != nil; id = id.next)
+ tcycle(id.ty);
+ Tarrow =>
+ if(int(t.rec&TRvis)){
+ error(t.src.start, "circularity in definition of " + typeconv(t));
+ *t = *terror; # break the cycle
+ return;
+ }
+ tt = t.tof;
+ t.rec |= TRvis;
+ tcycle(tt);
+ if(tt.kind == Tid)
+ tt = tt.decl.ty;
+ id = namedot(tt.ids, t.decl.sym);
+ if(id != nil)
+ tcycle(id.ty);
+ t.rec &= ~TRvis;
+ Tid =>
+ if(int(t.rec&TRvis)){
+ error(t.src.start, "circularity in definition of " + typeconv(t));
+ *t = *terror; # break the cycle
+ return;
+ }
+ t.rec |= TRvis;
+ tcycle(t.decl.ty);
+ t.rec &= ~TRvis;
+ Tinst =>
+ tcycle(t.tof);
+ for(tll = t.tlist; tll != nil; tll = tll.nxt)
+ tcycle(tll.t);
+ }
+}
+
+#
+# marks for checking for arcs
+#
+ ArcValue,
+ ArcList,
+ ArcArray,
+ ArcRef,
+ ArcCyc, # cycle found
+ ArcPolycyc:
+ con 1 << iota;
+
+cyctype(t: ref Type)
+{
+ if((t.ok & OKcyc) == OKcyc)
+ return;
+ t.ok |= OKcyc;
+ t.rec |= TRcyc;
+ case t.kind{
+ Terror or
+ Tint or
+ Tbig or
+ Tstring or
+ Treal or
+ Tbyte or
+ Tnone or
+ Tany or
+ Tfn or
+ Tchan or
+ Tarray or
+ Tref or
+ Tlist or
+ Tfix or
+ Tpoly =>
+ break;
+ Tadt or
+ Tmodule or
+ Ttuple or
+ Texception =>
+ for(id := t.ids; id != nil; id = id.next)
+ cycfield(t, id);
+ for(tg := t.tags; tg != nil; tg = tg.next){
+ if((tg.ty.ok & OKcyc) == OKcyc)
+ continue;
+ tg.ty.ok |= OKcyc;
+ for(id = tg.ty.ids; id != nil; id = id.next)
+ cycfield(t, id);
+ }
+ * =>
+ fatal("cyctype: unknown type kind "+string t.kind);
+ }
+ t.rec &= ~TRcyc;
+}
+
+cycfield(base: ref Type, id: ref Decl)
+{
+ if(!storespace[id.store])
+ return;
+ arc := cycarc(base, id.ty);
+
+ if((arc & (ArcCyc|ArcValue)) == (ArcCyc|ArcValue)){
+ if(id.cycerr == byte 0)
+ error(base.src.start, "illegal type cycle without a reference in field "
+ +id.sym.name+" of "+stypeconv(base));
+ id.cycerr = byte 1;
+ }else if(arc & ArcCyc){
+ if((arc & ArcArray) && id.cyc == byte 0 && !(arc & ArcPolycyc)){
+ if(id.cycerr == byte 0)
+ error(base.src.start, "illegal circular reference to type "+typeconv(id.ty)
+ +" in field "+id.sym.name+" of "+stypeconv(base));
+ id.cycerr = byte 1;
+ }
+ id.cycle = byte 1;
+ }else if(id.cyc != byte 0){
+ if(id.cycerr == byte 0)
+ error(id.src.start, "spurious cyclic qualifier for field "+id.sym.name+" of "+stypeconv(base));
+ id.cycerr = byte 1;
+ }
+}
+
+cycarc(base, t: ref Type): int
+{
+ if(t == nil)
+ return 0;
+ if((t.rec & TRcyc) == TRcyc){
+ if(tequal(t, base)){
+ if(t.kind == Tmodule)
+ return ArcCyc | ArcRef;
+ else
+ return ArcCyc | ArcValue;
+ }
+ return 0;
+ }
+ t.rec |= TRcyc;
+ me := 0;
+ case t.kind{
+ Terror or
+ Tint or
+ Tbig or
+ Tstring or
+ Treal or
+ Tbyte or
+ Tnone or
+ Tany or
+ Tchan or
+ Tfn or
+ Tfix or
+ Tpoly =>
+ break;
+ Tarray =>
+ me = cycarc(base, t.tof) & ~ArcValue | ArcArray;
+ Tref =>
+ me = cycarc(base, t.tof) & ~ArcValue | ArcRef;
+ Tlist =>
+ me = cycarc(base, t.tof) & ~ArcValue | ArcList;
+ Tadt or
+ Tadtpick or
+ Tmodule or
+ Ttuple or
+ Texception =>
+ me = 0;
+ arc: int;
+ for(id := t.ids; id != nil; id = id.next){
+ if(!storespace[id.store])
+ continue;
+ arc = cycarc(base, id.ty);
+ if((arc & ArcCyc) && id.cycerr == byte 0)
+ me |= arc;
+ }
+ for(tg := t.tags; tg != nil; tg = tg.next){
+ arc = cycarc(base, tg.ty);
+ if((arc & ArcCyc) && tg.cycerr == byte 0)
+ me |= arc;
+ }
+
+ if(t.kind == Tmodule)
+ me = me & ArcCyc | ArcRef | ArcPolycyc;
+ else
+ me &= ArcCyc | ArcValue | ArcPolycyc;
+ * =>
+ fatal("cycarc: unknown type kind "+string t.kind);
+ }
+ t.rec &= ~TRcyc;
+ if(int (t.flags&CYCLIC))
+ me |= ArcPolycyc;
+ return me;
+}
+
+#
+# set the sizes and field offsets for t
+# look only as deeply as needed to size this type.
+# cycsize type will clean up the rest.
+#
+sizetype(t: ref Type)
+{
+ id: ref Decl;
+ sz, al, s, a: int;
+
+ if(t == nil)
+ return;
+ if((t.ok & OKsized) == OKsized)
+ return;
+ t.ok |= OKsized;
+if((t.ok & (OKverify|OKsized)) != (OKverify|OKsized))
+fatal("sizetype bogus ok for " + stypeconv(t));
+ case t.kind{
+ * =>
+ fatal("sizetype: unknown type kind "+string t.kind);
+ Terror or
+ Tnone or
+ Tbyte or
+ Tint or
+ Tbig or
+ Tstring or
+ Tany or
+ Treal =>
+ fatal(typeconv(t)+" should have a size");
+ Tref or
+ Tchan or
+ Tarray or
+ Tlist or
+ Tmodule or
+ Tfix or
+ Tpoly =>
+ t.size = t.align = IBY2WD;
+ Tadt or
+ Ttuple or
+ Texception =>
+ if(t.tags == nil){
+#ZZZ
+ if(!debug['z']){
+ (sz, t.align) = sizeids(t.ids, 0);
+ t.size = align(sz, t.align);
+ }else{
+ (sz, nil) = sizeids(t.ids, 0);
+ t.align = IBY2LG;
+ t.size = align(sz, IBY2LG);
+ }
+ return;
+ }
+#ZZZ
+ if(!debug['z']){
+ (sz, al) = sizeids(t.ids, IBY2WD);
+ if(al < IBY2WD)
+ al = IBY2WD;
+ }else{
+ (sz, nil) = sizeids(t.ids, IBY2WD);
+ al = IBY2LG;
+ }
+ for(tg := t.tags; tg != nil; tg = tg.next){
+ if((tg.ty.ok & OKsized) == OKsized)
+ continue;
+ tg.ty.ok |= OKsized;
+#ZZZ
+ if(!debug['z']){
+ (s, a) = sizeids(tg.ty.ids, sz);
+ if(a < al)
+ a = al;
+ tg.ty.size = align(s, a);
+ tg.ty.align = a;
+ }else{
+ (s, nil) = sizeids(tg.ty.ids, sz);
+ tg.ty.size = align(s, IBY2LG);
+ tg.ty.align = IBY2LG;
+ }
+ }
+ Tfn =>
+ t.size = 0;
+ t.align = 1;
+ Tainit =>
+ t.size = 0;
+ t.align = 1;
+ Talt =>
+ t.size = t.cse.nlab * 2*IBY2WD + 2*IBY2WD;
+ t.align = IBY2WD;
+ Tcase or
+ Tcasec =>
+ t.size = t.cse.nlab * 3*IBY2WD + 2*IBY2WD;
+ t.align = IBY2WD;
+ Tcasel =>
+ t.size = t.cse.nlab * 6*IBY2WD + 3*IBY2WD;
+ t.align = IBY2LG;
+ Tgoto =>
+ t.size = t.cse.nlab * IBY2WD + IBY2WD;
+ if(t.cse.iwild != nil)
+ t.size += IBY2WD;
+ t.align = IBY2WD;
+ Tiface =>
+ sz = IBY2WD;
+ for(id = t.ids; id != nil; id = id.next){
+ sz = align(sz, IBY2WD) + IBY2WD;
+ sz += len array of byte id.sym.name + 1;
+ if(id.dot.ty.kind == Tadt)
+ sz += len array of byte id.dot.sym.name + 1;
+ }
+ t.size = sz;
+ t.align = IBY2WD;
+ Texcept =>
+ t.size = 0;
+ t.align = IBY2WD;
+ }
+}
+
+sizeids(id: ref Decl, off: int): (int, int)
+{
+ al := 1;
+ for(; id != nil; id = id.next){
+ if(storespace[id.store]){
+ sizetype(id.ty);
+ #
+ # alignment can be 0 if we have
+ # illegal forward declarations.
+ # just patch a; other code will flag an error
+ #
+ a := id.ty.align;
+ if(a == 0)
+ a = 1;
+
+ if(a > al)
+ al = a;
+
+ off = align(off, a);
+ id.offset = off;
+ off += id.ty.size;
+ }
+ }
+ return (off, al);
+}
+
+align(off, align: int): int
+{
+ if(align == 0)
+ fatal("align 0");
+ while(off % align)
+ off++;
+ return off;
+}
+
+#
+# recalculate a type's size
+#
+resizetype(t: ref Type)
+{
+ if((t.ok & OKsized) == OKsized){
+ t.ok &= ~OKsized;
+ cycsizetype(t);
+ }
+}
+
+#
+# check if a module is accessable from t
+# if so, mark that module interface
+#
+modrefable(t: ref Type)
+{
+ id: ref Decl;
+
+ if(t == nil || (t.ok & OKmodref) == OKmodref)
+ return;
+ if((t.ok & OKverify) != OKverify)
+ fatal("modrefable unused type "+stypeconv(t));
+ t.ok |= OKmodref;
+ case t.kind{
+ Terror or
+ Tint or
+ Tbig or
+ Tstring or
+ Treal or
+ Tbyte or
+ Tnone or
+ Tany or
+ Tfix or
+ Tpoly =>
+ break;
+ Tchan or
+ Tref or
+ Tarray or
+ Tlist =>
+ modrefable(t.tof);
+ Tmodule =>
+ t.tof.linkall = byte 1;
+ t.decl.refs++;
+ for(id = t.ids; id != nil; id = id.next){
+ case id.store{
+ Dglobal or
+ Dfn =>
+ modrefable(id.ty);
+ Dtype =>
+ if(id.ty.kind != Tadt)
+ break;
+ for(m := id.ty.ids; m != nil; m = m.next)
+ if(m.store == Dfn)
+ modrefable(m.ty);
+ }
+ }
+ Tfn or
+ Tadt or
+ Ttuple or
+ Texception =>
+ for(id = t.ids; id != nil; id = id.next)
+ if(id.store != Dfn)
+ modrefable(id.ty);
+ for(tg := t.tags; tg != nil; tg = tg.next){
+ # if((tg.ty.ok & OKmodref) == OKmodref)
+ # continue;
+ tg.ty.ok |= OKmodref;
+ for(id = tg.ty.ids; id != nil; id = id.next)
+ modrefable(id.ty);
+ }
+ for(id = t.polys; id != nil; id = id.next)
+ modrefable(id.ty);
+ modrefable(t.tof);
+ Tadtpick =>
+ modrefable(t.decl.dot.ty);
+ * =>
+ fatal("modrefable: unknown type kind "+string t.kind);
+ }
+}
+
+gendesc(d: ref Decl, size: int, decls: ref Decl): ref Desc
+{
+ if(debug['D'])
+ print("generate desc for %s\n", dotconv(d));
+ if(ispoly(d))
+ addfnptrs(d, 0);
+ desc := usedesc(mkdesc(size, decls));
+ return desc;
+}
+
+mkdesc(size: int, d: ref Decl): ref Desc
+{
+ pmap := array[(size+8*IBY2WD-1) / (8*IBY2WD)] of { * => byte 0 };
+ n := descmap(d, pmap, 0);
+ if(n >= 0)
+ n = n / (8*IBY2WD) + 1;
+ else
+ n = 0;
+ return enterdesc(pmap, size, n);
+}
+
+mktdesc(t: ref Type): ref Desc
+{
+usedty(t);
+ if(debug['D'])
+ print("generate desc for %s\n", typeconv(t));
+ if(t.decl == nil){
+ t.decl = mkdecl(t.src, Dtype, t);
+ t.decl.sym = enter("_mktdesc_", 0);
+ }
+ if(t.decl.desc != nil)
+ return t.decl.desc;
+ pmap := array[(t.size+8*IBY2WD-1) / (8*IBY2WD)] of {* => byte 0};
+ n := tdescmap(t, pmap, 0);
+ if(n >= 0)
+ n = n / (8*IBY2WD) + 1;
+ else
+ n = 0;
+ d := enterdesc(pmap, t.size, n);
+ t.decl.desc = d;
+ return d;
+}
+
+enterdesc(map: array of byte, size, nmap: int): ref Desc
+{
+ last : ref Desc = nil;
+ for(d := descriptors; d != nil; d = d.next){
+ if(d.size > size || d.size == size && d.nmap > nmap)
+ break;
+ if(d.size == size && d.nmap == nmap){
+ c := mapcmp(d.map, map, nmap);
+ if(c == 0)
+ return d;
+ if(c > 0)
+ break;
+ }
+ last = d;
+ }
+
+ d = ref Desc(-1, 0, map, size, nmap, nil);
+ if(last == nil){
+ d.next = descriptors;
+ descriptors = d;
+ }else{
+ d.next = last.next;
+ last.next = d;
+ }
+ return d;
+}
+
+mapcmp(a, b: array of byte, n: int): int
+{
+ for(i := 0; i < n; i++)
+ if(a[i] != b[i])
+ return int a[i] - int b[i];
+ return 0;
+}
+
+usedesc(d: ref Desc): ref Desc
+{
+ d.used = 1;
+ return d;
+}
+
+#
+# create the pointer description byte map for every type in decls
+# each bit corresponds to a word, and is 1 if occupied by a pointer
+# the high bit in the byte maps the first word
+#
+descmap(decls: ref Decl, map: array of byte, start: int): int
+{
+ if(debug['D'])
+ print("descmap offset %d\n", start);
+ last := -1;
+ for(d := decls; d != nil; d = d.next){
+ if(d.store == Dtype && d.ty.kind == Tmodule
+ || d.store == Dfn
+ || d.store == Dconst)
+ continue;
+ if(d.store == Dlocal && d.link != nil)
+ continue;
+ m := tdescmap(d.ty, map, d.offset + start);
+ if(debug['D']){
+ if(d.sym != nil)
+ print("descmap %s type %s offset %d returns %d\n", d.sym.name, typeconv(d.ty), d.offset+start, m);
+ else
+ print("descmap type %s offset %d returns %d\n", typeconv(d.ty), d.offset+start, m);
+ }
+ if(m >= 0)
+ last = m;
+ }
+ return last;
+}
+
+tdescmap(t: ref Type, map: array of byte, offset: int): int
+{
+ i, e, bit: int;
+
+ if(t == nil)
+ return -1;
+
+ m := -1;
+ if(t.kind == Talt){
+ lab := t.cse.labs;
+ e = t.cse.nlab;
+ offset += IBY2WD * 2;
+ for(i = 0; i < e; i++){
+ if(lab[i].isptr){
+ bit = offset / IBY2WD % 8;
+ map[offset / (8*IBY2WD)] |= byte 1 << (7 - bit);
+ m = offset;
+ }
+ offset += 2*IBY2WD;
+ }
+ return m;
+ }
+ if(t.kind == Tcasec){
+ e = t.cse.nlab;
+ offset += IBY2WD;
+ for(i = 0; i < e; i++){
+ bit = offset / IBY2WD % 8;
+ map[offset / (8*IBY2WD)] |= byte 1 << (7 - bit);
+ offset += IBY2WD;
+ bit = offset / IBY2WD % 8;
+ map[offset / (8*IBY2WD)] |= byte 1 << (7 - bit);
+ m = offset;
+ offset += 2*IBY2WD;
+ }
+ return m;
+ }
+
+ if(tattr[t.kind].isptr){
+ bit = offset / IBY2WD % 8;
+ map[offset / (8*IBY2WD)] |= byte 1 << (7 - bit);
+ return offset;
+ }
+ if(t.kind == Tadtpick)
+ t = t.tof;
+ if(t.kind == Ttuple || t.kind == Tadt || t.kind == Texception){
+ if(debug['D'])
+ print("descmap adt offset %d\n", offset);
+ if(t.rec != byte 0)
+ fatal("illegal cyclic type "+stypeconv(t)+" in tdescmap");
+ t.rec = byte 1;
+ offset = descmap(t.ids, map, offset);
+ t.rec = byte 0;
+ return offset;
+ }
+
+ return -1;
+}
+
+tcomset: int;
+
+#
+# can a t2 be assigned to a t1?
+# any means Tany matches all types,
+# not just references
+#
+tcompat(t1, t2: ref Type, any: int): int
+{
+ if(t1 == t2)
+ return 1;
+ if(t1 == nil || t2 == nil)
+ return 0;
+ if(t2.kind == Texception && t1.kind != Texception)
+ t2 = mkextuptype(t2);
+ tcomset = 0;
+ ok := rtcompat(t1, t2, any, 0);
+ v := cleartcomrec(t1) + cleartcomrec(t2);
+ if(v != tcomset)
+ fatal("recid t1 "+stypeconv(t1)+" and t2 "+stypeconv(t2)+" not balanced in tcompat: "+string v+" "+string tcomset);
+ return ok;
+}
+
+rtcompat(t1, t2: ref Type, any: int, inaorc: int): int
+{
+ if(t1 == t2)
+ return 1;
+ if(t1 == nil || t2 == nil)
+ return 0;
+ if(t1.kind == Terror || t2.kind == Terror)
+ return 1;
+ if(t2.kind == Texception && t1.kind != Texception)
+ t2 = mkextuptype(t2);
+
+ t1.rec |= TRcom;
+ t2.rec |= TRcom;
+ case t1.kind{
+ * =>
+ fatal("unknown type "+stypeconv(t1)+" v "+stypeconv(t2)+" in rtcompat");
+ return 0;
+ Tstring =>
+ return t2.kind == Tstring || t2.kind == Tany;
+ Texception =>
+ if(t2.kind == Texception && t1.cons == t2.cons){
+ if(assumetcom(t1, t2))
+ return 1;
+ return idcompat(t1.ids, t2.ids, 0, inaorc);
+ }
+ return 0;
+ Tnone or
+ Tint or
+ Tbig or
+ Tbyte or
+ Treal =>
+ return t1.kind == t2.kind;
+ Tfix =>
+ return t1.kind == t2.kind && sametree(t1.val, t2.val);
+ Tany =>
+ if(tattr[t2.kind].isptr)
+ return 1;
+ return any;
+ Tref or
+ Tlist or
+ Tarray or
+ Tchan =>
+ if(t1.kind != t2.kind){
+ if(t2.kind == Tany)
+ return 1;
+ return 0;
+ }
+ if(t1.kind != Tref && assumetcom(t1, t2))
+ return 1;
+ return rtcompat(t1.tof, t2.tof, 0, t1.kind == Tarray || t1.kind == Tchan || inaorc);
+ Tfn =>
+ break;
+ Ttuple =>
+ if(t2.kind == Tadt && t2.tags == nil
+ || t2.kind == Ttuple){
+ if(assumetcom(t1, t2))
+ return 1;
+ return idcompat(t1.ids, t2.ids, any, inaorc);
+ }
+ if(t2.kind == Tadtpick){
+ t2.tof.rec |= TRcom;
+ if(assumetcom(t1, t2.tof))
+ return 1;
+ return idcompat(t1.ids, t2.tof.ids.next, any, inaorc);
+ }
+ return 0;
+ Tadt =>
+ if(t2.kind == Ttuple && t1.tags == nil){
+ if(assumetcom(t1, t2))
+ return 1;
+ return idcompat(t1.ids, t2.ids, any, inaorc);
+ }
+ if(t1.tags != nil && t2.kind == Tadtpick && !inaorc)
+ t2 = t2.decl.dot.ty;
+ Tadtpick =>
+ #if(t2.kind == Ttuple)
+ # return idcompat(t1.tof.ids.next, t2.ids, any, inaorc);
+ break;
+ Tmodule =>
+ if(t2.kind == Tany)
+ return 1;
+ Tpoly =>
+ if(t2.kind == Tany)
+ return 1;
+ }
+ return tequal(t1, t2);
+}
+
+#
+# add the assumption that t1 and t2 are compatable
+#
+assumetcom(t1, t2: ref Type): int
+{
+ r1, r2: ref Type;
+
+ if(t1.tcom == nil && t2.tcom == nil){
+ tcomset += 2;
+ t1.tcom = t2.tcom = t1;
+ }else{
+ if(t1.tcom == nil){
+ r1 = t1;
+ t1 = t2;
+ t2 = r1;
+ }
+ for(r1 = t1.tcom; r1 != r1.tcom; r1 = r1.tcom)
+ ;
+ for(r2 = t2.tcom; r2 != nil && r2 != r2.tcom; r2 = r2.tcom)
+ ;
+ if(r1 == r2)
+ return 1;
+ if(r2 == nil)
+ tcomset++;
+ t2.tcom = t1;
+ for(; t2 != r1; t2 = r2){
+ r2 = t2.tcom;
+ t2.tcom = r1;
+ }
+ }
+ return 0;
+}
+
+cleartcomrec(t: ref Type): int
+{
+ n := 0;
+ for(; t != nil && (t.rec & TRcom) == TRcom; t = t.tof){
+ t.rec &= ~TRcom;
+ if(t.tcom != nil){
+ t.tcom = nil;
+ n++;
+ }
+ if(t.kind == Tadtpick)
+ n += cleartcomrec(t.tof);
+ if(t.kind == Tmodule)
+ t = t.tof;
+ for(id := t.ids; id != nil; id = id.next)
+ n += cleartcomrec(id.ty);
+ for(id = t.tags; id != nil; id = id.next)
+ n += cleartcomrec(id.ty);
+ for(id = t.polys; id != nil; id = id.next)
+ n += cleartcomrec(id.ty);
+ }
+ return n;
+}
+
+#
+# id1 and id2 are the fields in an adt or tuple
+# simple structural check; ignore names
+#
+idcompat(id1, id2: ref Decl, any: int, inaorc: int): int
+{
+ for(; id1 != nil; id1 = id1.next){
+ if(id1.store != Dfield)
+ continue;
+ while(id2 != nil && id2.store != Dfield)
+ id2 = id2.next;
+ if(id2 == nil
+ || id1.store != id2.store
+ || !rtcompat(id1.ty, id2.ty, any, inaorc))
+ return 0;
+ id2 = id2.next;
+ }
+ while(id2 != nil && id2.store != Dfield)
+ id2 = id2.next;
+ return id2 == nil;
+}
+
+#
+# structural equality on types
+# t->recid is used to detect cycles
+# t->rec is used to clear t->recid
+#
+tequal(t1, t2: ref Type): int
+{
+ eqrec = 0;
+ eqset = 0;
+ ok := rtequal(t1, t2);
+ v := cleareqrec(t1) + cleareqrec(t2);
+ if(0 && v != eqset)
+ fatal("recid t1 "+stypeconv(t1)+" and t2 "+stypeconv(t2)+" not balanced in tequal: "+string v+" "+string eqset);
+ eqset = 0;
+ return ok;
+}
+
+rtequal(t1, t2: ref Type): int
+{
+ #
+ # this is just a shortcut
+ #
+ if(t1 == t2)
+ return 1;
+
+ if(t1 == nil || t2 == nil)
+ return 0;
+ if(t1.kind == Terror || t2.kind == Terror)
+ return 1;
+
+ if(t1.kind != t2.kind)
+ return 0;
+
+ if(t1.eq != nil && t2.eq != nil)
+ return t1.eq == t2.eq;
+
+ t1.rec |= TReq;
+ t2.rec |= TReq;
+ case t1.kind{
+ * =>
+ fatal("bogus type "+stypeconv(t1)+" vs "+stypeconv(t2)+" in rtequal");
+ return 0;
+ Tnone or
+ Tbig or
+ Tbyte or
+ Treal or
+ Tint or
+ Tstring =>
+ #
+ # this should always be caught by t1 == t2 check
+ #
+ fatal("bogus value type "+stypeconv(t1)+" vs "+stypeconv(t2)+" in rtequal");
+ return 1;
+ Tfix =>
+ return sametree(t1.val, t2.val);
+ Tref or
+ Tlist or
+ Tarray or
+ Tchan =>
+ if(t1.kind != Tref && assumeteq(t1, t2))
+ return 1;
+ return rtequal(t1.tof, t2.tof);
+ Tfn =>
+ if(t1.varargs != t2.varargs)
+ return 0;
+ if(!idequal(t1.ids, t2.ids, 0, storespace))
+ return 0;
+ # if(!idequal(t1.polys, t2.polys, 1, nil))
+ if(!pyequal(t1, t2))
+ return 0;
+ return rtequal(t1.tof, t2.tof);
+ Ttuple or
+ Texception =>
+ if(t1.kind != t2.kind || t1.cons != t2.cons)
+ return 0;
+ if(assumeteq(t1, t2))
+ return 1;
+ return idequal(t1.ids, t2.ids, 0, storespace);
+ Tadt or
+ Tadtpick or
+ Tmodule =>
+ if(assumeteq(t1, t2))
+ return 1;
+
+ #
+ # compare interfaces when comparing modules
+ #
+ if(t1.kind == Tmodule)
+ return idequal(t1.tof.ids, t2.tof.ids, 1, nil);
+
+ #
+ # picked adts; check parent,
+ # assuming equiv picked fields,
+ # then check picked fields are equiv
+ #
+ if(t1.kind == Tadtpick && !rtequal(t1.decl.dot.ty, t2.decl.dot.ty))
+ return 0;
+
+ #
+ # adts with pick tags: check picked fields for equality
+ #
+ if(!idequal(t1.tags, t2.tags, 1, nil))
+ return 0;
+
+ # if(!idequal(t1.polys, t2.polys, 1, nil))
+ if(!pyequal(t1, t2))
+ return 0;
+ return idequal(t1.ids, t2.ids, 1, storespace);
+ Tpoly =>
+ if(assumeteq(t1, t2))
+ return 1;
+ if(t1.decl.sym != t2.decl.sym)
+ return 0;
+ return idequal(t1.ids, t2.ids, 1, nil);
+ }
+}
+
+assumeteq(t1, t2: ref Type): int
+{
+ r1, r2: ref Type;
+
+ if(t1.teq == nil && t2.teq == nil){
+ eqrec++;
+ eqset += 2;
+ t1.teq = t2.teq = t1;
+ }else{
+ if(t1.teq == nil){
+ r1 = t1;
+ t1 = t2;
+ t2 = r1;
+ }
+ for(r1 = t1.teq; r1 != r1.teq; r1 = r1.teq)
+ ;
+ for(r2 = t2.teq; r2 != nil && r2 != r2.teq; r2 = r2.teq)
+ ;
+ if(r1 == r2)
+ return 1;
+ if(r2 == nil)
+ eqset++;
+ t2.teq = t1;
+ for(; t2 != r1; t2 = r2){
+ r2 = t2.teq;
+ t2.teq = r1;
+ }
+ }
+ return 0;
+}
+
+#
+# checking structural equality for modules, adts, tuples, and fns
+#
+idequal(id1, id2: ref Decl, usenames: int, storeok: array of int): int
+{
+ #
+ # this is just a shortcut
+ #
+ if(id1 == id2)
+ return 1;
+
+ for(; id1 != nil; id1 = id1.next){
+ if(storeok != nil && !storeok[id1.store])
+ continue;
+ while(id2 != nil && storeok != nil && !storeok[id2.store])
+ id2 = id2.next;
+ if(id2 == nil
+ || usenames && id1.sym != id2.sym
+ || id1.store != id2.store
+ || id1.implicit != id2.implicit
+ || id1.cyc != id2.cyc
+ || (id1.dot == nil) != (id2.dot == nil)
+ || id1.dot != nil && id2.dot != nil && id1.dot.ty.kind != id2.dot.ty.kind
+ || !rtequal(id1.ty, id2.ty))
+ return 0;
+ id2 = id2.next;
+ }
+ while(id2 != nil && storeok != nil && !storeok[id2.store])
+ id2 = id2.next;
+ return id1 == nil && id2 == nil;
+}
+
+
+pyequal(t1: ref Type, t2: ref Type): int
+{
+ pt1, pt2: ref Type;
+ id1, id2: ref Decl;
+
+ if(t1 == t2)
+ return 1;
+ id1 = t1.polys;
+ id2 = t2.polys;
+ for(; id1 != nil; id1 = id1.next){
+ if(id2 == nil)
+ return 0;
+ pt1 = id1.ty;
+ pt2 = id2.ty;
+ if(!rtequal(pt1, pt2)){
+ if(t1.tmap != nil)
+ pt1 = valtmap(pt1, t1.tmap);
+ if(t2.tmap != nil)
+ pt2 = valtmap(pt2, t2.tmap);
+ if(!rtequal(pt1, pt2))
+ return 0;
+ }
+ id2 = id2.next;
+ }
+ return id1 == nil && id2 == nil;
+}
+
+cleareqrec(t: ref Type): int
+{
+ n := 0;
+ for(; t != nil && (t.rec & TReq) == TReq; t = t.tof){
+ t.rec &= ~TReq;
+ if(t.teq != nil){
+ t.teq = nil;
+ n++;
+ }
+ if(t.kind == Tadtpick)
+ n += cleareqrec(t.decl.dot.ty);
+ if(t.kind == Tmodule)
+ t = t.tof;
+ for(id := t.ids; id != nil; id = id.next)
+ n += cleareqrec(id.ty);
+ for(id = t.tags; id != nil; id = id.next)
+ n += cleareqrec(id.ty);
+ for(id = t.polys; id != nil; id = id.next)
+ n += cleareqrec(id.ty);
+ }
+ return n;
+}
+
+raisescompat(n1: ref Node, n2: ref Node): int
+{
+ if(n1 == n2)
+ return 1;
+ if(n2 == nil)
+ return 1; # no need to repeat in definition if given in declaration
+ if(n1 == nil)
+ return 0;
+ for((n1, n2) = (n1.left, n2.left); n1 != nil && n2 != nil; (n1, n2) = (n1.right, n2.right)){
+ if(n1.left.decl != n2.left.decl)
+ return 0;
+ }
+ return n1 == n2;
+}
+
+# t1 a polymorphic type
+fnunify(t1: ref Type, t2: ref Type, tp: ref Tpair, swapped: int): (int, ref Tpair)
+{
+ id, ids: ref Decl;
+ sym: ref Sym;
+ ok: int;
+
+ for(ids = t1.ids; ids != nil; ids = ids.next){
+ sym = ids.sym;
+ (id, nil) = fnlookup(sym, t2);
+ if(id != nil)
+ usetype(id.ty);
+ if(id == nil){
+ if(dowarn)
+ error(unifysrc.start, "type " + typeconv(t2) + " does not have a '" + sym.name + "' function");
+ return (0, tp);
+ }
+ else if(id.ty.kind != Tfn){
+ if(dowarn)
+ error(unifysrc.start, typeconv(id.ty) + " is not a function");
+ return (0, tp);
+ }
+ else{
+ (ok, tp) = rtunify(ids.ty, id.ty, tp, !swapped);
+ if(!ok){
+ if(dowarn)
+ error(unifysrc.start, typeconv(ids.ty) + " and " + typeconv(id.ty) + " are not compatible wrt " + sym.name);
+ return (0, tp);
+ }
+ }
+ }
+ return (1, tp);
+}
+
+fncleareqrec(t1: ref Type, t2: ref Type): int
+{
+ id, ids: ref Decl;
+ n: int;
+
+ n = 0;
+ n += cleareqrec(t1);
+ n += cleareqrec(t2);
+ for(ids = t1.ids; ids != nil; ids = ids.next){
+ (id, nil) = fnlookup(ids.sym, t2);
+ if(id == nil)
+ continue;
+ else{
+ n += cleareqrec(ids.ty);
+ n += cleareqrec(id.ty);
+ }
+ }
+ return n;
+}
+
+tunify(t1: ref Type, t2: ref Type): (int, ref Tpair)
+{
+ v: int;
+ p: ref Tpair;
+
+ eqrec = 0;
+ eqset = 0;
+ (ok, tp) := rtunify(t1, t2, nil, 0);
+ v = cleareqrec(t1) + cleareqrec(t2);
+ for(p = tp; p != nil; p = p.nxt)
+ v += fncleareqrec(p.t1, p.t2);
+ if(0 && v != eqset)
+ fatal("recid t1 " + stypeconv(t1) + " and t2 " + stypeconv(t2) + " not balanced in tunify: " + string v + " " + string eqset);
+ return (ok, tp);
+}
+
+rtunify(t1: ref Type, t2: ref Type, tp: ref Tpair, swapped: int): (int, ref Tpair)
+{
+ ok: int;
+
+ t1 = valtmap(t1, tp);
+ t2 = valtmap(t2, tp);
+ if(t1 == t2)
+ return (1, tp);
+ if(t1 == nil || t2 == nil)
+ return (0, tp);
+ if(t1.kind == Terror || t2.kind == Terror)
+ return (1, tp);
+ if(t1.kind != Tpoly && t2.kind == Tpoly){
+ (t1, t2) = (t2, t1);
+ swapped = !swapped;
+ }
+ if(t1.kind == Tpoly){
+ # if(typein(t1, t2))
+ # return (0, tp);
+ if(!tattr[t2.kind].isptr)
+ return (0, tp);
+ if(t2.kind != Tany)
+ tp = addtmap(t1, t2, tp);
+ return fnunify(t1, t2, tp, swapped);
+ }
+ if(t1.kind != Tany && t2.kind == Tany){
+ (t1, t2) = (t2, t1);
+ swapped = !swapped;
+ }
+ if(t1.kind == Tadt && t1.tags != nil && t2.kind == Tadtpick && !swapped)
+ t2 = t2.decl.dot.ty;
+ if(t2.kind == Tadt && t2.tags != nil && t1.kind == Tadtpick && swapped)
+ t1 = t1.decl.dot.ty;
+ if(t1.kind != Tany && t1.kind != t2.kind)
+ return (0, tp);
+ t1.rec |= TReq;
+ t2.rec |= TReq;
+ case(t1.kind){
+ * =>
+ return (tequal(t1, t2), tp);
+ Tany =>
+ return (tattr[t2.kind].isptr, tp);
+ Tref or
+ Tlist or
+ Tarray or
+ Tchan =>
+ if(t1.kind != Tref && assumeteq(t1, t2))
+ return (1, tp);
+ return rtunify(t1.tof, t2.tof, tp, swapped);
+ Tfn =>
+ (ok, tp) = idunify(t1.ids, t2.ids, tp, swapped);
+ if(!ok)
+ return (0, tp);
+ (ok, tp) = idunify(t1.polys, t2.polys, tp, swapped);
+ if(!ok)
+ return (0, tp);
+ return rtunify(t1.tof, t2.tof, tp, swapped);
+ Ttuple =>
+ if(assumeteq(t1, t2))
+ return (1, tp);
+ return idunify(t1.ids, t2.ids, tp, swapped);
+ Tadt or
+ Tadtpick =>
+ if(assumeteq(t1, t2))
+ return (1, tp);
+ (ok, tp) = idunify(t1.polys, t2.polys, tp, swapped);
+ if(!ok)
+ return (0, tp);
+ (ok, tp) = idunify(t1.tags, t2.tags, tp, swapped);
+ if(!ok)
+ return (0, tp);
+ return idunify(t1.ids, t2.ids, tp, swapped);
+ Tmodule =>
+ if(assumeteq(t1, t2))
+ return (1, tp);
+ return idunify(t1.tof.ids, t2.tof.ids, tp, swapped);
+ Tpoly =>
+ return (t1 == t2, tp);
+ }
+ return (1, tp);
+}
+
+idunify(id1: ref Decl, id2: ref Decl, tp: ref Tpair, swapped: int): (int, ref Tpair)
+{
+ ok: int;
+
+ if(id1 == id2)
+ return (1, tp);
+ for(; id1 != nil; id1 = id1.next){
+ if(id2 == nil)
+ return (0, tp);
+ (ok, tp) = rtunify(id1.ty, id2.ty, tp, swapped);
+ if(!ok)
+ return (0, tp);
+ id2 = id2.next;
+ }
+ return (id1 == nil && id2 == nil, tp);
+}
+
+polyequal(id1: ref Decl, id2: ref Decl): int
+{
+ # allow id2 list to have an optional for clause
+ ck2 := 0;
+ for(d := id2; d != nil; d = d.next)
+ if(d.ty.ids != nil)
+ ck2 = 1;
+ for(; id1 != nil; id1 = id1.next){
+ if(id2 == nil
+ || id1.sym != id2.sym
+ || id1.ty.decl != nil && id2.ty.decl != nil && id1.ty.decl.sym != id2.ty.decl.sym)
+ return 0;
+ if(ck2 && !idequal(id1.ty.ids, id2.ty.ids, 1, nil))
+ return 0;
+ id2 = id2.next;
+ }
+ return id1 == nil && id2 == nil;
+}
+
+calltype(f: ref Type, a: ref Node, rt: ref Type): ref Type
+{
+ t: ref Type;
+ id, first, last: ref Decl;
+
+ first = last = nil;
+ t = mktype(f.src.start, f.src.stop, Tfn, rt, nil);
+ if(f.kind == Tref)
+ t.polys = f.tof.polys;
+ else
+ t.polys = f.polys;
+ for( ; a != nil; a = a.right){
+ id = mkdecl(f.src, Darg, a.left.ty);
+ if(last == nil)
+ first = id;
+ else
+ last.next = id;
+ last = id;
+ }
+ t.ids = first;
+ if(f.kind == Tref)
+ t = mktype(f.src.start, f.src.stop, Tref, t, nil);
+ return t;
+}
+
+duptype(t: ref Type): ref Type
+{
+ nt: ref Type;
+
+ nt = ref Type;
+ *nt = *t;
+ nt.ok &= ~(OKverify|OKref|OKclass|OKsized|OKcycsize|OKcyc);
+ nt.flags |= INST;
+ nt.eq = nil;
+ nt.sbl = -1;
+ if(t.decl != nil && (nt.kind == Tadt || nt.kind == Tadtpick || nt.kind == Ttuple)){
+ nt.decl = dupdecl(t.decl);
+ nt.decl.ty = nt;
+ nt.decl.link = t.decl;
+ if(t.decl.dot != nil){
+ nt.decl.dot = dupdecl(t.decl.dot);
+ nt.decl.dot.link = t.decl.dot;
+ }
+ }
+ else
+ nt.decl = nil;
+ return nt;
+}
+
+dpolys(ids: ref Decl): int
+{
+ p: ref Decl;
+
+ for(p = ids; p != nil; p = p.next)
+ if(tpolys(p.ty))
+ return 1;
+ return 0;
+}
+
+tpolys(t: ref Type): int
+{
+ v: int;
+ tyl: ref Typelist;
+
+ if(t == nil)
+ return 0;
+ if(int(t.flags&(POLY|NOPOLY)))
+ return int(t.flags&POLY);
+ case(t.kind){
+ * =>
+ v = 0;
+ break;
+ Tarrow or
+ Tdot or
+ Tpoly =>
+ v = 1;
+ break;
+ Tref or
+ Tlist or
+ Tarray or
+ Tchan =>
+ v = tpolys(t.tof);
+ break;
+ Tid =>
+ v = tpolys(t.decl.ty);
+ break;
+ Tinst =>
+ for(tyl = t.tlist; tyl != nil; tyl = tyl.nxt)
+ if(tpolys(tyl.t)){
+ v = 1;
+ break;
+ }
+ v = tpolys(t.tof);
+ break;
+ Tfn or
+ Tadt or
+ Tadtpick or
+ Ttuple or
+ Texception =>
+ if(t.polys != nil){
+ v = 1;
+ break;
+ }
+ if(int(t.rec&TRvis))
+ return 0;
+ t.rec |= TRvis;
+ v = tpolys(t.tof) || dpolys(t.polys) || dpolys(t.ids) || dpolys(t.tags);
+ t.rec &= ~TRvis;
+ if(t.kind == Tadtpick && v == 0)
+ v = tpolys(t.decl.dot.ty);
+ break;
+ }
+ if(v)
+ t.flags |= POLY;
+ else
+ t.flags |= NOPOLY;
+ return v;
+}
+
+doccurs(ids: ref Decl, tp: ref Tpair): int
+{
+ p: ref Decl;
+
+ for(p = ids; p != nil; p = p.next){
+ if(toccurs(p.ty, tp))
+ return 1;
+ }
+ return 0;
+}
+
+toccurs(t: ref Type, tp: ref Tpair): int
+{
+ o: int;
+
+ if(t == nil)
+ return 0;
+ if(!int(t.flags&(POLY|NOPOLY)))
+ tpolys(t);
+ if(int(t.flags&NOPOLY))
+ return 0;
+ case(t.kind){
+ * =>
+ fatal("unknown type " + string t.kind + " in toccurs");
+ Tnone or
+ Tbig or
+ Tbyte or
+ Treal or
+ Tint or
+ Tstring or
+ Tfix or
+ Tmodule or
+ Terror =>
+ return 0;
+ Tarrow or
+ Tdot =>
+ return 1;
+ Tpoly =>
+ return valtmap(t, tp) != t;
+ Tref or
+ Tlist or
+ Tarray or
+ Tchan =>
+ return toccurs(t.tof, tp);
+ Tid =>
+ return toccurs(t.decl.ty, tp);
+ Tinst =>
+ for(tyl := t.tlist; tyl != nil; tyl = tyl.nxt)
+ if(toccurs(tyl.t, tp))
+ return 1;
+ return toccurs(t.tof, tp);
+ Tfn or
+ Tadt or
+ Tadtpick or
+ Ttuple or
+ Texception =>
+ if(int(t.rec&TRvis))
+ return 0;
+ t.rec |= TRvis;
+ o = toccurs(t.tof, tp) || doccurs(t.polys, tp) || doccurs(t.ids, tp) || doccurs(t.tags, tp);
+ t.rec &= ~TRvis;
+ if(t.kind == Tadtpick && o == 0)
+ o = toccurs(t.decl.dot.ty, tp);
+ return o;
+ }
+ return 0;
+}
+
+expandids(ids: ref Decl, adtt: ref Decl, tp: ref Tpair, sym: int): (ref Decl, ref Tpair)
+{
+ p, q, nids, last: ref Decl;
+
+ nids = last = nil;
+ for(p = ids; p != nil; p = p.next){
+ q = dupdecl(p);
+ (q.ty, tp) = expandtype(p.ty, nil, adtt, tp);
+ if(sym && q.ty.decl != nil)
+ q.sym = q.ty.decl.sym;
+ if(q.store == Dfn)
+ q.link = p;
+ if(nids == nil)
+ nids = q;
+ else
+ last.next = q;
+ last = q;
+ }
+ return (nids, tp);
+}
+
+expandtype(t: ref Type, instt: ref Type, adtt: ref Decl, tp: ref Tpair): (ref Type, ref Tpair)
+{
+ nt: ref Type;
+
+ if(t == nil)
+ return (nil, tp);
+ if(!toccurs(t, tp))
+ return (t, tp);
+ case(t.kind){
+ * =>
+ fatal("unknown type " + string t.kind + " in expandtype");
+ Tpoly =>
+ return (valtmap(t, tp), tp);
+ Tref or
+ Tlist or
+ Tarray or
+ Tchan =>
+ nt = duptype(t);
+ (nt.tof, tp) = expandtype(t.tof, nil, adtt, tp);
+ return (nt, tp);
+ Tid =>
+ return expandtype(idtype(t), nil, adtt, tp);
+ Tdot =>
+ return expandtype(dottype(t, adtt), nil, adtt, tp);
+ Tarrow =>
+ return expandtype(arrowtype(t, adtt), nil, adtt, tp);
+ Tinst =>
+ if((nt = valtmap(t, tp)) != t)
+ return (nt, tp);
+ (t, tp) = insttype(t, adtt, tp);
+ return expandtype(t, nil, adtt, tp);
+ Tfn or
+ Tadt or
+ Tadtpick or
+ Ttuple or
+ Texception =>
+ if((nt = valtmap(t, tp)) != t)
+ return (nt, tp);
+ if(t.kind == Tadt)
+ adtt = t.decl;
+ nt = duptype(t);
+ tp = addtmap(t, nt, tp);
+ if(instt != nil)
+ tp = addtmap(instt, nt, tp);
+ (nt.tof, tp) = expandtype(t.tof, nil, adtt, tp);
+ (nt.polys, tp) = expandids(t.polys, adtt, tp, 1);
+ (nt.ids, tp) = expandids(t.ids, adtt, tp, 0);
+ (nt.tags, tp) = expandids(t.tags, adtt, tp, 0);
+ if(t.kind == Tadt){
+ for(ids := nt.tags; ids != nil; ids = ids.next)
+ ids.ty.decl.dot = nt.decl;
+ }
+ if(t.kind == Tadtpick){
+ (nt.decl.dot.ty, tp) = expandtype(t.decl.dot.ty, nil, adtt, tp);
+ }
+ if(t.tmap != nil){
+ nt.tmap = nil;
+ for(p := t.tmap; p != nil; p = p.nxt)
+ nt.tmap = addtmap(valtmap(p.t1, tp), valtmap(p.t2, tp), nt.tmap);
+ }
+ return (nt, tp);
+ }
+ return (nil, tp);
+}
+
+#
+# create type signatures
+# sign the same information used
+# for testing type equality
+#
+sign(d: ref Decl): int
+{
+ t := d.ty;
+ if(t.sig != 0)
+ return t.sig;
+
+ if(ispoly(d))
+ rmfnptrs(d);
+
+ sigend := -1;
+ sigalloc := 1024;
+ sig: array of byte;
+ while(sigend < 0 || sigend >= sigalloc){
+ sigalloc *= 2;
+ sig = array[sigalloc] of byte;
+ eqrec = 0;
+ sigend = rtsign(t, sig, 0);
+ v := clearrec(t);
+ if(v != eqrec)
+ fatal("recid not balanced in sign: "+string v+" "+string eqrec);
+ eqrec = 0;
+ }
+
+ if(signdump != "" && dotconv(d) == signdump){
+ print("sign %s len %d\n", dotconv(d), sigend);
+ print("%s\n", string sig[:sigend]);
+ }
+
+ md5sig := array[Keyring->MD5dlen] of {* => byte 0};
+ md5(sig, sigend, md5sig, nil);
+
+ for(i := 0; i < Keyring->MD5dlen; i += 4)
+ t.sig ^= int md5sig[i+0] | (int md5sig[i+1]<<8) | (int md5sig[i+2]<<16) | (int md5sig[i+3]<<24);
+
+ if(debug['S'])
+ print("signed %s type %s len %d sig %#ux\n", dotconv(d), typeconv(t), sigend, t.sig);
+ return t.sig;
+}
+
+SIGSELF: con byte 'S';
+SIGVARARGS: con byte '*';
+SIGCYC: con byte 'y';
+SIGREC: con byte '@';
+
+sigkind := array[Tend] of
+{
+ Tnone => byte 'n',
+ Tadt => byte 'a',
+ Tadtpick => byte 'p',
+ Tarray => byte 'A',
+ Tbig => byte 'B',
+ Tbyte => byte 'b',
+ Tchan => byte 'C',
+ Treal => byte 'r',
+ Tfn => byte 'f',
+ Tint => byte 'i',
+ Tlist => byte 'L',
+ Tmodule => byte 'm',
+ Tref => byte 'R',
+ Tstring => byte 's',
+ Ttuple => byte 't',
+ Texception => byte 'e',
+ Tfix => byte 'x',
+ Tpoly => byte 'P',
+
+ * => byte 0,
+};
+
+rtsign(t: ref Type, sig: array of byte, spos: int): int
+{
+ id: ref Decl;
+
+ if(t == nil)
+ return spos;
+
+ if(spos < 0 || spos + 8 >= len sig)
+ return -1;
+
+ if(t.eq != nil && t.eq.id){
+ if(t.eq.id < 0 || t.eq.id > eqrec)
+ fatal("sign rec "+typeconv(t)+" "+string t.eq.id+" "+string eqrec);
+
+ sig[spos++] = SIGREC;
+ name := array of byte string t.eq.id;
+ if(spos + len name > len sig)
+ return -1;
+ sig[spos:] = name;
+ spos += len name;
+ return spos;
+ }
+ if(t.eq != nil){
+ eqrec++;
+ t.eq.id = eqrec;
+ }
+
+ kind := sigkind[t.kind];
+ sig[spos++] = kind;
+ if(kind == byte 0)
+ fatal("no sigkind for "+typeconv(t));
+
+ t.rec = byte 1;
+ case t.kind{
+ * =>
+ fatal("bogus type "+stypeconv(t)+" in rtsign");
+ return -1;
+ Tnone or
+ Tbig or
+ Tbyte or
+ Treal or
+ Tint or
+ Tstring or
+ Tpoly =>
+ return spos;
+ Tfix =>
+ name := array of byte string t.val.c.rval;
+ if(spos + len name - 1 >= len sig)
+ return -1;
+ sig[spos: ] = name;
+ spos += len name;
+ return spos;
+ Tref or
+ Tlist or
+ Tarray or
+ Tchan =>
+ return rtsign(t.tof, sig, spos);
+ Tfn =>
+ if(t.varargs != byte 0)
+ sig[spos++] = SIGVARARGS;
+ if(t.polys != nil)
+ spos = idsign(t.polys, 0, sig, spos);
+ spos = idsign(t.ids, 0, sig, spos);
+ if(t.eraises != nil)
+ spos = raisessign(t.eraises, sig, spos);
+ return rtsign(t.tof, sig, spos);
+ Ttuple =>
+ return idsign(t.ids, 0, sig, spos);
+ Tadt =>
+ #
+ # this is a little different than in rtequal,
+ # since we flatten the adt we used to represent the globals
+ #
+ if(t.eq == nil){
+ if(t.decl.sym.name != ".mp")
+ fatal("no t.eq field for "+typeconv(t));
+ spos--;
+ for(id = t.ids; id != nil; id = id.next){
+ spos = idsign1(id, 1, sig, spos);
+ if(spos < 0 || spos >= len sig)
+ return -1;
+ sig[spos++] = byte ';';
+ }
+ return spos;
+ }
+ if(t.polys != nil)
+ spos = idsign(t.polys, 0, sig, spos);
+ spos = idsign(t.ids, 1, sig, spos);
+ if(spos < 0 || t.tags == nil)
+ return spos;
+
+ #
+ # convert closing ')' to a ',', then sign any tags
+ #
+ sig[spos-1] = byte ',';
+ for(tg := t.tags; tg != nil; tg = tg.next){
+ name := array of byte (tg.sym.name + "=>");
+ if(spos + len name > len sig)
+ return -1;
+ sig[spos:] = name;
+ spos += len name;
+
+ spos = rtsign(tg.ty, sig, spos);
+ if(spos < 0 || spos >= len sig)
+ return -1;
+
+ if(tg.next != nil)
+ sig[spos++] = byte ',';
+ }
+ if(spos >= len sig)
+ return -1;
+ sig[spos++] = byte ')';
+ return spos;
+ Tadtpick =>
+ spos = idsign(t.ids, 1, sig, spos);
+ if(spos < 0)
+ return spos;
+ return rtsign(t.decl.dot.ty, sig, spos);
+ Tmodule =>
+ if(t.tof.linkall == byte 0)
+ fatal("signing a narrowed module");
+
+ if(spos >= len sig)
+ return -1;
+ sig[spos++] = byte '{';
+ for(id = t.tof.ids; id != nil; id = id.next){
+ if(id.tag)
+ continue;
+ if(id.sym.name == ".mp"){
+ spos = rtsign(id.ty, sig, spos);
+ if(spos < 0)
+ return -1;
+ continue;
+ }
+ spos = idsign1(id, 1, sig, spos);
+ if(spos < 0 || spos >= len sig)
+ return -1;
+ sig[spos++] = byte ';';
+ }
+ if(spos >= len sig)
+ return -1;
+ sig[spos++] = byte '}';
+ return spos;
+ }
+}
+
+idsign(id: ref Decl, usenames: int, sig: array of byte, spos: int): int
+{
+ if(spos >= len sig)
+ return -1;
+ sig[spos++] = byte '(';
+ first := 1;
+ for(; id != nil; id = id.next){
+ if(id.store == Dlocal)
+ fatal("local "+id.sym.name+" in idsign");
+
+ if(!storespace[id.store])
+ continue;
+
+ if(!first){
+ if(spos >= len sig)
+ return -1;
+ sig[spos++] = byte ',';
+ }
+
+ spos = idsign1(id, usenames, sig, spos);
+ if(spos < 0)
+ return -1;
+ first = 0;
+ }
+ if(spos >= len sig)
+ return -1;
+ sig[spos++] = byte ')';
+ return spos;
+}
+
+idsign1(id: ref Decl, usenames: int, sig: array of byte, spos: int): int
+{
+ if(usenames){
+ name := array of byte (id.sym.name+":");
+ if(spos + len name >= len sig)
+ return -1;
+ sig[spos:] = name;
+ spos += len name;
+ }
+
+ if(spos + 2 >= len sig)
+ return -1;
+
+ if(id.implicit != byte 0)
+ sig[spos++] = SIGSELF;
+
+ if(id.cyc != byte 0)
+ sig[spos++] = SIGCYC;
+
+ return rtsign(id.ty, sig, spos);
+}
+
+raisessign(n: ref Node, sig: array of byte, spos: int): int
+{
+ if(spos >= len sig)
+ return -1;
+ sig[spos++] = byte '(';
+ for(nn := n.left; nn != nil; nn = nn.right){
+ s := array of byte nn.left.decl.sym.name;
+ if(spos+len s - 1 >= len sig)
+ return -1;
+ sig[spos: ] = s;
+ spos += len s;
+ if(nn.right != nil){
+ if(spos >= len sig)
+ return -1;
+ sig[spos++] = byte ',';
+ }
+ }
+ if(spos >= len sig)
+ return -1;
+ sig[spos++] = byte ')';
+ return spos;
+}
+
+clearrec(t: ref Type): int
+{
+ id: ref Decl;
+
+ n := 0;
+ for(; t != nil && t.rec != byte 0; t = t.tof){
+ t.rec = byte 0;
+ if(t.eq != nil && t.eq.id != 0){
+ t.eq.id = 0;
+ n++;
+ }
+ if(t.kind == Tmodule){
+ for(id = t.tof.ids; id != nil; id = id.next)
+ n += clearrec(id.ty);
+ return n;
+ }
+ if(t.kind == Tadtpick)
+ n += clearrec(t.decl.dot.ty);
+ for(id = t.ids; id != nil; id = id.next)
+ n += clearrec(id.ty);
+ for(id = t.tags; id != nil; id = id.next)
+ n += clearrec(id.ty);
+ for(id = t.polys; id != nil; id = id.next)
+ n += clearrec(id.ty);
+ }
+ return n;
+}
+
+# must a variable of the given type be zeroed ? (for uninitialized declarations inside loops)
+tmustzero(t : ref Type) : int
+{
+ if(t==nil)
+ return 0;
+ if(tattr[t.kind].isptr)
+ return 1;
+ if(t.kind == Tadtpick)
+ t = t.tof;
+ if(t.kind == Ttuple || t.kind == Tadt)
+ return mustzero(t.ids);
+ return 0;
+}
+
+mustzero(decls : ref Decl) : int
+{
+ d : ref Decl;
+
+ for (d = decls; d != nil; d = d.next)
+ if (tmustzero(d.ty))
+ return 1;
+ return 0;
+}
+
+typeconv(t: ref Type): string
+{
+ if(t == nil)
+ return "nothing";
+ return tprint(t);
+}
+
+stypeconv(t: ref Type): string
+{
+ if(t == nil)
+ return "nothing";
+ return stprint(t);
+}
+
+tprint(t: ref Type): string
+{
+ id: ref Decl;
+
+ if(t == nil)
+ return "";
+ s := "";
+ if(t.kind < 0 || t.kind >= Tend){
+ s += "kind ";
+ s += string t.kind;
+ return s;
+ }
+ if(t.pr != byte 0 && t.decl != nil){
+ if(t.decl.dot != nil && !isimpmod(t.decl.dot.sym)){
+ s += t.decl.dot.sym.name;
+ s += "->";
+ }
+ s += t.decl.sym.name;
+ return s;
+ }
+ t.pr = byte 1;
+ case t.kind{
+ Tarrow =>
+ s += tprint(t.tof);
+ s += "->";
+ s += t.decl.sym.name;
+ Tdot =>
+ s += tprint(t.tof);
+ s += ".";
+ s += t.decl.sym.name;
+ Tid or
+ Tpoly =>
+ s += t.decl.sym.name;
+ Tinst =>
+ s += tprint(t.tof);
+ s += "[";
+ for(tyl := t.tlist; tyl != nil; tyl = tyl.nxt){
+ s += tprint(tyl.t);
+ if(tyl.nxt != nil)
+ s += ", ";
+ }
+ s += "]";
+ Tint or
+ Tbig or
+ Tstring or
+ Treal or
+ Tbyte or
+ Tany or
+ Tnone or
+ Terror or
+ Tainit or
+ Talt or
+ Tcase or
+ Tcasel or
+ Tcasec or
+ Tgoto or
+ Tiface or
+ Texception or
+ Texcept =>
+ s += kindname[t.kind];
+ Tfix =>
+ s += kindname[t.kind] + "(" + expconv(t.val) + ")";
+ Tref =>
+ s += "ref ";
+ s += tprint(t.tof);
+ Tchan or
+ Tarray or
+ Tlist =>
+ s += kindname[t.kind];
+ s += " of ";
+ s += tprint(t.tof);
+ Tadtpick =>
+ s += t.decl.dot.sym.name + "." + t.decl.sym.name;
+ Tadt =>
+ if(t.decl.dot != nil && !isimpmod(t.decl.dot.sym))
+ s += t.decl.dot.sym.name + "->";
+ s += t.decl.sym.name;
+ if(t.polys != nil){
+ s += "[";
+ for(id = t.polys; id != nil; id = id.next){
+ if(t.tmap != nil)
+ s += tprint(valtmap(id.ty, t.tmap));
+ else
+ s += id.sym.name;
+ if(id.next != nil)
+ s += ", ";
+ }
+ s += "]";
+ }
+ Tmodule =>
+ s += t.decl.sym.name;
+ Ttuple =>
+ s += "(";
+ for(id = t.ids; id != nil; id = id.next){
+ s += tprint(id.ty);
+ if(id.next != nil)
+ s += ", ";
+ }
+ s += ")";
+ Tfn =>
+ s += "fn";
+ if(t.polys != nil){
+ s += "[";
+ for(id = t.polys; id != nil; id = id.next){
+ s += id.sym.name;
+ if(id.next != nil)
+ s += ", ";
+ }
+ s += "]";
+ }
+ s += "(";
+ for(id = t.ids; id != nil; id = id.next){
+ if(id.sym == nil)
+ s += "nil: ";
+ else{
+ s += id.sym.name;
+ s += ": ";
+ }
+ if(id.implicit != byte 0)
+ s += "self ";
+ s += tprint(id.ty);
+ if(id.next != nil)
+ s += ", ";
+ }
+ if(t.varargs != byte 0 && t.ids != nil)
+ s += ", *";
+ else if(t.varargs != byte 0)
+ s += "*";
+ if(t.tof != nil && t.tof.kind != Tnone){
+ s += "): ";
+ s += tprint(t.tof);
+ }else
+ s += ")";
+ * =>
+ yyerror("tprint: unknown type kind "+string t.kind);
+ }
+ t.pr = byte 0;
+ return s;
+}
+
+stprint(t: ref Type): string
+{
+ if(t == nil)
+ return "";
+ s := "";
+ case t.kind{
+ Tid =>
+ s += "id ";
+ s += t.decl.sym.name;
+ Tadt or
+ Tadtpick or
+ Tmodule =>
+ return kindname[t.kind] + " " + tprint(t);
+ }
+ return tprint(t);
+}
+
+# generalize ref P.A, ref P.B to ref P
+
+# tparent(t1: ref Type, t2: ref Type): ref Type
+# {
+# if(t1 == nil || t2 == nil || t1.kind != Tref || t2.kind != Tref)
+# return t1;
+# t1 = t1.tof;
+# t2 = t2.tof;
+# if(t1 == nil || t2 == nil || t1.kind != Tadtpick || t2.kind != Tadtpick)
+# return t1;
+# t1 = t1.decl.dot.ty;
+# t2 = t2.decl.dot.ty;
+# if(tequal(t1, t2))
+# return mktype(t1.src.start, t1.src.stop, Tref, t1, nil);
+# return t1;
+# }
+
+tparent0(t1: ref Type, t2: ref Type): int
+{
+ id1, id2: ref Decl;
+
+ if(t1 == t2)
+ return 1;
+ if(t1 == nil || t2 == nil)
+ return 0;
+ if(t1.kind == Tadt && t2.kind == Tadtpick)
+ t2 = t2.decl.dot.ty;
+ if(t1.kind == Tadtpick && t2.kind == Tadt)
+ t1 = t1.decl.dot.ty;
+ if(t1.kind != t2.kind)
+ return 0;
+ case(t1.kind){
+ * =>
+ fatal("unknown type " + string t1.kind + " v " + string t2.kind + " in tparent");
+ break;
+ Terror or
+ Tstring or
+ Tnone or
+ Tint or
+ Tbig or
+ Tbyte or
+ Treal or
+ Tany =>
+ return 1;
+ Texception or
+ Tfix or
+ Tfn or
+ Tadt or
+ Tmodule or
+ Tpoly =>
+ return tcompat(t1, t2, 0);
+ Tref or
+ Tlist or
+ Tarray or
+ Tchan =>
+ return tparent0(t1.tof, t2.tof);
+ Ttuple =>
+ for((id1, id2) = (t1.ids, t2.ids); id1 != nil && id2 != nil; (id1, id2) = (id1.next, id2.next))
+ if(!tparent0(id1.ty, id2.ty))
+ return 0;
+ return id1 == nil && id2 == nil;
+ Tadtpick =>
+ return tequal(t1.decl.dot.ty, t2.decl.dot.ty);
+ }
+ return 0;
+}
+
+tparent1(t1: ref Type, t2: ref Type): ref Type
+{
+ t, nt: ref Type;
+ id, id1, id2, idt: ref Decl;
+
+ if(t1.kind == Tadt && t2.kind == Tadtpick)
+ t2 = t2.decl.dot.ty;
+ if(t1.kind == Tadtpick && t2.kind == Tadt)
+ t1 = t1.decl.dot.ty;
+ case(t1.kind){
+ * =>
+ return t1;
+ Tref or
+ Tlist or
+ Tarray or
+ Tchan =>
+ t = tparent1(t1.tof, t2.tof);
+ if(t == t1.tof)
+ return t1;
+ return mktype(t1.src.start, t1.src.stop, t1.kind, t, nil);
+ Ttuple =>
+ nt = nil;
+ id = nil;
+ for((id1, id2) = (t1.ids, t2.ids); id1 != nil && id2 != nil; (id1, id2) = (id1.next, id2.next)){
+ t = tparent1(id1.ty, id2.ty);
+ if(t != id1.ty){
+ if(nt == nil){
+ nt = mktype(t1.src.start, t1.src.stop, Ttuple, nil, dupdecls(t1.ids));
+ for((id, idt) = (nt.ids, t1.ids); idt != id1; (id, idt) = (id.next, idt.next))
+ ;
+ }
+ id.ty = t;
+ }
+ if(id != nil)
+ id = id.next;
+ }
+ if(nt == nil)
+ return t1;
+ return nt;
+ Tadtpick =>
+ if(tequal(t1, t2))
+ return t1;
+ return t1.decl.dot.ty;
+ }
+ return t1;
+}
+
+tparent(t1: ref Type, t2: ref Type): ref Type
+{
+ if(tparent0(t1, t2))
+ return tparent1(t1, t2);
+ return t1;
+}
+
+#
+# make the tuple type used to initialize an exception type
+#
+mkexbasetype(t: ref Type): ref Type
+{
+ if(t.cons == byte 0)
+ fatal("mkexbasetype on non-constant");
+ last := mkids(t.decl.src, nil, tstring, nil);
+ last.store = Dfield;
+ nt := mktype(t.src.start, t.src.stop, Texception, nil, last);
+ nt.cons = byte 0;
+ new := mkids(t.decl.src, nil, tint, nil);
+ new.store = Dfield;
+ last.next = new;
+ last = new;
+ for(id := t.ids; id != nil; id = id.next){
+ new = ref *id;
+ new.cyc = byte 0;
+ last.next = new;
+ last = new;
+ }
+ last.next = nil;
+ return usetype(nt);
+}
+
+#
+# make an instantiated exception type
+#
+mkextype(t: ref Type): ref Type
+{
+ nt: ref Type;
+
+ if(t.cons == byte 0)
+ fatal("mkextype on non-constant");
+ if(t.tof != nil)
+ return t.tof;
+ nt = copytypeids(t);
+ nt.cons = byte 0;
+ t.tof = usetype(nt);
+ return t.tof;
+}
+
+#
+# convert an instantiated exception type to it's underlying type
+#
+mkextuptype(t: ref Type): ref Type
+{
+ id: ref Decl;
+ nt: ref Type;
+
+ if(int t.cons)
+ return t;
+ if(t.tof != nil)
+ return t.tof;
+ id = t.ids;
+ if(id == nil)
+ nt = t;
+ else if(id.next == nil)
+ nt = id.ty;
+ else{
+ nt = copytypeids(t);
+ nt.cons = byte 0;
+ nt.kind = Ttuple;
+ }
+ t.tof = usetype(nt);
+ return t.tof;
+}
+
+ckfix(t: ref Type, max: real)
+{
+ s := t.val.c.rval;
+ if(max == 0.0)
+ k := (big 1<<32) - big 1;
+ else
+ k = big 2 * big (max/s) + big 1;
+ x := big 1;
+ for(p := 0; k > x; p++)
+ x *= big 2;
+ if(p == 0 || p > 32){
+ error(t.src.start, "cannot fit fixed type into an int");
+ return;
+ }
+ if(p < 32)
+ t.val.c.rval /= real (1<<(32-p));
+}
+
+scale(t: ref Type): real
+{
+ n: ref Node;
+
+ if(t.kind == Tint || t.kind == Treal)
+ return 1.0;
+ if(t.kind != Tfix)
+ fatal("scale() on non fixed point type");
+ n = t.val;
+ if(n.op != Oconst)
+ fatal("non constant scale");
+ if(n.ty != treal)
+ fatal("non real scale");
+ return n.c.rval;
+}
+
+scale2(f: ref Type, t: ref Type): real
+{
+ return scale(f)/scale(t);
+}
+
+# put x in normal form
+nf(x: real): (int, int)
+{
+ p: int;
+ m: real;
+
+ p = 0;
+ m = x;
+ while(m >= 1.0){
+ p++;
+ m /= 2.0;
+ }
+ while(m < 0.5){
+ p--;
+ m *= 2.0;
+ }
+ m *= real (1<<16)*real (1<<15);
+ if(m >= real 16r7fffffff - 0.5)
+ return (p, 16r7fffffff);
+ return (p, int m);
+}
+
+ispow2(x: real): int
+{
+ m: int;
+
+ (nil, m) = nf(x);
+ if(m != 1<<30)
+ return 0;
+ return 1;
+}
+
+round(x: real, n: int): (int, int)
+{
+ if(n != 31)
+ fatal("not 31 in round");
+ return nf(x);
+}
+
+fixmul2(sx: real, sy: real, sr: real): (int, int, int)
+{
+ k, n, a: int;
+ alpha: real;
+
+ alpha = (sx*sy)/sr;
+ n = 31;
+ (k, a) = round(1.0/alpha, n);
+ return (IMULX, 1-k, 0);
+}
+
+fixdiv2(sx: real, sy: real, sr: real): (int, int, int)
+{
+ k, n, b: int;
+ beta: real;
+
+ beta = sx/(sy*sr);
+ n = 31;
+ (k, b) = round(beta, n);
+ return (IDIVX, k-1, 0);
+}
+
+fixmul(sx: real, sy: real, sr: real): (int, int, int)
+{
+ k, m, n, a, v: int;
+ W: big;
+ alpha, eps: real;
+
+ alpha = (sx*sy)/sr;
+ if(ispow2(alpha))
+ return fixmul2(sx, sy, sr);
+ n = 31;
+ (k, a) = round(1.0/alpha, n);
+ m = n-k;
+ if(m < -n-1)
+ return (IMOVW, 0, 0); # result is zero whatever the values
+ v = 0;
+ W = big 0;
+ eps = real(1<<m)/(alpha*real(a)) - 1.0;
+ if(eps < 0.0){
+ v = a-1;
+ eps = -eps;
+ }
+ if(m < 0 && real(1<<n)*eps*real(a) >= real(a)-1.0+real(1<<m))
+ W = (big(1)<<(-m)) - big 1;
+ if(v != 0 || W != big 0)
+ m = m<<2|(v != 0)<<1|(W != big 0);
+ if(v == 0 && W == big 0)
+ return (IMULX0, m, a);
+ else
+ return (IMULX1, m, a);
+}
+
+fixdiv(sx: real, sy: real, sr: real): (int, int, int)
+{
+ k, m, n, b, v: int;
+ W: big;
+ beta, eps: real;
+
+ beta = sx/(sy*sr);
+ if(ispow2(beta))
+ return fixdiv2(sx, sy, sr);
+ n = 31;
+ (k, b) = round(beta, n);
+ m = k-n;
+ if(m <= -2*n)
+ return (IMOVW, 0, 0); #result is zero whatever the values
+ v = 0;
+ W = big 0;
+ eps = (real(1<<m)*real(b))/beta - 1.0;
+ if(eps < 0.0)
+ v = 1;
+ if(m < 0)
+ W = (big(1)<<(-m)) - big 1;
+ if(v != 0 || W != big 0)
+ m = m<<2|(v != 0)<<1|(W != big 0);
+ if(v == 0 && W == big 0)
+ return (IDIVX0, m, b);
+ else
+ return (IDIVX1, m, b);
+}
+
+fixcast(sx: real, sr: real): (int, int, int)
+{
+ (op, p, a) := fixmul(sx, 1.0, sr);
+ return (op-IMULX+ICVTXX, p, a);
+}
+
+fixop(op: int, tx: ref Type, ty: ref Type, tr: ref Type): (int, int, int)
+{
+ sx, sy, sr: real;
+
+ sx = scale(tx);
+ sy = scale(ty);
+ sr = scale(tr);
+ if(op == IMULX)
+ return fixmul(sx, sy, sr);
+ else if(op == IDIVX)
+ return fixdiv(sx, sy, sr);
+ else
+ return fixcast(sx, sr);
+}
+
+ispoly(d: ref Decl): int
+{
+ if(d == nil)
+ return 0;
+ t := d.ty;
+ if(t.kind == Tfn){
+ if(t.polys != nil)
+ return 1;
+ if((d = d.dot) == nil)
+ return 0;
+ t = d.ty;
+ return t.kind == Tadt && t.polys != nil;
+ }
+ return 0;
+}
+
+ispolyadt(t: ref Type): int
+{
+ return (t.kind == Tadt || t.kind == Tadtpick) && t.polys != nil && (t.flags & INST) == byte 0;
+}
+
+polydecl(ids: ref Decl): ref Decl
+{
+ id: ref Decl;
+ t: ref Type;
+
+ for(id = ids; id != nil; id = id.next){
+ t = mktype(id.src.start, id.src.stop, Tpoly, nil, nil);
+ id.ty = t;
+ t.decl = id;
+ }
+ return ids;
+}
+
+# try to convert an expression tree to a type
+exptotype(n: ref Node): ref Type
+{
+ t, tt: ref Type;
+ d: ref Decl;
+ tll: ref Typelist;
+ src: Src;
+
+ if(n == nil)
+ return nil;
+ t = nil;
+ case(n.op){
+ Oname =>
+ if((d = n.decl) != nil && d.store == Dtype)
+ t = d.ty;
+ Otype or Ochan =>
+ t = n.ty;
+ Oref =>
+ t = exptotype(n.left);
+ if(t != nil)
+ t = mktype(n.src.start, n.src.stop, Tref, t, nil);
+ Odot =>
+ t = exptotype(n.left);
+ if(t != nil){
+ d = namedot(t.tags, n.right.decl.sym);
+ if(d == nil)
+ t = nil;
+ else
+ t = d.ty;
+ }
+ if(t == nil)
+ t = exptotype(n.right);
+ Omdot =>
+ t = exptotype(n.right);
+ Oindex =>
+ t = exptotype(n.left);
+ if(t != nil){
+ src = n.src;
+ tll = nil;
+ for(n = n.right; n != nil; n = n.right){
+ if(n.op == Oseq)
+ tt = exptotype(n.left);
+ else
+ tt = exptotype(n);
+ if(tt == nil)
+ return nil;
+ tll = addtype(tt, tll);
+ if(n.op != Oseq)
+ break;
+ }
+ t = mkinsttype(src, t, tll);
+ }
+ }
+ return t;
+}
+
+uname(im: ref Decl): string
+{
+ s := "";
+ for(p := im; p != nil; p = p.next){
+ s += p.sym.name;
+ if(p.next != nil)
+ s += "+";
+ }
+ return s;
+}
+
+# check all implementation modules have consistent declarations
+# and create their union if needed
+#
+modimp(dl: ref Dlist, im: ref Decl): ref Decl
+{
+ u, d, dd, ids, dot, last: ref Decl;
+ s: ref Sym;
+
+ if(dl.next == nil)
+ return dl.d;
+ dl0 := dl;
+ sg0 := 0;
+ un := uname(im);
+ installids(Dglobal, mkids(dl.d.src, enter(".m."+un, 0), tnone, nil));
+ u = dupdecl(dl.d);
+ u.sym = enter(un, 0);
+ u.sym.decl = u;
+ u.ty = mktype(u.src.start, u.src.stop, Tmodule, nil, nil);
+ u.ty.decl = u;
+ for( ; dl != nil; dl = dl.next){
+ d = dl.d;
+ ids = d.ty.tof.ids; # iface
+ if(ids != nil && ids.store == Dglobal) # .mp
+ sg := sign(ids);
+ else
+ sg = 0;
+ if(dl == dl0)
+ sg0 = sg;
+ else if(sg != sg0)
+ error(d.src.start, d.sym.name + "'s module data not consistent with that of " + dl0.d.sym.name + "\n");
+ for(ids = d.ty.ids; ids != nil; ids = ids.next){
+ s = ids.sym;
+ if(s.decl != nil && s.decl.scope >= scope){
+ if(ids == s.decl){
+ dd = dupdecl(ids);
+ if(u.ty.ids == nil)
+ u.ty.ids = dd;
+ else
+ last.next = dd;
+ last = dd;
+ continue;
+ }
+ dot = s.decl.dot;
+ if(s.decl.store != Dwundef && dot != nil && dot != d && isimpmod(dot.sym) && dequal(ids, s.decl, 1))
+ ids.refs = s.decl.refs;
+ else
+ redecl(ids);
+ ids.init = s.decl.init;
+ }
+ }
+ }
+ u.ty = usetype(u.ty);
+ return u;
+}
+
+modres(d: ref Decl)
+{
+ ids, id, n, i: ref Decl;
+ t: ref Type;
+
+ for(ids = d.ty.ids; ids != nil; ids = ids.next){
+ id = ids.sym.decl;
+ if(ids != id){
+ n = ids.next;
+ i = ids.iface;
+ t = ids.ty;
+ *ids = *id;
+ ids.next = n;
+ ids.iface = i;
+ ids.ty = t;
+ }
+ }
+}
+
+# update the fields of duplicate declarations in other implementation modules
+# and their union
+#
+modresolve()
+{
+ dl: ref Dlist;
+
+ dl = impdecls;
+ if(dl.next == nil)
+ return;
+ for( ; dl != nil; dl = dl.next)
+ modres(dl.d);
+ modres(impdecl);
+}