summaryrefslogtreecommitdiff
path: root/appl/cmd/limbo/decls.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/limbo/decls.b')
-rw-r--r--appl/cmd/limbo/decls.b1177
1 files changed, 1177 insertions, 0 deletions
diff --git a/appl/cmd/limbo/decls.b b/appl/cmd/limbo/decls.b
new file mode 100644
index 00000000..53d9e822
--- /dev/null
+++ b/appl/cmd/limbo/decls.b
@@ -0,0 +1,1177 @@
+
+storename := array[Dend] of
+{
+ Dtype => "type",
+ Dfn => "function",
+ Dglobal => "global",
+ Darg => "argument",
+ Dlocal => "local",
+ Dconst => "con",
+ Dfield => "field",
+ Dtag => "pick tag",
+ Dimport => "import",
+ Dunbound => "unbound",
+ Dundef => "undefined",
+ Dwundef => "undefined",
+};
+
+storeart := array[Dend] of
+{
+ Dtype => "a ",
+ Dfn => "a ",
+ Dglobal => "a ",
+ Darg => "an ",
+ Dlocal => "a ",
+ Dconst => "a ",
+ Dfield => "a ",
+ Dtag => "a ",
+ Dimport => "an ",
+ Dunbound => "",
+ Dundef => "",
+ Dwundef => "",
+};
+
+storespace := array[Dend] of
+{
+ Dtype => 0,
+ Dfn => 0,
+ Dglobal => 1,
+ Darg => 1,
+ Dlocal => 1,
+ Dconst => 0,
+ Dfield => 1,
+ Dtag => 0,
+ Dimport => 0,
+ Dunbound => 0,
+ Dundef => 0,
+ Dwundef => 0,
+};
+
+impdecl: ref Decl;
+impdecls: ref Dlist;
+scopes := array[MaxScope] of ref Decl;
+tails := array[MaxScope] of ref Decl;
+scopekind := array[MaxScope] of byte;
+scopenode := array[MaxScope] of ref Node;
+iota: ref Decl;
+zdecl: Decl;
+
+popscopes()
+{
+ d: ref Decl;
+
+ #
+ # clear out any decls left in syms
+ #
+ while(scope >= ScopeBuiltin){
+ for(d = scopes[scope--]; d != nil; d = d.next){
+ if(d.sym != nil){
+ d.sym.decl = d.old;
+ d.old = nil;
+ }
+ }
+ }
+
+ for(id := impdecls; id != nil; id = id.next){
+ for(d = id.d.ty.ids; d != nil; d = d.next){
+ d.sym.decl = nil;
+ d.old = nil;
+ }
+ }
+ impdecls = nil;
+
+ scope = ScopeBuiltin;
+ scopes[ScopeBuiltin] = nil;
+ tails[ScopeBuiltin] = nil;
+}
+
+declstart()
+{
+ iota = mkids(nosrc, enter("iota", 0), tint, nil);
+ iota.init = mkconst(nosrc, big 0);
+
+ scope = ScopeNils;
+ scopes[ScopeNils] = nil;
+ tails[ScopeNils] = nil;
+
+ nildecl = mkdecl(nosrc, Dglobal, tany);
+ nildecl.sym = enter("nil", 0);
+ installids(Dglobal, nildecl);
+ d := mkdecl(nosrc, Dglobal, tstring);
+ d.sym = enterstring("");
+ installids(Dglobal, d);
+
+ scope = ScopeGlobal;
+ scopes[ScopeGlobal] = nil;
+ tails[ScopeGlobal] = nil;
+}
+
+redecl(d: ref Decl)
+{
+ old := d.sym.decl;
+ if(old.store == Dwundef)
+ return;
+ error(d.src.start, "redeclaration of "+declconv(d)+", previously declared as "+storeconv(old)+" on line "+
+ lineconv(old.src.start));
+}
+
+checkrefs(d: ref Decl)
+{
+ id, m: ref Decl;
+ refs: int;
+
+ for(; d != nil; d = d.next){
+ if(d.das != byte 0)
+ d.refs--;
+ case d.store{
+ Dtype =>
+ refs = d.refs;
+ if(d.ty.kind == Tadt){
+ for(id = d.ty.ids; id != nil; id = id.next){
+ d.refs += id.refs;
+ if(id.store != Dfn)
+ continue;
+ if(id.init == nil && id.link == nil && d.importid == nil)
+ error(d.src.start, "function "+d.sym.name+"."+id.sym.name+" not defined");
+ if(superwarn && !id.refs && d.importid == nil)
+ warn(d.src.start, "function "+d.sym.name+"."+id.sym.name+" not referenced");
+ }
+ }
+ if(d.ty.kind == Tmodule){
+ for(id = d.ty.ids; id != nil; id = id.next){
+ refs += id.refs;
+ if(id.iface != nil)
+ id.iface.refs += id.refs;
+ if(id.store == Dtype){
+ for(m = id.ty.ids; m != nil; m = m.next){
+ refs += m.refs;
+ if(m.iface != nil)
+ m.iface.refs += m.refs;
+ }
+ }
+ }
+ d.refs = refs;
+ }
+ if(superwarn && !refs && d.importid == nil)
+ warn(d.src.start, declconv(d)+" not referenced");
+ Dglobal =>
+ if(superwarn && !d.refs && d.sym != nil && d.sym.name[0] != '.')
+ warn(d.src.start, declconv(d)+" not referenced");
+ Dlocal or
+ Darg =>
+ if(!d.refs && d.sym != nil && d.sym.name != nil && d.sym.name[0] != '.')
+ warn(d.src.start, declconv(d)+" not referenced");
+ Dconst =>
+ if(superwarn && !d.refs && d.sym != nil)
+ warn(d.src.start, declconv(d)+" not referenced");
+ Dfn =>
+ if(d.init == nil && d.importid == nil)
+ error(d.src.start, declconv(d)+" not defined");
+ if(superwarn && !d.refs)
+ warn(d.src.start, declconv(d)+" not referenced");
+ Dimport =>
+ if(superwarn && !d.refs)
+ warn(d.src.start, declconv(d)+" not referenced");
+ }
+ if(d.das != byte 0)
+ d.refs++;
+ }
+}
+
+vardecl(ids: ref Decl, t: ref Type): ref Node
+{
+ n := mkn(Ovardecl, mkn(Oseq, nil, nil), nil);
+ n.decl = ids;
+ n.ty = t;
+ return n;
+}
+
+vardecled(n: ref Node)
+{
+ store := Dlocal;
+ if(scope == ScopeGlobal)
+ store = Dglobal;
+ if(n.ty.kind == Texception && n.ty.cons == byte 1){
+ store = Dconst;
+ fatal("Texception in vardecled");
+ }
+ ids := n.decl;
+ installids(store, ids);
+ t := n.ty;
+ for(last := ids; ids != nil; ids = ids.next){
+ ids.ty = t;
+ last = ids;
+ }
+ n.left.decl = last;
+}
+
+condecl(ids: ref Decl, init: ref Node): ref Node
+{
+ n := mkn(Ocondecl, mkn(Oseq, nil, nil), init);
+ n.decl = ids;
+ return n;
+}
+
+condecled(n: ref Node)
+{
+ ids := n.decl;
+ installids(Dconst, ids);
+ for(last := ids; ids != nil; ids = ids.next){
+ ids.ty = tunknown;
+ last = ids;
+ }
+ n.left.decl = last;
+}
+
+exdecl(ids: ref Decl, tids: ref Decl): ref Node
+{
+ n: ref Node;
+ t: ref Type;
+
+ t = mktype(ids.src.start, ids.src.stop, Texception, nil, tids);
+ t.cons = byte 1;
+ n = mkn(Oexdecl, mkn(Oseq, nil, nil), nil);
+ n.decl = ids;
+ n.ty = t;
+ return n;
+}
+
+exdecled(n: ref Node)
+{
+ ids, last: ref Decl;
+ t: ref Type;
+
+ ids = n.decl;
+ installids(Dconst, ids);
+ t = n.ty;
+ for(last = ids; ids != nil; ids = ids.next){
+ ids.ty = t;
+ last = ids;
+ }
+ n.left.decl = last;
+}
+
+importdecl(m: ref Node, ids: ref Decl): ref Node
+{
+ n := mkn(Oimport, mkn(Oseq, nil, nil), m);
+ n.decl = ids;
+ return n;
+}
+
+importdecled(n: ref Node)
+{
+ ids := n.decl;
+ installids(Dimport, ids);
+ for(last := ids; ids != nil; ids = ids.next){
+ ids.ty = tunknown;
+ last = ids;
+ }
+ n.left.decl = last;
+}
+
+mkscope(body: ref Node): ref Node
+{
+ n := mkn(Oscope, nil, body);
+ if(body != nil)
+ n.src = body.src;
+ return n;
+}
+
+fndecl(n: ref Node, t: ref Type, body: ref Node): ref Node
+{
+ n = mkbin(Ofunc, n, body);
+ n.ty = t;
+ return n;
+}
+
+fndecled(n: ref Node)
+{
+ left := n.left;
+ if(left.op == Oname){
+ d := left.decl.sym.decl;
+ if(d == nil || d.store == Dimport){
+ d = mkids(left.src, left.decl.sym, n.ty, nil);
+ installids(Dfn, d);
+ }
+ left.decl = d;
+ d.refs++;
+ }
+ if(left.op == Odot)
+ pushscope(nil, Sother);
+ if(n.ty.polys != nil){
+ pushscope(nil, Sother);
+ installids(Dtype, n.ty.polys);
+ }
+ pushscope(nil, Sother);
+ installids(Darg, n.ty.ids);
+ n.ty.ids = popscope();
+ if(n.ty.val != nil)
+ mergepolydecs(n.ty);
+ if(n.ty.polys != nil)
+ n.ty.polys = popscope();
+ if(left.op == Odot)
+ popscope();
+}
+
+#
+# check the function declaration only
+# the body will be type checked later by fncheck
+#
+fnchk(n: ref Node): ref Decl
+{
+ bad := 0;
+ d := n.left.decl;
+ if(n.left.op == Odot)
+ d = n.left.right.decl;
+ if(d == nil)
+ fatal("decl() fnchk nil");
+ n.left.decl = d;
+ if(d.store == Dglobal || d.store == Dfield)
+ d.store = Dfn;
+ if(d.store != Dfn || d.init != nil){
+ nerror(n, "redeclaration of function "+dotconv(d)+", previously declared as "
+ +storeconv(d)+" on line "+lineconv(d.src.start));
+ if(d.store == Dfn && d.init != nil)
+ bad = 1;
+ }
+ d.init = n;
+
+ t := n.ty;
+ inadt := d.dot;
+ if(inadt != nil && (inadt.store != Dtype || inadt.ty.kind != Tadt))
+ inadt = nil;
+ if(n.left.op == Odot){
+ pushscope(nil, Sother);
+ adtp := outerpolys(n.left);
+ if(adtp != nil)
+ installids(Dtype, adtp);
+ if(!polyequal(adtp, n.decl))
+ nerror(n, "adt polymorphic type mismatch");
+ n.decl = nil;
+ }
+ t = validtype(t, inadt);
+ if(n.left.op == Odot)
+ popscope();
+ if(debug['d'])
+ print("declare function %s ty %s newty %s\n", dotconv(d), typeconv(d.ty), typeconv(t));
+ t = usetype(t);
+
+ if(!polyequal(d.ty.polys, t.polys))
+ nerror(n, "function polymorphic type mismatch");
+ if(!tcompat(d.ty, t, 0))
+ nerror(n, "type mismatch: "+dotconv(d)+" defined as "
+ +typeconv(t)+" declared as "+typeconv(d.ty)+" on line "+lineconv(d.src.start));
+ else if(!raisescompat(d.ty.eraises, t.eraises))
+ nerror(n, "raises mismatch: " + dotconv(d));
+ if(t.varargs != byte 0)
+ nerror(n, "cannot define functions with a '*' argument, such as "+dotconv(d));
+
+ t.eraises = d.ty.eraises;
+
+ d.ty = t;
+ d.offset = idoffsets(t.ids, MaxTemp, IBY2WD);
+ d.src = n.src;
+
+ d.locals = nil;
+
+ n.ty = t;
+
+ if(bad)
+ return nil;
+ return d;
+}
+
+globalas(dst: ref Node, v: ref Node, valok: int): ref Node
+{
+ if(v == nil)
+ return nil;
+ if(v.op == Oas || v.op == Odas){
+ v = globalas(v.left, v.right, valok);
+ if(v == nil)
+ return nil;
+ }else if(valok && !initable(dst, v, 0))
+ return nil;
+ case dst.op{
+ Oname =>
+ if(dst.decl.init != nil)
+ nerror(dst, "duplicate assignment to "+expconv(dst)+", previously assigned on line "
+ +lineconv(dst.decl.init.src.start));
+ if(valok)
+ dst.decl.init = v;
+ return v;
+ Otuple =>
+ if(valok && v.op != Otuple)
+ fatal("can't deal with "+nodeconv(v)+" in tuple case of globalas");
+ tv := v.left;
+ for(dst = dst.left; dst != nil; dst = dst.right){
+ globalas(dst.left, tv.left, valok);
+ if(valok)
+ tv = tv.right;
+ }
+ return v;
+ }
+ fatal("can't deal with "+nodeconv(dst)+" in globalas");
+ return nil;
+}
+
+needsstore(d: ref Decl): int
+{
+ if(!d.refs)
+ return 0;
+ if(d.importid != nil)
+ return 0;
+ if(storespace[d.store])
+ return 1;
+ return 0;
+}
+
+#
+# return the list of all referenced storage variables
+#
+vars(d: ref Decl): ref Decl
+{
+ while(d != nil && !needsstore(d))
+ d = d.next;
+ for(v := d; v != nil; v = v.next){
+ while(v.next != nil){
+ n := v.next;
+ if(needsstore(n))
+ break;
+ v.next = n.next;
+ }
+ }
+ return d;
+}
+
+#
+# declare variables from the left side of a := statement
+#
+recdasdecl(n: ref Node, store: int, nid: int): (int, int)
+{
+ r: int;
+
+ case n.op{
+ Otuple =>
+ ok := 1;
+ for(n = n.left; n != nil; n = n.right){
+ (r, nid) = recdasdecl(n.left, store, nid);
+ ok &= r;
+ }
+ return (ok, nid);
+ Oname =>
+ if(n.decl == nildecl)
+ return (1, -1);
+ d := mkids(n.src, n.decl.sym, nil, nil);
+ installids(store, d);
+ n.decl = d;
+ old := d.old;
+ if(old != nil
+ && old.store != Dfn
+ && old.store != Dwundef
+ && old.store != Dundef)
+ warn(d.src.start, "redeclaration of "+declconv(d)+", previously declared as "
+ +storeconv(old)+" on line "+lineconv(old.src.start));
+ d.refs++;
+ d.das = byte 1;
+ if(nid >= 0)
+ nid++;
+ return (1, nid);
+ }
+ return (0, nid);
+}
+
+recmark(n: ref Node, nid: int): int
+{
+ case(n.op){
+ Otuple =>
+ for(n = n.left; n != nil; n = n.right)
+ nid = recmark(n.left, nid);
+ Oname =>
+ n.decl.nid = byte nid;
+ nid = 0;
+ }
+ return nid;
+}
+
+dasdecl(n: ref Node): int
+{
+ ok: int;
+
+ nid := 0;
+ store := Dlocal;
+ if(scope == ScopeGlobal)
+ store = Dglobal;
+
+ (ok, nid) = recdasdecl(n, store, nid);
+ if(!ok)
+ nerror(n, "illegal declaration expression "+expconv(n));
+ if(ok && store == Dlocal && nid > 1)
+ recmark(n, nid);
+ return ok;
+}
+
+#
+# declare global variables in nested := expressions
+#
+gdasdecl(n: ref Node)
+{
+ if(n == nil)
+ return;
+
+ if(n.op == Odas){
+ gdasdecl(n.right);
+ dasdecl(n.left);
+ }else{
+ gdasdecl(n.left);
+ gdasdecl(n.right);
+ }
+}
+
+undefed(src: Src, s: ref Sym): ref Decl
+{
+ d := mkids(src, s, tnone, nil);
+ error(src.start, s.name+" is not declared");
+ installids(Dwundef, d);
+ return d;
+}
+
+# inloop() : int
+# {
+# for (i := scope; i > 0; i--)
+# if (int scopekind[i] == Sloop)
+# return 1;
+# return 0;
+# }
+
+nested() : int
+{
+ for (i := scope; i > 0; i--)
+ if (int scopekind[i] == Sscope || int scopekind[i] == Sloop)
+ return 1;
+ return 0;
+}
+
+decltozero(n : ref Node)
+{
+ if ((scop := scopenode[scope]) != nil) {
+ if (n.right != nil && errors == 0)
+ fatal("Ovardecl/Oname/Otuple has right field\n");
+ n.right = scop.left;
+ scop.left = n;
+ }
+}
+
+pushscope(scp : ref Node, kind : int)
+{
+ if(scope >= MaxScope)
+ fatal("scope too deep");
+ scope++;
+ scopes[scope] = nil;
+ tails[scope] = nil;
+ scopenode[scope] = scp;
+ scopekind[scope] = byte kind;
+}
+
+curscope(): ref Decl
+{
+ return scopes[scope];
+}
+
+#
+# revert to old declarations for each symbol in the currect scope.
+# remove the effects of any imported adt types
+# whenever the adt is imported from a module,
+# we record in the type's decl the module to use
+# when calling members. the process is reversed here.
+#
+popscope(): ref Decl
+{
+ for(id := scopes[scope]; id != nil; id = id.next){
+ if(id.sym != nil){
+ id.sym.decl = id.old;
+ id.old = nil;
+ }
+ if(id.importid != nil)
+ id.importid.refs += id.refs;
+ t := id.ty;
+ if(id.store == Dtype
+ && t.decl != nil
+ && t.decl.timport == id)
+ t.decl.timport = id.timport;
+ if(id.store == Dlocal)
+ freeloc(id);
+ }
+ return scopes[scope--];
+}
+
+#
+# make a new scope,
+# preinstalled with some previously installed identifiers
+# don't add the identifiers to the scope chain,
+# so they remain separate from any newly installed ids
+#
+# these routines assume no ids are imports
+#
+repushids(ids: ref Decl)
+{
+ if(scope >= MaxScope)
+ fatal("scope too deep");
+ scope++;
+ scopes[scope] = nil;
+ tails[scope] = nil;
+ scopenode[scope] = nil;
+ scopekind[scope] = byte Sother;
+
+ for(; ids != nil; ids = ids.next){
+ if(ids.scope != scope
+ && (ids.dot == nil || !isimpmod(ids.dot.sym)
+ || ids.scope != ScopeGlobal || scope != ScopeGlobal + 1))
+ fatal("repushids scope mismatch");
+ s := ids.sym;
+ if(s != nil && ids.store != Dtag){
+ if(s.decl != nil && s.decl.scope >= scope)
+ ids.old = s.decl.old;
+ else
+ ids.old = s.decl;
+ s.decl = ids;
+ }
+ }
+}
+
+#
+# pop a scope which was started with repushids
+# return any newly installed ids
+#
+popids(ids: ref Decl): ref Decl
+{
+ for(; ids != nil; ids = ids.next){
+ if(ids.sym != nil && ids.store != Dtag){
+ ids.sym.decl = ids.old;
+ ids.old = nil;
+ }
+ }
+ return popscope();
+}
+
+installids(store: int, ids: ref Decl)
+{
+ last : ref Decl = nil;
+ for(d := ids; d != nil; d = d.next){
+ d.scope = scope;
+ if(d.store == Dundef)
+ d.store = store;
+ s := d.sym;
+ if(s != nil){
+ if(s.decl != nil && s.decl.scope >= scope){
+ redecl(d);
+ d.old = s.decl.old;
+ }else
+ d.old = s.decl;
+ s.decl = d;
+ }
+ last = d;
+ }
+ if(ids != nil){
+ d = tails[scope];
+ if(d == nil)
+ scopes[scope] = ids;
+ else
+ d.next = ids;
+ tails[scope] = last;
+ }
+}
+
+lookup(sym: ref Sym): ref Decl
+{
+ s: int;
+ d: ref Decl;
+
+ for(s = scope; s >= ScopeBuiltin; s--){
+ for(d = scopes[s]; d != nil; d = d.next){
+ if(d.sym == sym)
+ return d;
+ }
+ }
+ return nil;
+}
+
+mkids(src: Src, s: ref Sym, t: ref Type, next: ref Decl): ref Decl
+{
+ d := ref zdecl;
+ d.src = src;
+ d.store = Dundef;
+ d.ty = t;
+ d.next = next;
+ d.sym = s;
+ d.nid = byte 1;
+ return d;
+}
+
+mkdecl(src: Src, store: int, t: ref Type): ref Decl
+{
+ d := ref zdecl;
+ d.src = src;
+ d.store = store;
+ d.ty = t;
+ d.nid = byte 1;
+ return d;
+}
+
+dupdecl(old: ref Decl): ref Decl
+{
+ d := ref *old;
+ d.next = nil;
+ return d;
+}
+
+dupdecls(old: ref Decl): ref Decl
+{
+ d, nd, first, last: ref Decl;
+
+ first = last = nil;
+ for(d = old; d != nil; d = d.next){
+ nd = dupdecl(d);
+ if(first == nil)
+ first = nd;
+ else
+ last.next = nd;
+ last = nd;
+ }
+ return first;
+}
+
+appdecls(d: ref Decl, dd: ref Decl): ref Decl
+{
+ if(d == nil)
+ return dd;
+ for(t := d; t.next != nil; t = t.next)
+ ;
+ t.next = dd;
+ return d;
+}
+
+revids(id: ref Decl): ref Decl
+{
+ next : ref Decl;
+ d : ref Decl = nil;
+ for(; id != nil; id = next){
+ next = id.next;
+ id.next = d;
+ d = id;
+ }
+ return d;
+}
+
+idoffsets(id: ref Decl, offset: int, al: int): int
+{
+ algn := 1;
+ for(; id != nil; id = id.next){
+ if(storespace[id.store]){
+usedty(id.ty);
+ if(id.store == Dlocal && id.link != nil){
+ # id.nid always 1
+ id.offset = id.link.offset;
+ continue;
+ }
+ a := id.ty.align;
+ if(id.nid > byte 1){
+ for(d := id.next; d != nil && d.nid == byte 0; d = d.next)
+ if(d.ty.align > a)
+ a = d.ty.align;
+ algn = a;
+ }
+ offset = align(offset, a);
+ id.offset = offset;
+ offset += id.ty.size;
+ if(id.nid == byte 0 && (id.next == nil || id.next.nid != byte 0))
+ offset = align(offset, algn);
+ }
+ }
+ return align(offset, al);
+}
+
+idindices(id: ref Decl): int
+{
+ i := 0;
+ for(; id != nil; id = id.next){
+ if(storespace[id.store]){
+ usedty(id.ty);
+ id.offset = i++;
+ }
+ }
+ return i;
+}
+
+declconv(d: ref Decl): string
+{
+ if(d.sym == nil)
+ return storename[d.store] + " " + "<???>";
+ return storename[d.store] + " " + d.sym.name;
+}
+
+storeconv(d: ref Decl): string
+{
+ return storeart[d.store] + storename[d.store];
+}
+
+dotconv(d: ref Decl): string
+{
+ s: string;
+
+ if(d.dot != nil && !isimpmod(d.dot.sym)){
+ s = dotconv(d.dot);
+ if(d.dot.ty != nil && d.dot.ty.kind == Tmodule)
+ s += ".";
+ else
+ s += ".";
+ }
+ s += d.sym.name;
+ return s;
+}
+
+#
+# merge together two sorted lists, yielding a sorted list
+#
+namemerge(e, f: ref Decl): ref Decl
+{
+ d := rock := ref Decl;
+ while(e != nil && f != nil){
+ if(e.sym.name <= f.sym.name){
+ d.next = e;
+ e = e.next;
+ }else{
+ d.next = f;
+ f = f.next;
+ }
+ d = d.next;
+ }
+ if(e != nil)
+ d.next = e;
+ else
+ d.next = f;
+ return rock.next;
+}
+
+#
+# recursively split lists and remerge them after they are sorted
+#
+recnamesort(d: ref Decl, n: int): ref Decl
+{
+ if(n <= 1)
+ return d;
+ m := n / 2 - 1;
+ dd := d;
+ for(i := 0; i < m; i++)
+ dd = dd.next;
+ r := dd.next;
+ dd.next = nil;
+ return namemerge(recnamesort(d, n / 2),
+ recnamesort(r, (n + 1) / 2));
+}
+
+#
+# sort the ids by name
+#
+namesort(d: ref Decl): ref Decl
+{
+ n := 0;
+ for(dd := d; dd != nil; dd = dd.next)
+ n++;
+ return recnamesort(d, n);
+}
+
+printdecls(d: ref Decl)
+{
+ for(; d != nil; d = d.next)
+ print("%d: %s %s ref %d\n", d.offset, declconv(d), typeconv(d.ty), d.refs);
+}
+
+mergepolydecs(t: ref Type)
+{
+ n, nn: ref Node;
+ id, ids, ids1: ref Decl;
+
+ for(n = t.val; n != nil; n = n.right){
+ nn = n.left;
+ for(ids = nn.decl; ids != nil; ids = ids.next){
+ id = ids.sym.decl;
+ if(id == nil){
+ undefed(ids.src, ids.sym);
+ break;
+ }
+ if(id.store != Dtype){
+ error(ids.src.start, declconv(id) + " is not a type");
+ break;
+ }
+ if(id.ty.kind != Tpoly){
+ error(ids.src.start, declconv(id) + " is not a polymorphic type");
+ break;
+ }
+ if(id.ty.ids != nil)
+ error(ids.src.start, declconv(id) + " redefined");
+ pushscope(nil, Sother);
+ fielddecled(nn.left);
+ id.ty.ids = popscope();
+ for(ids1 = id.ty.ids; ids1 != nil; ids1 = ids1.next){
+ ids1.dot = id;
+ bindtypes(ids1.ty);
+ if(ids1.ty.kind != Tfn){
+ error(ids1.src.start, "only function types expected");
+ id.ty.ids = nil;
+ }
+ }
+ }
+ }
+ t.val = nil;
+}
+
+adjfnptrs(d: ref Decl, polys1: ref Decl, polys2: ref Decl)
+{
+ n: int;
+ id, idt, idf, arg: ref Decl;
+
+ n = 0;
+ for(id = d.ty.ids; id != nil; id = id.next)
+ n++;
+ for(idt = polys1; idt != nil; idt = idt.next)
+ for(idf = idt.ty.ids; idf != nil; idf = idf.next)
+ n -= 2;
+ for(idt = polys2; idt != nil; idt = idt.next)
+ for(idf = idt.ty.ids; idf != nil; idf = idf.next)
+ n -= 2;
+ for(arg = d.ty.ids; --n >= 0; arg = arg.next)
+ ;
+ for(idt = polys1; idt != nil; idt = idt.next){
+ for(idf = idt.ty.ids; idf != nil; idf = idf.next){
+ idf.link = arg;
+ arg = arg.next.next;
+ }
+ }
+ for(idt = polys2; idt != nil; idt = idt.next){
+ for(idf = idt.ty.ids; idf != nil; idf = idf.next){
+ idf.link = arg;
+ arg = arg.next.next;
+ }
+ }
+}
+
+addptrs(polys: ref Decl, fps: ref Decl, last: ref Decl, link: int, src: Src): (ref Decl, ref Decl)
+{
+ for(idt := polys; idt != nil; idt = idt.next){
+ for(idf := idt.ty.ids; idf != nil; idf = idf.next){
+ fp := mkdecl(src, Darg, tany);
+ fp.sym = idf.sym;
+ if(link)
+ idf.link = fp;
+ if(fps == nil)
+ fps = fp;
+ else
+ last.next = fp;
+ last = fp;
+ fp = mkdecl(src, Darg, tint);
+ fp.sym = idf.sym;
+ last.next = fp;
+ last = fp;
+ }
+ }
+ return (fps, last);
+}
+
+addfnptrs(d: ref Decl, link: int)
+{
+ fps, last, polys: ref Decl;
+
+ polys = encpolys(d);
+ if(int(d.ty.flags&FULLARGS)){
+ if(link)
+ adjfnptrs(d, d.ty.polys, polys);
+ return;
+ }
+ d.ty.flags |= FULLARGS;
+ fps = last = nil;
+ (fps, last) = addptrs(d.ty.polys, fps, last, link, d.src);
+ (fps, last) = addptrs(polys, fps, last, link, d.src);
+ for(last = d.ty.ids; last != nil && last.next != nil; last = last.next)
+ ;
+ if(last != nil)
+ last.next = fps;
+ else
+ d.ty.ids = fps;
+ d.offset = idoffsets(d.ty.ids, MaxTemp, IBY2WD);
+}
+
+rmfnptrs(d: ref Decl)
+{
+ n: int;
+ id, idt, idf: ref Decl;
+
+ if(int(d.ty.flags&FULLARGS))
+ d.ty.flags &= ~FULLARGS;
+ else
+ return;
+ n = 0;
+ for(id = d.ty.ids; id != nil; id = id.next)
+ n++;
+ for(idt = d.ty.polys; idt != nil; idt = idt.next)
+ for(idf = idt.ty.ids; idf != nil; idf = idf.next)
+ n -= 2;
+ for(idt = encpolys(d); idt != nil; idt = idt.next)
+ for(idf = idt.ty.ids; idf != nil; idf = idf.next)
+ n -= 2;
+ if(n == 0){
+ d.ty.ids = nil;
+ return;
+ }
+ for(id = d.ty.ids; --n > 0; id = id.next)
+ ;
+ id.next = nil;
+ d.offset = idoffsets(d.ty.ids, MaxTemp, IBY2WD);
+}
+
+local(d: ref Decl): int
+{
+ for(d = d.dot; d != nil; d = d.dot)
+ if(d.store == Dtype && d.ty.kind == Tmodule)
+ return 0;
+ return 1;
+}
+
+lmodule(d: ref Decl): ref Decl
+{
+ for(d = d.dot; d != nil; d = d.dot)
+ if(d.store == Dtype && d.ty.kind == Tmodule)
+ return d;
+ return nil;
+}
+
+outerpolys(n: ref Node): ref Decl
+{
+ d: ref Decl;
+
+ if(n.op == Odot){
+ d = n.right.decl;
+ if(d == nil)
+ fatal("decl() outeradt nil");
+ d = d.dot;
+ if(d != nil && d.store == Dtype && d.ty.kind == Tadt)
+ return d.ty.polys;
+ }
+ return nil;
+}
+
+encpolys(d: ref Decl): ref Decl
+{
+ if((d = d.dot) == nil)
+ return nil;
+ return d.ty.polys;
+}
+
+fnlookup(s: ref Sym, t: ref Type): (ref Decl, ref Node)
+{
+ id: ref Decl;
+ mod: ref Node;
+
+ id = nil;
+ mod = nil;
+ if(t.kind == Tpoly || t.kind == Tmodule)
+ id = namedot(t.ids, s);
+ else if(t.kind == Tref){
+ t = t.tof;
+ if(t.kind == Tadt){
+ id = namedot(t.ids, s);
+ if(t.decl != nil && t.decl.timport != nil)
+ mod = t.decl.timport.eimport;
+ }
+ else if(t.kind == Tadtpick){
+ id = namedot(t.ids, s);
+ if(t.decl != nil && t.decl.timport != nil)
+ mod = t.decl.timport.eimport;
+ t = t.decl.dot.ty;
+ if(id == nil)
+ id = namedot(t.ids, s);
+ if(t.decl != nil && t.decl.timport != nil)
+ mod = t.decl.timport.eimport;
+ }
+ }
+ if(id == nil){
+ id = lookup(s);
+ if(id != nil)
+ mod = id.eimport;
+ }
+ return (id, mod);
+}
+
+isimpmod(s: ref Sym): int
+{
+ d: ref Decl;
+
+ for(d = impmods; d != nil; d = d.next)
+ if(d.sym == s)
+ return 1;
+ return 0;
+}
+
+dequal(d1: ref Decl, d2: ref Decl, full: int): int
+{
+ return d1.sym == d2.sym &&
+ d1.store == d2.store &&
+ d1.implicit == d2.implicit &&
+ d1.cyc == d2.cyc &&
+ (!full || tequal(d1.ty, d2.ty)) &&
+ (!full || d1.store == Dfn || sametree(d1.init, d2.init));
+}
+
+tzero(t: ref Type): int
+{
+ return t.kind == Texception || tmustzero(t);
+}
+
+isptr(t: ref Type): int
+{
+ return t.kind == Texception || tattr[t.kind].isptr;
+}
+
+# can d share the same stack location as another local ?
+shareloc(d: ref Decl)
+{
+ z: int;
+ t, tt: ref Type;
+ dd, res: ref Decl;
+
+ if(d.store != Dlocal || d.nid != byte 1)
+ return;
+ t = d.ty;
+ res = nil;
+ for(dd = fndecls; dd != nil; dd = dd.next){
+ if(d == dd)
+ fatal("d==dd in shareloc");
+ if(dd.store != Dlocal || dd.nid != byte 1 || dd.link != nil || dd.tref != 0)
+ continue;
+ tt = dd.ty;
+ if(t.size != tt.size || t.align != tt.align)
+ continue;
+ z = tzero(t)+tzero(tt);
+ if(z > 0)
+ continue; # for now
+ if(t == tt || tequal(t, tt))
+ res = dd;
+ else{
+ if(z == 1)
+ continue;
+ if(z == 0 || isptr(t) || isptr(tt) || mktdesc(t) == mktdesc(tt))
+ res = dd;
+ }
+ if(res != nil){
+ d.link = res;
+ res.tref = 1;
+ return;
+ }
+ }
+ return;
+}
+
+freeloc(d: ref Decl)
+{
+ if(d.link != nil)
+ d.link.tref = 0;
+}