ref: 876f68cfab0905e1ab21be625fabd3c49a819075
dir: /appl/cmd/limbo/types.b/
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 customized check for overlapping definitions. # 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) && oldcycles && 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[Crypt->MD5dlen] of {* => byte 0}; md5(sig, sigend, md5sig, nil); for(i := 0; i < Crypt->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 its 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); }