shithub: purgatorio

ref: 21947578303cdb0fcb4b0cdfe80aa9dab5f6b447
dir: /limbo/types.c/

View raw version
#include "limbo.h"
#include "mp.h"
#include "libsec.h"

char *kindname[Tend] =
{
	/* 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 tattr[Tend] =
{
	/*		 isptr	refable	conable	big	vis */
	/* Tnone */	{ 0,	0,	0,	0,	0, },
	/* Tadt */	{ 0,	1,	1,	1,	1, },
	/* Tadtpick */	{ 0,	1,	0,	1,	1, },
	/* Tarray */	{ 1,	0,	0,	0,	1, },
	/* Tbig */	{ 0,	0,	1,	1,	1, },
	/* Tbyte */	{ 0,	0,	1,	0,	1, },
	/* Tchan */	{ 1,	0,	0,	0,	1, },
	/* Treal */	{ 0,	0,	1,	1,	1, },
	/* Tfn */	{ 0,	1,	0,	0,	1, },
	/* Tint */	{ 0,	0,	1,	0,	1, },
	/* Tlist */	{ 1,	0,	0,	0,	1, },
	/* Tmodule */	{ 1,	0,	0,	0,	1, },
	/* Tref */	{ 1,	0,	0,	0,	1, },
	/* Tstring */	{ 1,	0,	1,	0,	1, },
	/* Ttuple */	{ 0,	1,	1,	1,	1, },
	/* Texception */	{ 0,	0,	0,	1,	1, },
	/* Tfix */	{ 0,	0,	1,	0,	1, },
	/* Tpoly */	{ 1,	0,	0,	0,	1, },

	/* Tainit */	{ 0,	0,	0,	1,	0, },
	/* Talt */	{ 0,	0,	0,	1,	0, },
	/* Tany */	{ 1,	0,	0,	0,	0, },
	/* Tarrow */	{ 0,	0,	0,	0,	1, },
	/* Tcase */	{ 0,	0,	0,	1,	0, },
	/* Tcasel */	{ 0,	0,	0,	1,	0, },
	/* Tcasec */	{ 0,	0,	0,	1,	0, },
	/* Tdot */	{ 0,	0,	0,	0,	1, },
	/* Terror */	{ 0,	1,	1,	0,	0, },
	/* Tgoto */	{ 0,	0,	0,	1,	0, },
	/* Tid */	{ 0,	0,	0,	0,	1, },
	/* Tiface */	{ 0,	0,	0,	1,	0, },
	/* Texcept */	{ 0,	0,	0,	1,	0, },
	/* Tinst */	{ 0,	1,	1,	1,	1, },
};

static	Teq	*eqclass[Tend];

static	Type	ztype;
static	int	eqrec;
static	int	eqset;
static	int	tcomset;

static	int	idcompat(Decl*, Decl*, int, int);
static	int	rtcompat(Type *t1, Type *t2, int any, int);
static	int	assumeteq(Type *t1, Type *t2);
static	int	assumetcom(Type *t1, Type *t2);
static	int	cleartcomrec(Type *t);
static	int	rtequal(Type*, Type*);
static	int	cleareqrec(Type*);
static	int	idequal(Decl*, Decl*, int, int*);
static	int	pyequal(Type*, Type*);
static	int	rtsign(Type*, uchar*, int, int);
static	int	clearrec(Type*);
static	int	idsign(Decl*, int, uchar*, int, int);
static	int	idsign1(Decl*, int, uchar*, int, int);
static	int	raisessign(Node *n, uchar *sig, int lensig, int spos);
static	void	ckfix(Type*, double);
static	int	fnunify(Type*, Type*, Tpair**, int);
static	int	rtunify(Type*, Type*, Tpair**, int);
static	int	idunify(Decl*, Decl*, Tpair**, int);
static	int	toccurs(Type*, Tpair**);
static	int	fncleareqrec(Type*, Type*);
static	Type*	comtype(Src*, Type*, Decl*);
static	Type*	duptype(Type*);
static	int	tpolys(Type*);

static void
addtmap(Type *t1, Type *t2, Tpair **tpp)
{
	Tpair *tp;

	tp = allocmem(sizeof *tp);
	tp->t1 = t1;
	tp->t2 = t2;
	tp->nxt = *tpp;
	*tpp = tp;
}

Type*
valtmap(Type *t, Tpair *tp)
{
	for( ; tp != nil; tp = tp->nxt)
		if(tp->t1 == t)
			return tp->t2;
	return t;
}

Typelist*
addtype(Type *t, Typelist *hd)
{
	Typelist *tl, *p;

	tl = allocmem(sizeof(*tl));
	tl->t = t;
	tl->nxt = nil;
	if(hd == nil)
		return tl;
	for(p = hd; p->nxt != nil; p = p->nxt)
		;
	p->nxt = tl;
	return hd;
}

void
typeinit(void)
{
	Decl *id;

	anontupsym = enter(".tuple", 0);

	ztype.sbl = -1;
	ztype.ok = 0;
	ztype.rec = 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 = nosrc;
	id = tfnptr->ids->next = mkids(&nosrc, nil, tint, nil);
	id->store = Dfield;
	id->offset = IBY2WD;
	id->sym = enter("t1", 0);
	id->src = nosrc;

	rtexception = mktype(&noline, &noline, Tref, texception, nil);
	rtexception->size = IBY2WD;
	rtexception->align = IBY2WD;
	rtexception->ok = OKmask;
}

void
typestart(void)
{
	descriptors = nil;
	nfns = 0;
	nadts = 0;
	selfdecl = nil;
	if(tfnptr->decl != nil)
		tfnptr->decl->desc = nil;

	memset(eqclass, 0, sizeof eqclass);

	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);
}

Teq*
modclass(void)
{
	return eqclass[Tmodule];
}

Type*
mktype(Line *start, Line *stop, int kind, Type *tof, Decl *args)
{
	Type *t;

	t = allocmem(sizeof *t);
	*t = ztype;
	t->src.start = *start;
	t->src.stop = *stop;
	t->kind = kind;
	t->tof = tof;
	t->ids = args;
	return t;
}

Type*
mktalt(Case *c)
{
	Type *t;
	char buf[32];
	static int nalt;

	t = mktype(&noline, &noline, Talt, nil, nil);
	t->decl = mkdecl(&nosrc, Dtype, t);
	seprint(buf, buf+sizeof(buf), ".a%d", nalt++);
	t->decl->sym = enter(buf, 0);
	t->cse = c;
	return usetype(t);
}

/*
 * copy t and the top level of ids
 */
Type*
copytypeids(Type *t)
{
	Type *nt;
	Decl *id, *new, *last;

	nt = allocmem(sizeof *nt);
	*nt = *t;
	last = nil;
	for(id = t->ids; id != nil; id = id->next){
		new = allocmem(sizeof *id);
		*new = *id;
		if(last == nil)
			nt->ids = new;
		else
			last->next = new;
		last = new;
	}
	return nt;
}

/*
 * make each of the ids have type t
 */
Decl*
typeids(Decl *ids, Type *t)
{
	Decl *id;

	if(ids == nil)
		return nil;

	ids->ty = t;
	for(id = ids->next; id != nil; id = id->next){
		id->ty = t;
	}
	return ids;
}

void
typebuiltin(Decl *d, Type *t)
{
	d->ty = t;
	t->decl = d;
	installids(Dtype, d);
}

Node *
fielddecl(int store, Decl *ids)
{
	Node *n;

	n = mkn(Ofielddecl, nil, nil);
	n->decl = ids;
	for(; ids != nil; ids = ids->next)
		ids->store = store;
	return n;
}

Node *
typedecl(Decl *ids, Type *t)
{
	Node *n;

	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;
}

void
typedecled(Node *n)
{
	installids(Dtype, n->decl);
}

Node *
adtdecl(Decl *ids, Node *fields)
{
	Node *n;
	Type *t;

	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;
}

void
adtdecled(Node *n)
{
	Decl *d, *ids;

	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;
}

void
fielddecled(Node *n)
{
	for(; n != nil; n = n->right){
		switch(n->op){
		case Oseq:
			fielddecled(n->left);
			break;
		case Oadtdecl:
			adtdecled(n);
			return;
		case Otypedecl:
			typedecled(n);
			return;
		case Ofielddecl:
			installids(Dfield, n->decl);
			return;
		case Ocondecl:
			condecled(n);
			gdasdecl(n->right);
			return;
		case Oexdecl:
			exdecled(n);
			return;
		case Opickdecl:
			pickdecled(n);
			return;
		default:
			fatal("can't deal with %O in fielddecled", n->op);
		}
	}
}

int
pickdecled(Node *n)
{
	Decl *d;
	int tag;

	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
 */
Type*
mkadtcon(Type *t)
{
	Decl *id, *new, *last;
	Type *nt;

	nt = allocmem(sizeof *nt);
	*nt = *t;
	last = nil;
	nt->ids = nil;
	nt->kind = Ttuple;
	for(id = t->ids; id != nil; id = id->next){
		if(id->store != Dfield)
			continue;
		new = allocmem(sizeof *id);
		*new = *id;
		new->cyc = 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
 */
Type*
mkadtpickcon(Type *t, Type *tgt)
{
	Decl *id, *new, *last;
	Type *nt;

	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 = allocmem(sizeof *id);
		*new = *id;
		new->cyc = 0;
		last->next = new;
		last = new;
	}
	for(id = tgt->ids; id != nil; id = id->next){
		if(id->store != Dfield)
			continue;
		new = allocmem(sizeof *id);
		*new = *id;
		new->cyc = 0;
		last->next = new;
		last = new;
	}
	last->next = nil;
	return nt;
}

/*
 * make an identifier type
 */
Type*
mkidtype(Src *src, Sym *s)
{
	Type *t;

	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
 */
Type*
mkarrowtype(Line *start, Line *stop, Type *t, Sym *s)
{
	Src src;

	src.start = *start;
	src.stop = *stop;
	t = mktype(start, stop, Tarrow, t, 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
 */
Type*
mkdottype(Line *start, Line *stop, Type *t, Sym *s)
{
	Src src;

	src.start = *start;
	src.stop = *stop;
	t = mktype(start, stop, Tdot, t, nil);
	if(s->unbound == nil){
		s->unbound = mkdecl(&src, Dunbound, nil);
		s->unbound->sym = s;
	}
	t->decl = s->unbound;
	return t;
}

Type*
mkinsttype(Src* src, Type *tt, Typelist *tl)
{
	Type *t;

	t = mktype(&src->start, &src->stop, Tinst, tt, nil);
	t->u.tlist = tl;
	return t;
}

/*
 * look up the name f in the fields of a module, adt, or tuple
 */
Decl*
namedot(Decl *ids, Sym *s)
{
	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
 */
void
adtdefd(Type *t)
{
	Decl *d, *id, *next, *aux, *store, *auxhd, *tagnext;
	int seentags;

	if(debug['x'])
		print("adt %T defd\n", 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.
 */
Decl*
pickdefd(Type *t, Decl *tg)
{
	Decl *id, *xid, *lasttg, *d;
	Type *tt;
	int tag;

	lasttg = 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 %K, previously declared as %k on line %L",
					id, xid, xid->src.start);
			id->dot = d;
		}
	}
	if(lasttg == nil){
		error(t->src.start, "empty pick field declaration in %T", t);
		t->tags = nil;
	}else
		lasttg->next = nil;
	d->tag = tag;
	return tg;
}

Node*
moddecl(Decl *ids, Node *fields)
{
	Node *n;
	Type *t;

	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;
}

void
moddecled(Node *n)
{
	Decl *d, *ids, *im, *dot;
	Type *t;
	Sym *s;
	char buf[StrSize];
	int isimp;
	Dlist *dm, *dl;

	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 = malloc(sizeof(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
	 */
	seprint(buf, buf+sizeof(buf), ".m.%s", d->sym->name);
	installids(Dglobal, mkids(&d->src, enter(buf, 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
 */
Type*
mkiface(Decl *m)
{
	Decl *iface, *last, *globals, *glast, *id, *d;
	Type *t;
	char buf[StrSize];

	iface = last = allocmem(sizeof(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){
		switch(id->store){
		case Dglobal:
			glast = glast->next = dupdecl(id);
			id->iface = globals;
			glast->iface = id;
			break;
		case Dfn:
			id->iface = last = last->next = dupdecl(id);
			last->iface = id;
			break;
		case 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;
				}
			}
			break;
		}
	}
	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);
	seprint(buf, buf+sizeof(buf), ".m.%s", m->sym->name);
	id = enter(buf, 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;
}

void
joiniface(Type *mt, Type *t)
{
	Decl *id, *d, *iface, *globals;

	iface = t->ids;
	globals = iface;
	if(iface != nil && iface->store == Dglobal)
		iface = iface->next;
	for(id = mt->tof->ids; id != nil; id = id->next){
		switch(id->store){
		case Dglobal:
			for(d = id->ty->ids; d != nil; d = d->next)
				d->iface->iface = globals;
			break;
		case Dfn:
			id->iface->iface = iface;
			iface = iface->next;
			break;
		default:
			fatal("unknown store %k in joiniface", id);
			break;
		}
	}
	if(iface != nil)
		fatal("join iface not matched");
	mt->tof = t;
}

void
addiface(Decl *m, Decl *d)
{
	Type *t;
	Decl *id, *last, *dd, *lastorig;
	Dlist *dl;

	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(debug['v']) print("addiface %p %p\n", d, dd);
	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
 */
void
narrowmods(void)
{
	Teq *eq;
	Decl *id, *last;
	Type *t;
	long offset;

	for(eq = modclass(); eq != nil; eq = eq->eq){
		t = eq->ty->tof;

		if(t->linkall == 0){
			last = 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->val = offset;
	}
}

/*
 * check to see if any data field of module m if referenced.
 * if so, mark all data in m
 */
void
moddataref(void)
{
	Teq *eq;
	Decl *id;

	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
 */
Decl*
modglobals(Decl *mod, Decl *globals)
{
	Decl *id, *head, *last;

	/*
	 * 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 = allocmem(sizeof(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
 */
Type*
validtype(Type *t, Decl *inadt)
{
	if(t == nil)
		return t;
	bindtypes(t);
	t = verifytypes(t, inadt, nil);
	cycsizetype(t);
	teqclass(t);
	return t;
}

Type*
usetype(Type *t)
{
	if(t == nil)
		return t;
	t = validtype(t, nil);
	reftype(t);
	return t;
}

Type*
internaltype(Type *t)
{
	bindtypes(t);
	t->ok = OKverify;
	sizetype(t);
	t->ok = OKmask;
	return t;
}

/*
 * checks that t is a valid top-level type
 */
Type*
topvartype(Type *t, Decl *id, int tyok, int polyok)
{
	if(t->kind == Tadt && t->tags != nil || t->kind == Tadtpick)
		error(id->src.start, "cannot declare %s with type %T", id->sym->name, t);
	if(!tyok && t->kind == Tfn)
		error(id->src.start, "cannot declare %s to be a function", id->sym->name);
	if(!polyok && (t->kind == Tadt || t->kind == Tadtpick) && ispolyadt(t))
		error(id->src.start, "cannot declare %s of a polymorphic type", id->sym->name);
	return t;
}

Type*
toptype(Src *src, Type *t)
{
	if(t->kind == Tadt && t->tags != nil || t->kind == Tadtpick)
		error(src->start, "%T, an adt with pick fields, must be used with ref", t);
	if(t->kind == Tfn)
		error(src->start, "data cannot have a fn type like %T", t);
	return t;
}

static Type*
comtype(Src *src, Type *t, Decl* adtd)
{
	if(adtd == nil && (t->kind == Tadt || t->kind == Tadtpick) && ispolyadt(t))
		error(src->start, "polymorphic type %T illegal here", t);
	return t;
}

void
usedty(Type *t)
{
	if(t != nil && (t->ok | OKmodref) != OKmask)
		fatal("used ty %t %2.2ux", t, t->ok);
}

void
bindtypes(Type *t)
{
	Decl *id;
	Typelist *tl;

	if(t == nil)
		return;
	if((t->ok & OKbind) == OKbind)
		return;
	t->ok |= OKbind;
	switch(t->kind){
	case 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);
		}
		break;
	case Tadtpick:
	case Tmodule:
	case Terror:
	case Tint:
	case Tbig:
	case Tstring:
	case Treal:
	case Tbyte:
	case Tnone:
	case Tany:
	case Tiface:
	case Tainit:
	case Talt:
	case Tcase:
	case Tcasel:
	case Tcasec:
	case Tgoto:
	case Texcept:
	case Tfix:
	case Tpoly:
		break;
	case Tarray:
	case Tarrow:
	case Tchan:
	case Tdot:
	case Tlist:
	case Tref:
		bindtypes(t->tof);
		break;
	case 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;
		break;
	case Ttuple:
	case Texception:
		for(id = t->ids; id != nil; id = id->next)
			bindtypes(id->ty);
		break;
	case 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);
		}
		break;
	case Tinst:
		bindtypes(t->tof);
		for(tl = t->u.tlist; tl != nil; tl = tl->nxt)
			bindtypes(tl->t);
		break;
	default:
		fatal("bindtypes: unknown type kind %d", t->kind);
	}
}

/*
 * walk the type checking for validity
 */
Type*
verifytypes(Type *t, Decl *adtt, Decl *poly)
{
	Node *n;
	Decl *id, *id1, *last;
	char buf[32];
	int i, cyc;
	Ok ok, ok1;
	double max;
	Typelist *tl;

	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 %t", t);
	cyc = t->flags&CYCLIC;
	switch(t->kind){
	case Terror:
	case Tint:
	case Tbig:
	case Tstring:
	case Treal:
	case Tbyte:
	case Tnone:
	case Tany:
	case Tiface:
	case Tainit:
	case Talt:
	case Tcase:
	case Tcasel:
	case Tcasec:
	case Tgoto:
	case Texcept:
		break;
	case Tfix:
		n = t->val;
		max = 0.0;
		if(n->op == Oseq){
			ok = echeck(n->left, 0, 0, n);
			ok1 = echeck(n->right, 0, 0, n);
			if(!ok.ok || !ok1.ok)
				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->rval) <= 0){
				error(t->src.start, "non-positive fixed point maximum");
				return terror;
			}
			n = n->left;
		}
		else{
			ok = echeck(n, 0, 0, nil);
			if(!ok.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->rval <= 0){
			error(t->src.start, "non-positive fixed point scale");
			return terror;
		}
		ckfix(t, max);
		break;
	case 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 %T", t->tof);
			return terror;
		}
		if(0 && t->tof->kind == Tfn && t->tof->ids != nil && 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");
		break;
	case Tchan:
	case Tarray:
	case Tlist:
		t->tof = comtype(&t->src, toptype(&t->src, verifytypes(t->tof, adtt, nil)), adtt);
		break;
	case Tid:
		t->ok &= ~OKverify;
		t = verifytypes(idtype(t), adtt, nil);
		break;
	case Tarrow:
		t->ok &= ~OKverify;
		t = verifytypes(arrowtype(t, adtt), adtt, nil);
		break;
	case Tdot:
		/*
		 * verify the parent adt & lookup the tag fields
		 */
		t->ok &= ~OKverify;
		t = verifytypes(dottype(t, adtt), adtt, nil);
		break;
	case Tadt:
		/*
		 * this is where Tadt may get tag fields added
		 */
		adtdefd(t);
		break;
	case 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, "pick fields cannot be a con like %s", id->sym->name);
		}
		verifytypes(t->decl->dot->ty, nil, nil);
		break;
	case 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);
		}
 		break;
	case Ttuple:
	case Texception:
		if(t->decl == nil){
			t->decl = mkdecl(&t->src, Dtype, t);
			t->decl->sym = enter(".tuple", 0);
		}
		i = 0;
		for(id = t->ids; id != nil; id = id->next){
			id->store = Dfield;
			if(id->sym == nil){
				seprint(buf, buf+sizeof(buf), "t%d", i);
				id->sym = enter(buf, 0);
			}
			i++;
			id->ty = toptype(&id->src, verifytypes(id->ty, adtt, nil));
			/* id->ty = comtype(&id->src, toptype(&id->src, verifytypes(id->ty, adtt, nil)), adtt); */
		}
		break;
	case Tfn:
		last = 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){
				Decl *selfd;

				selfd = poly ? poly : 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 %s or ref %s",
						selfd->sym->name, 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 && (last == nil || last->ty != tstring))
			error(t->src.start, "variable arguments must be preceded by a string");
		if(t->varargs && t->polys != nil)
			error(t->src.start, "polymorphic functions must not have variable arguments");
		break;
	case Tpoly:
		for(id = t->ids; id != nil; id = id->next){
			id->store = Dfn;
			id->ty = verifytypes(id->ty, adtt, t->decl);
		}
		break;
	case Tinst:
		t->ok &= ~OKverify;
		t->tof = verifytypes(t->tof, adtt, nil);
		for(tl = t->u.tlist; tl != nil; tl = tl->nxt)
			tl->t = verifytypes(tl->t, adtt, nil);
		t = verifytypes(insttype(t, adtt, nil), adtt, nil);
		break;
	default:
		fatal("verifytypes: unknown type kind %d", t->kind);
	}
	if(cyc)
		t->flags |= CYCLIC;
	return t;
}

/*
 * resolve an id type
 */
Type*
idtype(Type *t)
{
	Decl *id;
	Type *tt;

	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, "%s is not declared", id->sym->name);
		}else if(id->store == Dimport){
			id->store = Dwundef;
			error(t->src.start, "%s's type cannot be determined", id->sym->name);
		}else if(id->store != Dwundef)
			error(t->src.start, "%s is not a type", id->sym->name);
		return terror;
	}
	if(tt == nil){
		error(t->src.start, "%t not fully defined", t);
		return terror;
	}
	return tt;
}

/*
 * resolve a -> qualified type
 */
Type*
arrowtype(Type *t, Decl *adtt)
{
	Type *tt;
	Decl *id;

	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, "%s's type cannot be determined", id->sym->name);
			return terror;
		}

		/*
		 * forward references to module variables can't be resolved
		 */
		if(id->store != Dtype && !(id->ty->ok & OKbind)){
			error(t->src.start, "%s's type cannot be determined", id->sym->name);
			return terror;
		}

		if(id->store == Dwundef)
			return terror;
		tt = id->ty = verifytypes(id->ty, adtt, nil);
		if(tt == nil){
			error(t->tof->src.start, "%T is not a module", t->tof);
			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, "%T is not a module", tt);
		return terror;
	}
	id = namedot(tt->ids, t->decl->sym);
	if(id == nil){
		error(t->src.start, "%s is not a member of %T", t->decl->sym->name, tt);
		return terror;
	}
	if(id->store == Dtype && id->ty != nil){
		t->decl = id;
		return id->ty;
	}
	error(t->src.start, "%T is not a type", t);
	return terror;
}

/*
 * resolve a . qualified type
 */
Type*
dottype(Type *t, Decl *adtt)
{
	Type *tt;
	Decl *id;

	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, "%T is not an adt", tt);
		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, "%s is not a pick tag of %T", t->decl->sym->name, tt);
	return terror;
}

Type*
insttype(Type *t, Decl *adtt, Tpair **tp)
{
	Type *tt;
	Typelist *tl;
	Decl *ids;
	Tpair *tp1, *tp2;
	Src src;

	src = t->src;
	if(tp == nil){
		tp2 = nil;
		tp = &tp2;
	}
	if(t->tof->kind != Tadt && t->tof->kind != Tadtpick){
		error(src.start, "%T is not an adt", t->tof);
		return terror;
	}
	if(t->tof->kind == Tadt)
		ids = t->tof->polys;
	else
		ids = t->tof->decl->dot->ty->polys;
	if(ids == nil){
		error(src.start, "%T is not a polymorphic adt", t->tof);
		return terror;
	}
	for(tl = t->u.tlist; tl != nil && ids != nil; tl = tl->nxt, ids = ids->next){
		tt = tl->t;
		if(!tattr[tt->kind].isptr){
			error(src.start, "%T is not a pointer type", tt);
			return terror;
		}
		unifysrc = src;
		if(!tunify(ids->ty, tt, &tp1)){
			error(src.start, "type %T does not match %T", tt, ids->ty);
			return terror;
		}
		/* usetype(tt); */
		tt = verifytypes(tt, adtt, nil);
		addtmap(ids->ty, tt, tp);
	}
	if(tl != nil){
		error(src.start, "too many actual types in instantiation");
		return terror;
	}
	if(ids != nil){
		error(src.start, "too few actual types in instantiation");
		return terror;
	}
	tp1 = *tp;
	tt = t->tof;
	t = expandtype(tt, t, adtt, tp);
	if(t == tt && adtt == nil)
		t = duptype(t);
	if(t != tt){
		t->u.tmap = tp1;
		if(debug['w']){
			print("tmap for %T: ", t);
			for( ; tp1!=nil; tp1=tp1->nxt)
				print("%T -> %T ", tp1->t1, tp1->t2);
			print("\n");
		}
	}
	t->src = src;
	return t;
}

/*
 * walk a type, putting all adts, modules, and tuples into equivalence classes
 */
void
teqclass(Type *t)
{
	Decl *id, *tg;
	Teq *teq;

	if(t == nil || (t->ok & OKclass) == OKclass)
		return;
	t->ok |= OKclass;
	switch(t->kind){
	case Terror:
	case Tint:
	case Tbig:
	case Tstring:
	case Treal:
	case Tbyte:
	case Tnone:
	case Tany:
	case Tiface:
	case Tainit:
	case Talt:
	case Tcase:
	case Tcasel:
	case Tcasec:
	case Tgoto:
	case Texcept:
	case Tfix:
	case Tpoly:
		return;
	case Tref:
		teqclass(t->tof);
		return;
	case Tchan:
	case Tarray:
	case Tlist:
		teqclass(t->tof);
		if(!debug['Z'])
			return;
		break;
	case Tadt:
	case Tadtpick:
	case Ttuple:
	case 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);
		break;
	case Tmodule:
		t->tof = mkiface(t->decl);
		for(id = t->ids; id != nil; id = id->next)
			teqclass(id->ty);
		break;
	case 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;
	default:
		fatal("teqclass: unknown type kind %d", t->kind);
		return;
	}

	/*
	 * find an equivalent type
	 * stupid linear lookup could be made faster
	 */
	if((t->ok & OKsized) != OKsized)
		fatal("eqclass type not sized: %t", 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
	 */
	t->eq = allocmem(sizeof(Teq));
	t->eq->id = 0;
	t->eq->ty = t;
	t->eq->eq = eqclass[t->kind];
	eqclass[t->kind] = t->eq;
}

/*
 * record that we've used the type
 * using a type uses all types reachable from that type
 */
void
reftype(Type *t)
{
	Decl *id, *tg;

	if(t == nil || (t->ok & OKref) == OKref)
		return;
	t->ok |= OKref;
	if(t->decl != nil && t->decl->refs == 0)
		t->decl->refs++;
	switch(t->kind){
	case Terror:
	case Tint:
	case Tbig:
	case Tstring:
	case Treal:
	case Tbyte:
	case Tnone:
	case Tany:
	case Tiface:
	case Tainit:
	case Talt:
	case Tcase:
	case Tcasel:
	case Tcasec:
	case Tgoto:
	case Texcept:
	case Tfix:
	case Tpoly:
		break;
	case Tref:
	case Tchan:
	case Tarray:
	case Tlist:
		if(t->decl != nil){
			if(nadts >= lenadts){
				lenadts = nadts + 32;
				adts = reallocmem(adts, lenadts * sizeof *adts);
			}
			adts[nadts++] = t->decl;
		}
		reftype(t->tof);
		break;
	case Tadt:
	case Tadtpick:
	case Ttuple:
	case Texception:
		if(t->kind == Tadt || t->kind == Ttuple && t->decl->sym != anontupsym){
			if(nadts >= lenadts){
				lenadts = nadts + 32;
				adts = reallocmem(adts, lenadts * sizeof *adts);
			}
			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);
		break;
	case 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;
	case 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);
		break;
	default:
		fatal("reftype: unknown type kind %d", t->kind);
		break;
	}
}

/*
 * check all reachable types for cycles and illegal forward references
 * find the size of all the types
 */
void
cycsizetype(Type *t)
{
	Decl *id, *tg;

	if(t == nil || (t->ok & (OKcycsize|OKcyc|OKsized)) == (OKcycsize|OKcyc|OKsized))
		return;
	t->ok |= OKcycsize;
	switch(t->kind){
	case Terror:
	case Tint:
	case Tbig:
	case Tstring:
	case Treal:
	case Tbyte:
	case Tnone:
	case Tany:
	case Tiface:
	case Tainit:
	case Talt:
	case Tcase:
	case Tcasel:
	case Tcasec:
	case Tgoto:
	case Texcept:
	case Tfix:
	case Tpoly:
		t->ok |= OKcyc;
		sizetype(t);
		break;
	case Tref:
	case Tchan:
	case Tarray:
	case Tlist:
		cyctype(t);
		sizetype(t);
		cycsizetype(t->tof);
		break;
	case Tadt:
	case Ttuple:
	case 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);
		break;
	case Tadtpick:
		t->ok &= ~OKcycsize;
		cycsizetype(t->decl->dot->ty);
		break;
	case Tmodule:
		cyctype(t);
		sizetype(t);
		for(id = t->ids; id != nil; id = id->next)
			cycsizetype(id->ty);
		sizeids(t->ids, 0);
		break;
	case 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);
		break;
	default:
		fatal("cycsizetype: unknown type kind %d", t->kind);
		break;
	}
}

/* check for circularity in type declarations
 * - has to be called before verifytypes
 */
void
tcycle(Type *t)
{
	Decl *id;
	Type *tt;
	Typelist *tl;

	if(t == nil)
		return;
	switch(t->kind){
	default:
		break;
	case Tchan:
	case Tarray:
	case Tref:
	case Tlist:
	case Tdot:
		tcycle(t->tof);
		break;
	case Tfn:
	case Ttuple:
		tcycle(t->tof);
		for(id = t->ids; id != nil; id = id->next)
			tcycle(id->ty);
		break;
	case Tarrow:
		if(t->rec&TRvis){
			error(t->src.start, "circularity in definition of %T", 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;
		break;
	case Tid:
		if(t->rec&TRvis){
			error(t->src.start, "circularity in definition of %T", t);
			*t = *terror;	/* break the cycle */
			return;
		}
		t->rec |= TRvis;
		tcycle(t->decl->ty);
		t->rec &= ~TRvis;
		break;
	case Tinst:
		tcycle(t->tof);
		for(tl = t->u.tlist; tl != nil; tl = tl->nxt)
			tcycle(tl->t);
		break;
	}
}

/*
 * marks for checking for arcs
 */
enum
{
	ArcValue	= 1 << 0,
	ArcList		= 1 << 1,
	ArcArray	= 1 << 2,
	ArcRef		= 1 << 3,
	ArcCyc		= 1 << 4,		/* cycle found */
	ArcPolycyc	= 1 << 5,
};

void
cyctype(Type *t)
{
	Decl *id, *tg;

	if((t->ok & OKcyc) == OKcyc)
		return;
	t->ok |= OKcyc;
	t->rec |= TRcyc;
	switch(t->kind){
	case Terror:
	case Tint:
	case Tbig:
	case Tstring:
	case Treal:
	case Tbyte:
	case Tnone:
	case Tany:
	case Tfn:
	case Tchan:
	case Tarray:
	case Tref:
	case Tlist:
	case Tfix:
	case Tpoly:
		break;
	case Tadt:
	case Tmodule:
	case Ttuple:
	case 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);
		}
		break;
	default:
		fatal("checktype: unknown type kind %d", t->kind);
		break;
	}
	t->rec &= ~TRcyc;
}

void
cycfield(Type *base, Decl *id)
{
	int arc;

	if(!storespace[id->store])
		return;
	arc = cycarc(base, id->ty);

	if((arc & (ArcCyc|ArcValue)) == (ArcCyc|ArcValue)){
		if(id->cycerr == 0)
			error(base->src.start, "illegal type cycle without a reference in field %s of %t",
				id->sym->name, base);
		id->cycerr = 1;
	}else if(arc & ArcCyc){
		if((arc & ArcArray) && oldcycles && id->cyc == 0 && !(arc & ArcPolycyc)){
			if(id->cycerr == 0)
				error(base->src.start, "illegal circular reference to type %T in field %s of %t",
					id->ty, id->sym->name, base);
			id->cycerr = 1;
		}
		id->cycle = 1;
	}else if(id->cyc != 0){
		if(id->cycerr == 0)
			error(id->src.start, "spurious cyclic qualifier for field %s of %t", id->sym->name, base);
		id->cycerr = 1;
	}
}

int
cycarc(Type *base, Type *t)
{
	Decl *id, *tg;
	int me, arc;

	if(t == nil)
		return 0;
	if(t->rec & TRcyc){
		if(tequal(t, base)){
			if(t->kind == Tmodule)
				return ArcCyc | ArcRef;
			else
				return ArcCyc | ArcValue;
		}
		return 0;
	}
	t->rec |= TRcyc;
	me = 0;
	switch(t->kind){
	case Terror:
	case Tint:
	case Tbig:
	case Tstring:
	case Treal:
	case Tbyte:
	case Tnone:
	case Tany:
	case Tchan:
	case Tfn:
	case Tfix:
	case Tpoly:
		break;
	case Tarray:
		me = cycarc(base, t->tof) & ~ArcValue | ArcArray;
		break;
	case Tref:
		me = cycarc(base, t->tof) & ~ArcValue | ArcRef;
		break;
	case Tlist:
		me = cycarc(base, t->tof) & ~ArcValue | ArcList;
		break;
	case Tadt:
	case Tadtpick:
	case Tmodule:
	case Ttuple:
	case Texception:
		me = 0;
		for(id = t->ids; id != nil; id = id->next){
			if(!storespace[id->store])
				continue;
			arc = cycarc(base, id->ty);
			if((arc & ArcCyc) && id->cycerr == 0)
				me |= arc;
		}
		for(tg = t->tags; tg != nil; tg = tg->next){
			arc = cycarc(base, tg->ty);
			if((arc & ArcCyc) && tg->cycerr == 0)
				me |= arc;
		}

		if(t->kind == Tmodule)
			me = me & ArcCyc | ArcRef | ArcPolycyc;
		else
			me &= ArcCyc | ArcValue | ArcPolycyc;
		break;
	default:
		fatal("cycarc: unknown type kind %d", t->kind);
		break;
	}
	t->rec &= ~TRcyc;
	if(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.
 */
void
sizetype(Type *t)
{
	Decl *id, *tg;
	Szal szal;
	long sz, al, a;

	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 %t", t);
	switch(t->kind){
	default:
		fatal("sizetype: unknown type kind %d", t->kind);
		break;
	case Terror:
	case Tnone:
	case Tbyte:
	case Tint:
	case Tbig:
	case Tstring:
	case Tany:
	case Treal:
		fatal("%T should have a size", t);
		break;
	case Tref:
	case Tchan:
	case Tarray:
	case Tlist:
	case Tmodule:
	case Tfix:
	case Tpoly:
		t->size = t->align = IBY2WD;
		break;
	case Ttuple:
	case Tadt:
	case Texception:
		if(t->tags == nil){
			if(!debug['z']){
				szal = sizeids(t->ids, 0);
				t->size = align(szal.size, szal.align);
				t->align = szal.align;
			}else{
				szal = sizeids(t->ids, 0);
				t->align = IBY2LG;
				t->size = align(szal.size, IBY2LG);
			}
			return;
		}
		if(!debug['z']){
			szal = sizeids(t->ids, IBY2WD);
			sz = szal.size;
			al = szal.align;
			if(al < IBY2WD)
				al = IBY2WD;
		}else{
			szal = sizeids(t->ids, IBY2WD);
			sz = szal.size;
			al = IBY2LG;
		}
		for(tg = t->tags; tg != nil; tg = tg->next){
			if((tg->ty->ok & OKsized) == OKsized)
				continue;
			tg->ty->ok |= OKsized;
			if(!debug['z']){
				szal = sizeids(tg->ty->ids, sz);
				a = szal.align;
				if(a < al)
					a = al;
				tg->ty->size = align(szal.size, a);
				tg->ty->align = a;
			}else{
				szal = sizeids(tg->ty->ids, sz);
				tg->ty->size = align(szal.size, IBY2LG);
				tg->ty->align = IBY2LG;
			}			
		}
		break;
	case Tfn:
		t->size = 0;
		t->align = 1;
		break;
	case Tainit:
		t->size = 0;
		t->align = 1;
		break;
	case Talt:
		t->size = t->cse->nlab * 2*IBY2WD + 2*IBY2WD;
		t->align = IBY2WD;
		break;
	case Tcase:
	case Tcasec:
		t->size = t->cse->nlab * 3*IBY2WD + 2*IBY2WD;
		t->align = IBY2WD;
		break;
	case Tcasel:
		t->size = t->cse->nlab * 6*IBY2WD + 3*IBY2WD;
		t->align = IBY2LG;
		break;
	case Tgoto:
		t->size = t->cse->nlab * IBY2WD + IBY2WD;
		if(t->cse->iwild != nil)
			t->size += IBY2WD;
		t->align = IBY2WD;
		break;
	case Tiface:
		sz = IBY2WD;
		for(id = t->ids; id != nil; id = id->next){
			sz = align(sz, IBY2WD) + IBY2WD;
			sz += id->sym->len + 1;
			if(id->dot->ty->kind == Tadt)
				sz += id->dot->sym->len + 1;
		}
		t->size = sz;
		t->align = IBY2WD;
		break;
	case Texcept:
		t->size = 0;
		t->align = IBY2WD;
		break;
	}
}

Szal
sizeids(Decl *id, long off)
{
	Szal szal;
	int a, al;

	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;
		}
	}
	szal.size = off;
	szal.align = al;
	return szal;
}

long
align(long off, int align)
{
	if(align == 0)
		fatal("align 0");
	while(off % align)
		off++;
	return off;
}

/*
 * recalculate a type's size
 */
void
resizetype(Type *t)
{
	if((t->ok & OKsized) == OKsized){
		t->ok &= ~OKsized;
		cycsizetype(t);
	}
}

/*
 * check if a module is accessable from t
 * if so, mark that module interface
 */
void
modrefable(Type *t)
{
	Decl *id, *m, *tg;

	if(t == nil || (t->ok & OKmodref) == OKmodref)
		return;
	if((t->ok & OKverify) != OKverify)
		fatal("modrefable unused type %t", t);
	t->ok |= OKmodref;
	switch(t->kind){
	case Terror:
	case Tint:
	case Tbig:
	case Tstring:
	case Treal:
	case Tbyte:
	case Tnone:
	case Tany:
	case Tfix:
	case Tpoly:
		break;
	case Tchan:
	case Tref:
	case Tarray:
	case Tlist:
		modrefable(t->tof);
		break;
	case Tmodule:
		t->tof->linkall = 1;
		t->decl->refs++;
		for(id = t->ids; id != nil; id = id->next){
			switch(id->store){
			case Dglobal:
			case Dfn:
				modrefable(id->ty);
				break;
			case Dtype:
				if(id->ty->kind != Tadt)
					break;
				for(m = id->ty->ids; m != nil; m = m->next)
					if(m->store == Dfn)
						modrefable(m->ty);
				break;
			}
		}
		break;
	case Tfn:
	case Tadt:
	case Ttuple:
	case 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);
		break;
	case Tadtpick:
		modrefable(t->decl->dot->ty);
		break;
	default:
		fatal("unknown type kind %d", t->kind);
		break;
	}
}

Desc*
gendesc(Decl *d, long size, Decl *decls)
{
	Desc *desc;

	if(debug['D'])
		print("generate desc for %D\n", d);
	if(ispoly(d))
		addfnptrs(d, 0);
	desc = usedesc(mkdesc(size, decls));
	return desc;
}

Desc*
mkdesc(long size, Decl *d)
{
	uchar *pmap;
	long len, n;

	len = (size+8*IBY2WD-1) / (8*IBY2WD);
	pmap = allocmem(len);
	memset(pmap, 0, len);
	n = descmap(d, pmap, 0);
	if(n >= 0)
		n = n / (8*IBY2WD) + 1;
	else
		n = 0;
	if(n > len)
		fatal("wrote off end of decl map: %ld %ld", n, len);
	return enterdesc(pmap, size, n);
}

Desc*
mktdesc(Type *t)
{
	Desc *d;
	uchar *pmap;
	long len, n;

usedty(t);
	if(debug['D'])
		print("generate desc for %T\n", 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;
	len = (t->size+8*IBY2WD-1) / (8*IBY2WD);
	pmap = allocmem(len);
	memset(pmap, 0, len);
	n = tdescmap(t, pmap, 0);
	if(n >= 0)
		n = n / (8*IBY2WD) + 1;
	else
		n = 0;
	if(n > len)
		fatal("wrote off end of type map for %T: %ld %ld 0x%2.2ux", t, n, len, t->ok);
	d = enterdesc(pmap, t->size, n);
	t->decl->desc = d;
	if(debug['j']){
		uchar *m, *e;

		print("generate desc for %T\n", t);
		print("\tdesc\t$%d,%lud,\"", d->id, d->size);
		e = d->map + d->nmap;
		for(m = d->map; m < e; m++)
			print("%.2x", *m);
		print("\"\n");
	}
	return d;
}

Desc*
enterdesc(uchar *map, long size, long nmap)
{
	Desc *d, *last;
	int c;

	last = 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 = memcmp(d->map, map, nmap);
			if(c == 0){
				free(map);
				return d;
			}
			if(c > 0)
				break;
		}
		last = d;
	}
	d = allocmem(sizeof *d);
	d->id = -1;
	d->used = 0;
	d->map = map;
	d->size = size;
	d->nmap = nmap;
	if(last == nil){
		d->next = descriptors;
		descriptors = d;
	}else{
		d->next = last->next;
		last->next = d;
	}
	return d;
}

Desc*
usedesc(Desc *d)
{
	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
 */
long
descmap(Decl *decls, uchar *map, long start)
{
	Decl *d;
	long last, m;

	if(debug['D'])
		print("descmap offset %ld\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 %T offset %ld returns %ld\n",
					d->sym->name, d->ty, d->offset+start, m);
			else
				print("descmap type %T offset %ld returns %ld\n", d->ty, d->offset+start, m);
		}
		if(m >= 0)
			last = m;
	}
	return last;
}

long
tdescmap(Type *t, uchar *map, long offset)
{
	Label *lab;
	long i, e, m;
	int bit;

	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)] |= 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)] |= 1 << (7 - bit);
			offset += IBY2WD;
			bit = offset / IBY2WD % 8;
			map[offset / (8*IBY2WD)] |= 1 << (7 - bit);
			m = offset;
			offset += 2*IBY2WD;
		}
		return m;
	}

	if(tattr[t->kind].isptr){
		bit = offset / IBY2WD % 8;
		map[offset / (8*IBY2WD)] |= 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 %ld\n", offset);
		if(t->rec != 0)
			fatal("illegal cyclic type %t in tdescmap", t);
		t->rec = 1;
		offset = descmap(t->ids, map, offset);
		t->rec = 0;
		return offset;
	}

	return -1;
}

/*
 * can a t2 be assigned to a t1?
 * any means Tany matches all types,
 * not just references
 */
int
tcompat(Type *t1, Type *t2, int any)
{
	int ok, v;

	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 %t and t2 %t not balanced in tcompat: %d v %d", t1, t2, v, tcomset);
	return ok;
}

static int
rtcompat(Type *t1, Type *t2, int any, int inaorc)
{
	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);

	if(debug['x'])
		print("rtcompat: %t and %t\n", t1, t2);

	t1->rec |= TRcom;
	t2->rec |= TRcom;
	switch(t1->kind){
	default:
		fatal("unknown type %t v %t in rtcompat", t1, t2);
	case Tstring:
		return t2->kind == Tstring || t2->kind == Tany;
	case 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;
	case Tnone:
	case Tint:
	case Tbig:
	case Tbyte:
	case Treal:
		return t1->kind == t2->kind;
	case Tfix:
		return t1->kind == t2->kind && sametree(t1->val, t2->val);
	case Tany:
		if(tattr[t2->kind].isptr)
			return 1;
		return any;
	case Tref:
	case Tlist:
	case Tarray:
	case 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);
	case Tfn:
		break;
	case 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;
	case 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;
		break;
	case Tadtpick:
/*
		if(t2->kind == Ttuple)
			return idcompat(t1->tof->ids->next, t2->ids, any, inaorc);
*/
		break;
	case Tmodule:
		if(t2->kind == Tany)
			return 1;
		break;
	case Tpoly:
		if(t2->kind == Tany)
			return 1;
		break;
	}
	return tequal(t1, t2);
}

/*
 * add the assumption that t1 and t2 are compatable
 */
static int
assumetcom(Type *t1, Type *t2)
{
	Type *r1, *r2;

	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;
}

static int
cleartcomrec(Type *t)
{
	Decl *id;
	int n;

	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
 */
static int
idcompat(Decl *id1, Decl *id2, int any, int inaorc)
{
	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;
}

int
tequal(Type *t1, Type *t2)
{
	int ok, v;

	eqrec = 0;
	eqset = 0;
	ok = rtequal(t1, t2);
	v = cleareqrec(t1) + cleareqrec(t2);
	if(v != eqset && 0)
		fatal("recid t1 %t and t2 %t not balanced in tequal: %d %d", t1, t2, v, eqset);
	eqset = 0;
	return ok;
}

/*
 * structural equality on types
 */
static int
rtequal(Type *t1, Type *t2)
{
	/*
	 * 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;

	if(debug['x'])
		print("rtequal: %t and %t\n", t1, t2);

	t1->rec |= TReq;
	t2->rec |= TReq;
	switch(t1->kind){
	default:
		fatal("unknown type %t v %t in rtequal", t1, t2);
	case Tnone:
	case Tbig:
	case Tbyte:
	case Treal:
	case Tint:
	case Tstring:
		/*
		 * this should always be caught by t1 == t2 check
		 */
		fatal("bogus value type %t vs %t in rtequal", t1, t2);
		return 1;
	case Tfix:
		return sametree(t1->val, t2->val);
	case Tref:
	case Tlist:
	case Tarray:
	case Tchan:
		if(t1->kind != Tref && assumeteq(t1, t2))
			return 1;
		return rtequal(t1->tof, t2->tof);
	case 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);
	case Ttuple:
	case 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);
	case Tadt:
	case Tadtpick:
	case 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);
	case Tpoly:
		if(assumeteq(t1, t2))
			return 1;
		if(t1->decl->sym != t2->decl->sym)
			return 0;
		return idequal(t1->ids, t2->ids, 1, nil);
	}
}

static int
assumeteq(Type *t1, Type *t2)
{
	Type *r1, *r2;

	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 adts, tuples, and fns
 */
static int
idequal(Decl *id1, Decl *id2, int usenames, int *storeok)
{
	/*
	 * 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;
}

static int
pyequal(Type *t1, Type *t2)
{
	Type *pt1, *pt2;
	Decl *id1, *id2;

	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->u.tmap != nil)
				pt1 = valtmap(pt1, t1->u.tmap);
			if(t2->u.tmap != nil)
				pt2 = valtmap(pt2, t2->u.tmap);
			if(!rtequal(pt1, pt2))
				return 0;
		}
		id2 = id2->next;
	}
	return id1 == nil && id2 == nil;
}

static int
cleareqrec(Type *t)
{
	Decl *id;
	int n;

	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;
}

int
raisescompat(Node *n1, Node *n2)
{
	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 = n1->left, n2 = n2->left; n1 != nil && n2 != nil; n1 = n1->right, n2 = n2->right){
		if(n1->left->decl != n2->left->decl)
			return 0;
	}
	return n1 == n2;
}

/* t1 a polymorphic type */
static int
fnunify(Type *t1, Type *t2, Tpair **tp, int swapped)
{
	Decl *id, *ids;
	Sym *sym;

	for(ids = t1->ids; ids != nil; ids = ids->next){
		sym = ids->sym;
		id = fnlookup(sym, t2, nil);
		if(id != nil)
			usetype(id->ty);
		if(id == nil){
			if(dowarn)
				error(unifysrc.start, "type %T does not have a '%s' function", t2, sym->name);
			return 0;
		}
		else if(id->ty->kind != Tfn){
			if(dowarn)
				error(unifysrc.start, "%T is not a function", id->ty);
			return 0;
		}
		else if(!rtunify(ids->ty, id->ty, tp, !swapped)){
			if(dowarn)
				error(unifysrc.start, "%T and %T are not compatible wrt %s", ids->ty, id->ty, sym->name);
			return 0;
		}
	}
	return 1;
}

static int
fncleareqrec(Type *t1, Type *t2)
{
	Decl *id, *ids;
	int n;

	n = 0;
	n += cleareqrec(t1);
	n += cleareqrec(t2);
	for(ids = t1->ids; ids != nil; ids = ids->next){
		id = fnlookup(ids->sym, t2, nil);
		if(id == nil)
			continue;
		else{
			n += cleareqrec(ids->ty);
			n += cleareqrec(id->ty);
		}
	}
	return n;
}
int
tunify(Type *t1, Type *t2, Tpair **tp)
{
	int ok, v;
	Tpair *p;

	*tp = nil;
	eqrec = 0;
	eqset = 0;
	ok = rtunify(t1, t2, tp, 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 %t and t2 %t not balanced in tunify: %d %d", t1, t2, v, eqset);
	return ok;
}

static int
rtunify(Type *t1, Type *t2, Tpair **tp, int swapped)
{
	Type *tmp;

if(debug['w']) print("rtunifya - %T %T\n", t1, t2);
	t1 = valtmap(t1, *tp);
	t2 = valtmap(t2, *tp);
if(debug['w']) print("rtunifyb - %T %T\n", t1, t2);
	if(t1 == t2)
		return 1;
	if(t1 == nil || t2 == nil)
		return 0;
	if(t1->kind == Terror || t2->kind == Terror)
		return 1;
	if(t1->kind != Tpoly && t2->kind == Tpoly){
		tmp = t1;
		t1 = t2;
		t2 = tmp;
		swapped = !swapped;
	}
	if(t1->kind == Tpoly){
/*
		if(typein(t1, t2))
			 return 0;
*/
		if(!tattr[t2->kind].isptr)
			return 0;
		if(t2->kind != Tany)
			addtmap(t1, t2, tp);
		return fnunify(t1, t2, tp, swapped);
	}
	if(t1->kind != Tany && t2->kind == Tany){
		tmp = t1;
		t1 = t2;
		t2 = tmp;
		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;
	t1->rec |= TReq;
	t2->rec |= TReq;
	switch(t1->kind){
	default:
		return tequal(t1, t2);
	case Tany:
		return tattr[t2->kind].isptr;
	case Tref:
	case Tlist:
	case Tarray:
	case Tchan:
		if(t1->kind != Tref && assumeteq(t1, t2))
			return 1;
		return rtunify(t1->tof, t2->tof, tp, swapped);
	case Tfn:
		if(!idunify(t1->ids, t2->ids, tp, swapped))
			return 0;
		if(!idunify(t1->polys, t2->polys, tp, swapped))
			return 0;
		return rtunify(t1->tof, t2->tof, tp, swapped);
	case Ttuple:
		if(assumeteq(t1, t2))
			return 1;
		return idunify(t1->ids, t2->ids, tp, swapped);
	case Tadt:
	case Tadtpick:
		if(assumeteq(t1, t2))
			return 1;
		if(!idunify(t1->polys, t2->polys, tp, swapped))
			return 0;
		if(!idunify(t1->tags, t2->tags, tp, swapped))
			return 0;
		return idunify(t1->ids, t2->ids, tp, swapped);
	case Tmodule:
		if(assumeteq(t1, t2))
			return 1;
		return idunify(t1->tof->ids, t2->tof->ids, tp, swapped);
	case Tpoly:
		return t1 == t2;
	}
}

static int
idunify(Decl *id1, Decl *id2, Tpair **tp, int swapped)
{
	if(id1 == id2)
		return 1;
	for(; id1 != nil; id1 = id1->next){
		if(id2 == nil || !rtunify(id1->ty, id2->ty, tp, swapped))
			return 0;
		id2 = id2->next;
	}
	return id1 == nil && id2 == nil;
}

int
polyequal(Decl *id1, Decl *id2)
{
	int ck2;
	Decl *d;

	/* 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;
}

Type*
calltype(Type *f, Node *a, Type *rt)
{
	Type *t;
	Decl *id, *first, *last;

	first = last = nil;
	t = mktype(&f->src.start, &f->src.stop, Tfn, rt, nil);
	t->polys = f->kind == Tref ? f->tof->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;
}

static Type*
duptype(Type *t)
{
	Type *nt;

	nt = allocmem(sizeof(*nt));
	*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;
}

static int
dpolys(Decl *ids)
{
	Decl *p;

	for(p = ids; p != nil; p = p->next)
		if(tpolys(p->ty))
			return 1;
	return 0;
}

static int
tpolys(Type *t)
{
	int v;
	Typelist *tl;

	if(t == nil)
		return 0;
	if(t->flags&(POLY|NOPOLY))
		return t->flags&POLY;
	switch(t->kind){
	default:
		v = 0;
		break;
	case Tarrow:
	case Tdot:
	case Tpoly:
		v = 1;
		break;
	case Tref:
	case Tlist:
	case Tarray:
	case Tchan:
		v = tpolys(t->tof);
		break;
	case Tid:
		v = tpolys(t->decl->ty);
		break;
	case Tinst:
		v = 0;
		for(tl = t->u.tlist; tl != nil; tl = tl->nxt)
			if(tpolys(tl->t)){
				v = 1;
				break;
			}
		if(v == 0)
			v = tpolys(t->tof);
		break;
	case Tfn:
	case Tadt:
	case Tadtpick:
	case Ttuple:
	case Texception:
		if(t->polys != nil){
			v = 1;
			break;
		}
		if(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;
}

static int
doccurs(Decl *ids, Tpair **tp)
{
	Decl *p;

	for(p = ids; p != nil; p = p->next)
		if(toccurs(p->ty, tp))
			return 1;
	return 0;
}

static int
toccurs(Type *t, Tpair **tp)
{
	int o;
	Typelist *tl;

	if(t == nil)
		return 0;
	if(!(t->flags&(POLY|NOPOLY)))
		tpolys(t);
	if(t->flags&NOPOLY)
		return 0;
	switch(t->kind){
		default:
			fatal("unknown type %t in toccurs", t);
		case Tnone:
		case Tbig:
		case Tbyte:
		case Treal:
		case Tint:
		case Tstring:
		case Tfix:
		case Tmodule:
		case Terror:
			return 0;
		case Tarrow:
		case Tdot:
			return 1;
		case Tpoly:
			return valtmap(t, *tp) != t;
		case Tref:
		case Tlist:
		case Tarray:
		case Tchan:
			return toccurs(t->tof, tp);
		case Tid:
			return toccurs(t->decl->ty, tp);
		case Tinst:
			for(tl = t->u.tlist; tl != nil; tl = tl->nxt)
				if(toccurs(tl->t, tp))
					return 1;
			return toccurs(t->tof, tp);
		case Tfn:
		case Tadt:
		case Tadtpick:
		case Ttuple:
		case Texception:
			if(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;
	}
}

static Decl*
expandids(Decl *ids, Decl *adtt, Tpair **tp, int sym)
{
	Decl *p, *q, *nids, *last;

	nids = last = nil;
	for(p = ids; p != nil; p = p->next){
		q = dupdecl(p);
		q->ty = expandtype(p->ty, nil, adtt, tp);
		if(sym && q->ty->decl != nil)
			q->sym = q->ty->decl->sym;
		if(q->store == Dfn){
if(debug['v']) print("%p->link = %p\n", q, p);
			q->link = p;
		}
		if(nids == nil)
			nids = q;
		else
			last->next = q;
		last = q;
	}
	return nids;
}

Type*
expandtype(Type *t, Type *instt, Decl *adtt, Tpair **tp)
{
	Type *nt;
	Decl *ids;

	if(t == nil)
		return nil;
if(debug['w']) print("expandtype %d %#p %T\n", t->kind, t, t);
	if(!toccurs(t, tp))
		return t;
if(debug['w']) print("\texpanding\n");
	switch(t->kind){
		default:
			fatal("unknown type %t in expandtype", t);
		case Tpoly:
			return valtmap(t, *tp);
		case Tref:
		case Tlist:
		case Tarray:
		case Tchan:
			nt = duptype(t);
			nt->tof = expandtype(t->tof, nil, adtt, tp);
			return nt;
		case Tid:
			return expandtype(idtype(t), nil, adtt, tp);
		case Tdot:
			return expandtype(dottype(t, adtt), nil, adtt, tp);
		case Tarrow:
			return expandtype(arrowtype(t, adtt), nil, adtt, tp);
		case Tinst:
			if((nt = valtmap(t, *tp)) != t)
				return nt;
			return expandtype(insttype(t, adtt, tp), nil, adtt, tp);
		case Tfn:
		case Tadt:
		case Tadtpick:
		case Ttuple:
		case Texception:
			if((nt = valtmap(t, *tp)) != t)
				return nt;
			if(t->kind == Tadt)
				adtt = t->decl;
			nt = duptype(t);
			addtmap(t, nt, tp);
			if(instt != nil)
				addtmap(instt, nt, tp);
			nt->tof = expandtype(t->tof, nil, adtt, tp);
			nt->polys = expandids(t->polys, adtt, tp, 1);
			nt->ids = expandids(t->ids, adtt, tp, 0);
			nt->tags = 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 = expandtype(t->decl->dot->ty, nil, adtt, tp);
			}
			if((t->kind == Tadt || t->kind == Tadtpick) && t->u.tmap != nil){
				Tpair *p;

				nt->u.tmap = nil;
				for(p = t->u.tmap; p != nil; p = p->nxt)
					addtmap(valtmap(p->t1, *tp), valtmap(p->t2, *tp), &nt->u.tmap);
				if(debug['w']){
					print("new tmap for %T->%T: ", t, nt);
					for(p=nt->u.tmap;p!=nil;p=p->nxt)print("%T -> %T ", p->t1, p->t2);
					print("\n");
				}
			}
			return nt;
	}
}

/*
 * create type signatures
 * sign the same information used
 * for testing type equality
 */
ulong
sign(Decl *d)
{
	Type *t;
	uchar *sig, md5sig[MD5dlen];
	char buf[StrSize];
	int i, sigend, sigalloc, v;

	t = d->ty;
	if(t->sig != 0)
		return t->sig;

	if(ispoly(d))
		rmfnptrs(d);

	sig = 0;
	sigend = -1;
	sigalloc = 1024;
	while(sigend < 0 || sigend >= sigalloc){
		sigalloc *= 2;
		sig = reallocmem(sig, sigalloc);
		eqrec = 0;
		sigend = rtsign(t, sig, sigalloc, 0);
		v = clearrec(t);
		if(v != eqrec)
			fatal("recid not balanced in sign: %d %d", v, eqrec);
		eqrec = 0;
	}
	sig[sigend] = '\0';

	if(signdump != nil){
		seprint(buf, buf+sizeof(buf), "%D", d);
		if(strcmp(buf, signdump) == 0){
			print("sign %D len %d\n", d, sigend);
			print("%s\n", (char*)sig);
		}
	}

	md5(sig, sigend, md5sig, nil);
	for(i = 0; i < MD5dlen; i += 4)
		t->sig ^= md5sig[i+0] | (md5sig[i+1]<<8) | (md5sig[i+2]<<16) | (md5sig[i+3]<<24);
	if(debug['S'])
		print("signed %D type %T len %d sig %#lux\n", d, t, sigend, t->sig);
	free(sig);
	return t->sig;
}

enum
{
	SIGSELF =	'S',
	SIGVARARGS =	'*',
	SIGCYC =	'y',
	SIGREC =	'@'
};

static int sigkind[Tend] =
{
	/* Tnone */	'n',
	/* Tadt */	'a',
	/* Tadtpick */	'p',
	/* Tarray */	'A',
	/* Tbig */	'B',
	/* Tbyte */	'b',
	/* Tchan */	'C',
	/* Treal */	'r',
	/* Tfn */	'f',
	/* Tint */	'i',
	/* Tlist */	'L',
	/* Tmodule */	'm',
	/* Tref */	'R',
	/* Tstring */	's',
	/* Ttuple */	't',
	/* Texception */	'e',
	/* Tfix */	'x',
	/* Tpoly */	'P',
};

static int
rtsign(Type *t, uchar *sig, int lensig, int spos)
{
	Decl *id, *tg;
	char name[32];
	int kind, lenname;

	if(t == nil)
		return spos;

	if(spos < 0 || spos + 8 >= lensig)
		return -1;

	if(t->eq != nil && t->eq->id){
		if(t->eq->id < 0 || t->eq->id > eqrec)
			fatal("sign rec %T %d %d", t, t->eq->id, eqrec);

		sig[spos++] = SIGREC;
		seprint(name, name+sizeof(name), "%d", t->eq->id);
		lenname = strlen(name);
		if(spos + lenname > lensig)
			return -1;
		strcpy((char*)&sig[spos], name);
		spos += lenname;
		return spos;
	}
	if(t->eq != nil){
		eqrec++;
		t->eq->id = eqrec;
	}

	kind = sigkind[t->kind];
	sig[spos++] = kind;
	if(kind == 0)
		fatal("no sigkind for %t", t);

	t->rec = 1;
	switch(t->kind){
	default:
		fatal("bogus type %t in rtsign", t);
		return -1;
	case Tnone:
	case Tbig:
	case Tbyte:
	case Treal:
	case Tint:
	case Tstring:
	case Tpoly:
		return spos;
	case Tfix:
		seprint(name, name+sizeof(name), "%g", t->val->rval);
		lenname = strlen(name);
		if(spos+lenname-1 >= lensig)
			return -1;
		strcpy((char*)&sig[spos], name);
		spos += lenname;
		return spos;
	case Tref:
	case Tlist:
	case Tarray:
	case Tchan:
		return rtsign(t->tof, sig, lensig, spos);
	case Tfn:
		if(t->varargs != 0)
			sig[spos++] = SIGVARARGS;
		if(t->polys != nil)
			spos = idsign(t->polys, 0, sig, lensig, spos);
		spos = idsign(t->ids, 0, sig, lensig, spos);
		if(t->u.eraises)
			spos = raisessign(t->u.eraises, sig, lensig, spos);
		return rtsign(t->tof, sig, lensig, spos);
	case Ttuple:
		return idsign(t->ids, 0, sig, lensig, spos);
	case 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(strcmp(t->decl->sym->name, ".mp") != 0)
				fatal("no t->eq field for %t", t);
			spos--;
			for(id = t->ids; id != nil; id = id->next){
				spos = idsign1(id, 1, sig, lensig, spos);
				if(spos < 0 || spos >= lensig)
					return -1;
				sig[spos++] = ';';
			}
			return spos;
		}
		if(t->polys != nil)
			spos = idsign(t->polys, 0, sig, lensig, spos);
		spos = idsign(t->ids, 1, sig, lensig, spos);
		if(spos < 0 || t->tags == nil)
			return spos;

		/*
		 * convert closing ')' to a ',', then sign any tags
		 */
		sig[spos-1] = ',';
		for(tg = t->tags; tg != nil; tg = tg->next){
			lenname = tg->sym->len;
			if(spos + lenname + 2 >= lensig)
				return -1;
			strcpy((char*)&sig[spos], tg->sym->name);
			spos += lenname;
			sig[spos++] = '=';
			sig[spos++] = '>';

			spos = rtsign(tg->ty, sig, lensig, spos);
			if(spos < 0 || spos >= lensig)
				return -1;

			if(tg->next != nil)
				sig[spos++] = ',';
		}
		if(spos >= lensig)
			return -1;
		sig[spos++] = ')';
		return spos;
	case Tadtpick:
		spos = idsign(t->ids, 1, sig, lensig, spos);
		if(spos < 0)
			return spos;
		return rtsign(t->decl->dot->ty, sig, lensig, spos);
	case Tmodule:
		if(t->tof->linkall == 0)
			fatal("signing a narrowed module");

		if(spos >= lensig)
			return -1;
		sig[spos++] = '{';
		for(id = t->tof->ids; id != nil; id = id->next){
			if(id->tag)
				continue;
			if(strcmp(id->sym->name, ".mp") == 0){
				spos = rtsign(id->ty, sig, lensig, spos);
				if(spos < 0)
					return -1;
				continue;
			}
			spos = idsign1(id, 1, sig, lensig, spos);
			if(spos < 0 || spos >= lensig)
				return -1;
			sig[spos++] = ';';
		}
		if(spos >= lensig)
			return -1;
		sig[spos++] = '}';
		return spos;
	}
}

static int
idsign(Decl *id, int usenames, uchar *sig, int lensig, int spos)
{
	int first;

	if(spos >= lensig)
		return -1;
	sig[spos++] = '(';
	first = 1;
	for(; id != nil; id = id->next){
		if(id->store == Dlocal)
			fatal("local %s in idsign", id->sym->name);

		if(!storespace[id->store])
			continue;

		if(!first){
			if(spos >= lensig)
				return -1;
			sig[spos++] = ',';
		}

		spos = idsign1(id, usenames, sig, lensig, spos);
		if(spos < 0)
			return -1;
		first = 0;
	}
	if(spos >= lensig)
		return -1;
	sig[spos++] = ')';
	return spos;
}

static int
idsign1(Decl *id, int usenames, uchar *sig, int lensig, int spos)
{
	char *name;
	int lenname;

	if(usenames){
		name = id->sym->name;
		lenname = id->sym->len;
		if(spos + lenname + 1 >= lensig)
			return -1;
		strcpy((char*)&sig[spos], name);
		spos += lenname;
		sig[spos++] = ':';
	}

	if(spos + 2 >= lensig)
		return -1;

	if(id->implicit != 0)
		sig[spos++] = SIGSELF;

	if(id->cyc != 0)
		sig[spos++] = SIGCYC;

	return rtsign(id->ty, sig, lensig, spos);
}

static int
raisessign(Node *n, uchar *sig, int lensig, int spos)
{
	int m;
	char *s;
	Node *nn;

	if(spos >= lensig)
		return -1;
	sig[spos++] = '(';
	for(nn = n->left; nn != nil; nn = nn->right){
		s = nn->left->decl->sym->name;
		m = nn->left->decl->sym->len;
		if(spos+m-1 >= lensig)
			return -1;
		strcpy((char*)&sig[spos], s);
		spos += m;
		if(nn->right != nil){
			if(spos >= lensig)
				return -1;
			sig[spos++] = ',';
		}
	}
	if(spos >= lensig)
		return -1;
	sig[spos++] = ')';
	return spos;
}

static int
clearrec(Type *t)
{
	Decl *id;
	int n;

	n = 0;
	for(; t != nil && t->rec; t = t->tof){
		t->rec = 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) */
int
tmustzero(Type *t)
{
	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;
}

int
mustzero(Decl *decls)
{
	Decl *d;

	for (d = decls; d != nil; d = d->next)
		if (tmustzero(d->ty))
			return 1;
	return 0;
}

int
typeconv(Fmt *f)
{
	Type *t;
	char *p, buf[1024];

	t = va_arg(f->args, Type*);
	if(t == nil){
		p = "nothing";
	}else{
		p = buf;
		buf[0] = 0;
		tprint(buf, buf+sizeof(buf), t);
	}
	return fmtstrcpy(f, p);
}

int
stypeconv(Fmt *f)
{
	Type *t;
	char *p, buf[1024];

	t = va_arg(f->args, Type*);
	if(t == nil){
		p = "nothing";
	}else{
		p = buf;
		buf[0] = 0;
		stprint(buf, buf+sizeof(buf), t);
	}
	return fmtstrcpy(f, p);
}

int
ctypeconv(Fmt *f)
{
	Type *t;
	char buf[1024];

	t = va_arg(f->args, Type*);
	buf[0] = 0;
	ctprint(buf, buf+sizeof(buf), t);
	return fmtstrcpy(f, buf);
}

char*
tprint(char *buf, char *end, Type *t)
{
	Decl *id;
	Typelist *tl;

	if(t == nil)
		return buf;
	if(t->kind >= Tend)
		return seprint(buf, end, "kind %d", t->kind);
	switch(t->kind){
	case Tarrow:
		buf = seprint(buf, end, "%T->%s", t->tof, t->decl->sym->name);
		break;
	case Tdot:
		buf = seprint(buf, end, "%T.%s", t->tof, t->decl->sym->name);
		break;
	case Tid:
	case Tpoly:
		buf = seprint(buf, end, "%s", t->decl->sym->name);
		break;
	case Tinst:
		buf = tprint(buf, end, t->tof);
		buf = secpy(buf ,end, "[");
		for(tl = t->u.tlist; tl != nil; tl = tl->nxt){
			buf = tprint(buf, end, tl->t);
			if(tl->nxt != nil)
				buf = secpy(buf, end, ", ");
		}
		buf = secpy(buf, end, "]");
		break;
	case Tint:
	case Tbig:
	case Tstring:
	case Treal:
	case Tbyte:
	case Tany:
	case Tnone:
	case Terror:
	case Tainit:
	case Talt:
	case Tcase:
	case Tcasel:
	case Tcasec:
	case Tgoto:
	case Tiface:
	case Texception:
	case Texcept:
		buf = secpy(buf, end, kindname[t->kind]);
		break;
	case Tfix:
		buf = seprint(buf, end, "%s(%v)", kindname[t->kind], t->val);
		break;
	case Tref:
		buf = secpy(buf, end, "ref ");
		buf = tprint(buf, end, t->tof);
		break;
	case Tchan:
	case Tarray:
	case Tlist:
		buf = seprint(buf, end, "%s of ", kindname[t->kind]);
		buf = tprint(buf, end, t->tof);
		break;
	case Tadtpick:
		buf = seprint(buf, end, "%s.%s", t->decl->dot->sym->name, t->decl->sym->name);
		break;
	case Tadt:
		if(t->decl->dot != nil && !isimpmod(t->decl->dot->sym))
			buf = seprint(buf, end, "%s->%s", t->decl->dot->sym->name, t->decl->sym->name);
		else
			buf = seprint(buf, end, "%s", t->decl->sym->name);
		if(t->polys != nil){
			buf = secpy(buf ,end, "[");
			for(id = t->polys; id != nil; id = id->next){
				if(t->u.tmap != nil)
					buf = tprint(buf, end, valtmap(id->ty, t->u.tmap));
				else
					buf = seprint(buf, end, "%s", id->sym->name);
				if(id->next != nil)
					buf = secpy(buf, end, ", ");
			}
			buf = secpy(buf, end, "]");
		}
		break;
	case Tmodule:
		buf = seprint(buf, end, "%s", t->decl->sym->name);
		break;
	case Ttuple:
		buf = secpy(buf, end, "(");
		for(id = t->ids; id != nil; id = id->next){
			buf = tprint(buf, end, id->ty);
			if(id->next != nil)
				buf = secpy(buf, end, ", ");
		}
		buf = secpy(buf, end, ")");
		break;
	case Tfn:
		buf = secpy(buf, end, "fn");
		if(t->polys != nil){
			buf = secpy(buf, end, "[");
			for(id = t->polys; id != nil; id = id->next){
				buf = seprint(buf, end, "%s", id->sym->name);
				if(id->next != nil)
					buf = secpy(buf, end, ", ");
			}
			buf = secpy(buf, end, "]");
		}
		buf = secpy(buf, end, "(");
		for(id = t->ids; id != nil; id = id->next){
			if(id->sym == nil)
				buf = secpy(buf, end, "nil: ");
			else
				buf = seprint(buf, end, "%s: ", id->sym->name);
			if(id->implicit)
				buf = secpy(buf, end, "self ");
			buf = tprint(buf, end, id->ty);
			if(id->next != nil)
				buf = secpy(buf, end, ", ");
		}
		if(t->varargs && t->ids != nil)
			buf = secpy(buf, end, ", *");
		else if(t->varargs)
			buf = secpy(buf, end, "*");
		if(t->tof != nil && t->tof->kind != Tnone){
			buf = secpy(buf, end, "): ");
			buf = tprint(buf, end, t->tof);
			break;
		}
		buf = secpy(buf, end, ")");
		break;
	default:
		yyerror("tprint: unknown type kind %d", t->kind);
		break;
	}
	return buf;
}

char*
stprint(char *buf, char *end, Type *t)
{
	if(t == nil)
		return buf;
	switch(t->kind){
	case Tid:
		return seprint(buf, end, "id %s", t->decl->sym->name);
	case Tadt:
	case Tadtpick:
	case Tmodule:
		buf = secpy(buf, end, kindname[t->kind]);
		buf = secpy(buf, end, " ");
		return tprint(buf, end, t);
	}
	return tprint(buf, end, t);
}

/* generalize ref P.A, ref P.B to ref P */

/*
Type*
tparentx(Type *t1, Type* t2)
{
	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;
}
*/

static int
tparent0(Type *t1, Type *t2)
{
	Decl *id1, *id2;

	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;
	switch(t1->kind){
	default:
		fatal("unknown type %t v %t in tparent", t1, t2);
		break;
	case Terror:
	case Tstring:
	case Tnone:
	case Tint:
	case Tbig:
	case Tbyte:
	case Treal:
	case Tany:
		return 1;
	case Texception:
	case Tfix:
	case Tfn:
	case Tadt:
	case Tmodule:
	case Tpoly:
		return tcompat(t1, t2, 0);
	case Tref:
	case Tlist:
	case Tarray:
	case Tchan:
		return tparent0(t1->tof, t2->tof);
	case Ttuple:
		for(id1 = t1->ids, id2 = t2->ids; id1 != nil && id2 != nil; id1 = id1->next, id2 = id2->next)
			if(!tparent0(id1->ty, id2->ty))
				return 0;
		return id1 == nil && id2 == nil;
	case Tadtpick:
		return tequal(t1->decl->dot->ty, t2->decl->dot->ty);
	}
	return 0;
}

static Type*
tparent1(Type *t1, Type *t2)
{
	Type *t, *nt;
	Decl *id, *id1, *id2, *idt;

	if(t1->kind == Tadt && t2->kind == Tadtpick)
		t2 = t2->decl->dot->ty;
	if(t1->kind == Tadtpick && t2->kind == Tadt)
		t1 = t1->decl->dot->ty;
	switch(t1->kind){
	default:
		return t1;
	case Tref:
	case Tlist:
	case Tarray:
	case 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);
	case Ttuple:
		nt = nil;
		id = nil;
		for(id1 = t1->ids, id2 = t2->ids; id1 != nil && id2 != nil; id1 = id1->next, id2 = 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 = nt->ids, idt = t1->ids; idt != id1; id = id->next, idt = idt->next)
						;
				}
				id->ty = t;
			}
			if(id != nil)
				id = id->next;
		}
		if(nt == nil)
			return t1;
		return nt;
	case Tadtpick:
		if(tequal(t1, t2))
			return t1;
		return t1->decl->dot->ty;
	}
}

Type*
tparent(Type *t1, Type *t2)
{
	if(tparent0(t1, t2))
		return tparent1(t1, t2);
	return t1;
}

/*
 * make the tuple type used to initialize an exception type
 */
Type*
mkexbasetype(Type *t)
{
	Decl *id, *new, *last;
	Type *nt;

	if(!t->cons)
		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 = 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 = allocmem(sizeof *id);
		*new = *id;
		new->cyc = 0;
		last->next = new;
		last = new;
	}
	last->next = nil;
	return usetype(nt);
}

/*
 * make an instantiated exception type
 */
Type*
mkextype(Type *t)
{
	Type *nt;

	if(!t->cons)
		fatal("mkextype on non-constant");
	if(t->tof != nil)
		return t->tof;
	nt = copytypeids(t);
	nt->cons = 0;
	t->tof = usetype(nt);
	return t->tof;
}

/*
 * convert an instantiated exception type to its underlying type
 */
Type*
mkextuptype(Type *t)
{
	Decl *id;
	Type *nt;

	if(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 = 0;
		nt->kind = Ttuple;
	}
	t->tof = usetype(nt);
	return t->tof;
}

static void
ckfix(Type *t, double max)
{
	int p;
	vlong k, x;
	double s;

	s = t->val->rval;
	if(max == 0.0)
		k = ((vlong)1<<32)-1;
	else
		k = 2*(vlong)(max/s+0.5)+1;
	x = 1;
	for(p = 0; k > x; p++)
		x *= 2;
	if(p == 0 || p > 32){
		error(t->src.start, "cannot fit fixed type into an int");
		return;
	}
	if(p < 32)
		t->val->rval /= (double)(1<<(32-p));
}

double
scale(Type *t)
{
	Node *n;

	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->rval;
}

double
scale2(Type *f, Type *t)
{
	return scale(f)/scale(t);
}

#define I(x)	((int)(x))
#define V(x)	((Long)(x))
#define D(x)	((double)(x))

/* put x in normal form */
static int
nf(double x, int *mant)
{
	int p;
	double m;

	p = 0;
	m = x;
	while(m >= 1){
		p++;
		m /= 2;
	}
	while(m < 0.5){
		p--;
		m *= 2;
	}
	m *= D(1<<16)*D(1<<15);
	if(m >= D(0x7fffffff) - 0.5){
		*mant = 0x7fffffff;
		return p;
	}
	*mant = I(m+0.5);
	return p;
}

static int
ispow2(double x)
{
	int m;

	nf(x, &m);
	if(m != 1<<30)
		return 0;
	return 1;
}

static int
fround(double x, int n, int *m)
{
	if(n != 31)
		fatal("not 31 in fround");
	return nf(x, m);
}

static int
fixmul2(double sx, double sy, double sr, int *rp, int *ra)
{
	int k, n, a;
	double alpha;

	alpha = (sx*sy)/sr;
	n = 31;
	k = fround(1/alpha, n, &a);
	*rp = 1-k;
	*ra = 0;
	return IMULX;
}

static int
fixdiv2(double sx, double sy, double sr, int *rp, int *ra)
{
	int k, n, b;
	double beta;

	beta = sx/(sy*sr);
	n = 31;
	k = fround(beta, n, &b);
	*rp = k-1;
	*ra = 0;
	return IDIVX;
}

static int
fixmul(double sx, double sy, double sr, int *rp, int *ra)
{
	int k, m, n, a, v;
	vlong W;
	double alpha, eps;

	alpha = (sx*sy)/sr;
	if(ispow2(alpha))
		return fixmul2(sx, sy, sr, rp, ra);
	n = 31;
	k = fround(1/alpha, n, &a);
	m = n-k;
	if(m < -n-1)
		return IMOVW;	/* result is zero whatever the values */
	v = 0;
	W = 0;
	eps = D(1<<m)/(alpha*D(a)) - 1;
	if(eps < 0){
		v = a-1;
		eps = -eps;
	}
	if(m < 0 && D(1<<n)*eps*D(a) >= D(a)-1+D(1<<m))
		W = (V(1)<<(-m)) - 1;
	if(v != 0 || W != 0)
		m = m<<2|(v != 0)<<1|(W != 0);
	*rp = m;
	*ra = a;
	return v == 0 && W == 0 ? IMULX0: IMULX1;
}

static int
fixdiv(double sx, double sy, double sr, int *rp, int *ra)
{
	int k, m, n, b, v;
	vlong W;
	double beta, eps;

	beta = sx/(sy*sr);
	if(ispow2(beta))
		return fixdiv2(sx, sy, sr, rp, ra);
	n = 31;
	k = fround(beta, n, &b);
	m = k-n;
	if(m <= -2*n)
		return IMOVW;	/* result is zero whatever the values */
	v = 0;
	W = 0;
	eps = (D(1<<m)*D(b))/beta - 1;
	if(eps < 0)
		v = 1;
	if(m < 0)
		W = (V(1)<<(-m)) - 1;
	if(v != 0 || W != 0)
		m = m<<2|(v != 0)<<1|(W != 0);
	*rp = m;
	*ra = b;
	return v == 0 && W == 0 ? IDIVX0: IDIVX1;
}

static int
fixcast(double sx, double sr, int *rp, int *ra)
{
	int op;

	op = fixmul(sx, 1.0, sr, rp, ra);
	return op-IMULX+ICVTXX;
}

int
fixop(int op, Type *tx, Type *ty, Type *tr, int *rp, int *ra)
{
	double sx, sy, sr;

	sx = scale(tx);
	sy = scale(ty);
	sr = scale(tr);
	if(op == IMULX)
		op = fixmul(sx, sy, sr, rp, ra);
	else if(op == IDIVX)
		op = fixdiv(sx, sy, sr, rp, ra);
	else
		op = fixcast(sx, sr, rp, ra);
	return op;
}

int
ispoly(Decl *d)
{
	Type *t;

	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;
}

int
ispolyadt(Type *t)
{
	return (t->kind == Tadt || t->kind == Tadtpick) && t->polys != nil && !(t->flags & INST);
}

Decl*
polydecl(Decl *ids)
{
	Decl *id;
	Type *t;

	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 */
Type*
exptotype(Node *n)
{
	Type *t, *tt;
	Decl *d;
	Typelist *tl;
	Src *src;

	if(n == nil)
		return nil;
	t = nil;
	switch(n->op){
		case Oname:
			if((d = n->decl) != nil && d->store == Dtype)
				t = d->ty;
			break;
		case Otype:
		case Ochan:
			t = n->ty;
			break;
		case Oref:
			t = exptotype(n->left);
			if(t != nil)
				t = mktype(&n->src.start, &n->src.stop, Tref, t, nil);
			break;
		case 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);
			break;
		case Omdot:
			t = exptotype(n->right);
			break;
		case Oindex:
			t = exptotype(n->left);
			if(t != nil){
				src = &n->src;
				tl = 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;
					tl = addtype(tt, tl);
					if(n->op != Oseq)
						break;
				}
				t = mkinsttype(src, t, tl);
			}
			break;
	}
	return t;
}

static char*
uname(Decl *im)
{
	Decl *p;
	int n;
	char *s;

	n = 0;
	for(p = im; p != nil; p = p->next)
		n += strlen(p->sym->name)+1;
	s = allocmem(n);
	strcpy(s, "");
	for(p = im; p != nil; p = p->next){
		strcat(s, p->sym->name);
		if(p->next != nil)
			strcat(s, "+");
	}
	return s;
}

/* check all implementation modules have consistent declarations 
 * and create their union if needed
 */
Decl*
modimp(Dlist *dl, Decl *im)
{
	Decl *u, *d, *dd, *ids, *dot, *last;
	Sym *s;
	Dlist *dl0;
	long sg, sg0;
	char buf[StrSize], *un;

	if(dl->next == nil)
		return dl->d;
	dl0 = dl;
	sg0 = 0;
	un = uname(im);
	seprint(buf, buf+sizeof(buf), ".m.%s", un);
	installids(Dglobal, mkids(&dl->d->src, enter(buf, 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;
	last = nil;
	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, "%s's module data not consistent with that of %s\n", d->sym->name, dl0->d->sym->name);
		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;
}

static void
modres(Decl *d)
{
	Decl *ids, *id, *n, *i;
	Type *t;

	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
 */	
void
modresolve(void)
{
	Dlist *dl;

	dl = impdecls;
	if(dl->next == nil)
		return;
	for( ; dl != nil; dl = dl->next)
		modres(dl->d);
	modres(impdecl);
}