#include "cc.h"

Node*
dodecl(void (*f)(int,Type*,Sym*), int c, Type *t, Node *n, int gen)
{
	Sym *s;
	Node *n1;
	long v;

	nearln = lineno;
	lastfield = 0;

loop:
	if(n != Z)
	switch(n->op) {
	default:
		diag(n, "unknown declarator: %O", n->op);
		break;

	case OARRAY:
		t = typ(TARRAY, t);
		t->width = 0;
		n1 = n->right;
		n = n->left;
		if(n1 != Z) {
			complex(n1);
			v = -1;
			if(n1->op == OCONST)
				v = n1->vconst;
			if(v <= 0) {
				diag(n, "array size must be a positive constant");
				v = 1;
			}
			t->width = v * t->link->width;
			t->nwidth = n1->left;
		}
		goto loop;

	case OIND:
		t = typ(TIND, t);
		t->garb = n->garb;
		n = n->left;
		goto loop;

	case OFUNC:
		t = typ(TFUNC, t);
		t->down = fnproto(n);
		n = n->left;
		goto loop;

	case OBIT:
		n1 = n->right;
		complex(n1);
		lastfield = -1;
		if(n1->op == OCONST)
			lastfield = n1->vconst;
		if(lastfield < 0) {
			diag(n, "field width must be non-negative constant");
			lastfield = 1;
		}
		if(lastfield == 0) {
			lastbit = 0;
			firstbit = 1;
			if(n->left != Z) {
				diag(n, "zero width named field");
				lastfield = 1;
			}
		}
		if(!typei[t->etype]) {
			diag(n, "field type must be int-like");
			t = types[TINT];
			lastfield = 1;
		}
		if(lastfield > tfield->width*8) {
			diag(n, "field width larger than field unit");
			lastfield = 1;
		}
		lastbit += lastfield;
		if(lastbit > tfield->width*8) {
			lastbit = lastfield;
			firstbit = 1;
		}
		n = n->left;
		goto loop;

	case ONAME:
		if(f == NODECL)
			break;
		s = n->sym;
		(*f)(c, t, s);
		if(s->class == CLOCAL)
			s = mkstatic(s);
		firstbit = 0;
		n->sym = s;
		n->type = s->type;
		acidvar(s);
		if(gen)
			vtgen(n);
		break;
	}
	lastdcl = t;
	return n;
}

Sym*
mkstatic(Sym *s)
{
	Sym *s1;

	if(s->class != CLOCAL)
		return s;
	snprint(symb, NSYMB, "%s$%d", s->name, s->block);
	s1 = lookup();
	if(s1->class != CSTATIC) {
		s1->type = s->type;
		s1->offset = s->offset;
		s1->block = s->block;
		s1->class = CSTATIC;
	}
	return s1;
}

/*
 * make a copy of a typedef
 * the problem is to split out incomplete
 * arrays so that it is in the variable
 * rather than the typedef.
 */
Type*
tcopy(Type *t)
{
	Type *tl, *tx;
	int et;

	if(t == T)
		return t;
	et = t->etype;
	if(typesu[et])
		return t;
	tl = tcopy(t->link);
	if(tl != t->link ||
	  (et == TARRAY && t->width == 0)) {
		tx = typ(TXXX, 0);
		*tx = *t;
		tx->link = tl;
		return tx;
	}
	return t;
}

Node*
doinit(Sym *s, Type *t, long o, Node *a)
{
	Node *n, *reta;

	if(t == T)
		return Z;
	if(s->class == CEXTERN)
		s->class = CGLOBL;
	if(0) {
		print("t = %T; o = %ld; n = %s\n", t, o, s->name);
		prtree(a, "doinit value");
	}

	n = initlist;
	if(a->op == OINIT)
		a = a->left;
	initlist = a;

	reta = a;
	init1(s, t, o, 0);
	if(initlist != Z)
		diag(initlist, "more initializers than structure: %s",
			s->name);
	initlist = n;

	return reta;
}

/*
 * get next major operator,
 * dont advance initlist.
 */
Node*
peekinit(void)
{
	Node *a;

	a = initlist;

loop:
	if(a == Z)
		return a;
	if(a->op == OLIST) {
		a = a->left;
		goto loop;
	}
	return a;
}

/*
 * consume and return next element on
 * initlist. expand strings.
 */
Node*
nextinit(void)
{
	Node *a, *n;

	a = initlist;
	n = Z;

	if(a == Z)
		return a;
	if(a->op == OLIST) {
		n = a->right;
		a = a->left;
	}
	initlist = n;
	return a;
}

int
isstruct(Node *a, Type *t)
{
	Node *n;

	switch(a->op) {
	case ODOTDOT:
		n = a->left;
		if(n && n->type && sametype(n->type, t))
			return 1;
	case OSTRING:
	case OLSTRING:
	case OCONST:
	case OINIT:
	case OELEM:
		return 0;
	}

	n = new(ODOTDOT, Z, Z);
	*n = *a;

	/*
	 * ODOTDOT is a flag for tcom
	 * a second tcom will not be performed
	 */
	a->op = ODOTDOT;
	a->left = n;
	a->right = Z;

	if(tcom(n))
		return 0;

	if(sametype(n->type, t))
		return 1;
	return 0;
}

void
init1(Sym *s, Type *t, long o, int exflag)
{
	Node *a, *r, nod;
	Type *t1;
	long e, w, so, mw;

	a = peekinit();
	if(a == Z)
		return;

	if(0) {
		print("t = %T; o = %ld; n = %s\n", t, o, s->name);
		prtree(a, "init1 value");
	}

	if(exflag && a->op == OINIT){
		doinit(s, t, o, nextinit());
		return;
	}

	switch(t->etype) {
	default:
		diag(Z, "unknown type in initialization: %T to: %s", t, s->name);
		return;

	case TCHAR:
	case TUCHAR:
	case TINT:
	case TUINT:
	case TSHORT:
	case TUSHORT:
	case TLONG:
	case TULONG:
	case TVLONG:
	case TUVLONG:
	case TFLOAT:
	case TDOUBLE:
	case TIND:
	single:
		if(a->op == OARRAY || a->op == OELEM)
			return;

		a = nextinit();
		if(a == Z)
			return;

		if(t->nbits)
			diag(Z, "cannot initialize bitfields");
		if(0 && s->class == CAUTO)
			return;

		complex(a);
		if(a->type == T)
			return;

		if(a->op == OCONST) {
			if(!sametype(a->type, t)) {
				/* hoop jumping to save malloc */
				if(nodcast == Z)
					nodcast = new(OCAST, Z, Z);
				nod = *nodcast;
				nod.left = a;
				nod.type = t;
				nod.lineno = a->lineno;
				complex(&nod);
				if(nod.type)
					*a = nod;
			}
			if(a->op != OCONST) {
/*
				diag(a, "initializer is not a constant: %s",
					s->name);
*/
				return;
			}
			if(vconst(a) == 0)
				return;
			return;
		}
		if(t->etype == TIND) {
			while(a->op == OCAST) {
				warn(a, "CAST in initialization ignored");
				a = a->left;
			}
			if(0 && !sametype(t, a->type)) {
				diag(a, "initialization of incompatible pointers: %s",
					s->name);
				print("%T and %T\n", t, a->type);
			}
/*
			if(a->op == OADDR)
				a = a->left;
*/
			return;
		}

		while(a->op == OCAST)
			a = a->left;
		if(a->op == OADDR) {
			warn(a, "initialize pointer to an integer: %s", s->name);
			/* a = a->left; */
			return;
		}
		/* diag(a, "initializer is not a constant: %s", s->name); */
		return;

	case TARRAY:
		w = t->link->width;
		if(a->op == OSTRING || a->op == OLSTRING)
		if(typei[t->link->etype]) {

			/*
			 * get rid of null if sizes match exactly
			 */
			a = nextinit();
			/* mw = t->width/w; */
			so = a->type->width/a->type->link->width;
			if(t->width <= 0)
				t->width = w*(so-1);
			USED(a);
			return;
		}

		mw = -w;
		for(e=0;;) {
			/*
			 * peek ahead for element initializer
			 */
			a = peekinit();
			if(a == Z)
				break;
			if(a->op == OELEM && t->link->etype != TSTRUCT)
				break;
			if(a->op == OARRAY) {
				if(e && exflag)
					break;
				a = nextinit();
				r = a->left;
				complex(r);
				if(r->op != OCONST) {
					diag(r, "initializer subscript must be constant");
					return;
				}
				e = r->vconst;
				if(t->width != 0)
					if(e < 0 || e*w >= t->width) {
						diag(a, "initialization index out of range: %ld", e);
						continue;
					}
			}

			so = e*w;
			if(so > mw)
				mw = so;
			if(t->width != 0)
				if(mw >= t->width)
					break;
			init1(s, t->link, o+so, 1);
			e++;
		}
		if(t->width == 0)
			t->width = mw+w;
		return;

	case TUNION:
	case TSTRUCT:
		/*
		 * peek ahead to find type of rhs.
		 * if its a structure, then treat
		 * this element as a variable
		 * rather than an aggregate.
		 */
		if(isstruct(a, t))
			goto single;

		if(t->width <= 0) {
			diag(Z, "incomplete structure: %s", s->name);
			return;
		}

	again:
		for(t1 = t->link; t1 != T; t1 = t1->down) {
			if(a->op == OARRAY && t1->etype != TARRAY)
				break;
			if(a->op == OELEM) {
				if(t1->sym != a->sym)
					continue;
				nextinit();
			}
			init1(s, t1, o+t1->offset, 1);
			a = peekinit();
			if(a == Z)
				break;
			if(a->op == OELEM)
				goto again;
		}
		if(a && a->op == OELEM)
			diag(a, "structure element not found %F", a);
		return;
	}
}

/*
Node*
newlist(Node *l, Node *r)
{
	if(r == Z)
		return l;
	if(l == Z)
		return r;
	return new(OLIST, l, r);
}
*/

void
suallign(Type *t)
{
	Type *l;
	long o, w;

	o = 0;
	switch(t->etype) {

	case TSTRUCT:
		t->offset = 0;
		w = 0;
		for(l = t->link; l != T; l = l->down) {
			if(l->nbits) {
				if(l->shift <= 0) {
					l->shift = -l->shift;
					w = round(w, tfield->width);
					o = w;
					w += tfield->width;
				}
				l->offset = o;
			} else {
				if(l->width <= 0)
					if(l->sym)
						diag(Z, "incomplete structure element: %s",
							l->sym->name);
					else
						diag(Z, "incomplete structure element");
				w = align(w, l, Ael1);
				l->offset = w;
				w = align(w, l, Ael2);
			}
		}
		w = align(w, t, Asu2);
		t->width = w;
		acidtype(t);
		ttgen(t);
		return;

	case TUNION:
		t->offset = 0;
		w = 0;
		for(l = t->link; l != T; l = l->down) {
			if(l->width <= 0)
				if(l->sym)
					diag(Z, "incomplete union element: %s",
						l->sym->name);
				else
					diag(Z, "incomplete union element");
			l->offset = 0;
			l->shift = 0;
			o = align(align(0, l, Ael1), l, Ael2);
			if(o > w)
				w = o;
		}
		w = align(w, t, Asu2);
		t->width = w;
		acidtype(t);
		ttgen(t);
		return;

	default:
		diag(Z, "unknown type in suallign: %T", t);
		break;
	}
}

long
round(long v, int w)
{
	int r;

	if(w <= 0 || w > 8) {
		diag(Z, "rounding by %d", w);
		w = 1;
	}
	r = v%w;
	if(r)
		v += w-r;
	return v;
}

Type*
ofnproto(Node *n)
{
	Type *tl, *tr, *t;

	if(n == Z)
		return T;
	switch(n->op) {
	case OLIST:
		tl = ofnproto(n->left);
		tr = ofnproto(n->right);
		if(tl == T)
			return tr;
		tl->down = tr;
		return tl;

	case ONAME:
		if(n->type == T)
			n->type = n->sym->type;
		t = typ(TXXX, T);
		*t = *n->sym->type;
		t->down = T;
		return t;
	}
	return T;
}

#define	ANSIPROTO	1
#define	OLDPROTO	2

void
argmark(Node *n, int pass)
{
	Type *t;

	autoffset = align(0, thisfn->link, Aarg0);
	for(; n->left != Z; n = n->left) {
		if(n->op != OFUNC || n->left->op != ONAME)
			continue;
		walkparam(n->right, pass);
		if(pass != 0 && anyproto(n->right) == OLDPROTO) {
			t = typ(TFUNC, n->left->sym->type->link);
			t->down = typ(TOLD, T);
			t->down->down = ofnproto(n->right);
			tmerge(t, n->left->sym);
			n->left->sym->type = t;
		}
		break;
	}
	autoffset = 0;
}

void
walkparam(Node *n, int pass)
{
	Sym *s;
	Node *n1;

	if(n != Z && n->op == OPROTO && n->left == Z && n->type == types[TVOID])
		return;

loop:
	if(n == Z)
		return;
	switch(n->op) {
	default:
		diag(n, "argument not a name/prototype: %O", n->op);
		break;

	case OLIST:
		walkparam(n->left, pass);
		n = n->right;
		goto loop;

	case OPROTO:
		for(n1 = n; n1 != Z; n1=n1->left)
			if(n1->op == ONAME) {
				if(pass == 0) {
					s = n1->sym;
					push1(s);
					s->offset = -1;
					break;
				}
				dodecl(pdecl, CPARAM, n->type, n->left, 1);
				break;
			}
		if(n1)
			break;
		if(pass == 0) {
			/*
			 * extension:
			 *	allow no name in argument declaration
			diag(Z, "no name in argument declaration");
			 */
			break;
		}
		dodecl(NODECL, CPARAM, n->type, n->left, 1);
		pdecl(CPARAM, lastdcl, S);
		break;

	case ODOTDOT:
		break;
	
	case ONAME:
		s = n->sym;
		if(pass == 0) {
			push1(s);
			s->offset = -1;
			break;
		}
		if(s->offset != -1) {
			autoffset = align(autoffset, s->type, Aarg1);
			s->offset = autoffset;
			autoffset = align(autoffset, s->type, Aarg2);
		} else
			dodecl(pdecl, CXXX, types[TINT], n, 1);
		break;
	}
}

void
markdcl(void)
{
	Decl *d;

	blockno++;
	d = push();
	d->val = DMARK;
	d->offset = autoffset;
	d->block = autobn;
	autobn = blockno;
}

Node*
revertdcl(void)
{
	Decl *d;
	Sym *s;

	for(;;) {
		d = dclstack;
		if(d == D) {
			diag(Z, "pop off dcl stack");
			break;
		}
		dclstack = d->link;
		s = d->sym;
		switch(d->val) {
		case DMARK:
			autoffset = d->offset;
			autobn = d->block;
			free(d);
			return Z;

		case DAUTO:
			if(0) {
				if(s->class == CAUTO)
					warn(Z, "auto declared and not used: %s", s->name);
				if(s->class == CPARAM)
					warn(Z, "param declared and not used: %s", s->name);
			}
			s->type = d->type;
			s->class = d->class;
			s->offset = d->offset;
			s->block = d->block;
			s->lineno = d->lineno;
			break;

		case DSUE:
			s->suetag = d->type;
			s->sueblock = d->block;
			s->lineno = d->lineno;
			break;

		case DLABEL:
			if(0 && s->label)
				warn(s->label, "label declared and not used \"%s\"", s->name);
			s->label = Z;
			s->lineno = d->lineno;
			break;
		}
		free(d);
	}
	return Z;
}

Type*
fnproto(Node *n)
{
	int r;

	r = anyproto(n->right);
	if(r == 0 || (r & OLDPROTO)) {
		if(r & ANSIPROTO)
			diag(n, "mixed ansi/old function declaration: %F", n->left);
		return T;
	}
	return fnproto1(n->right);
}

int
anyproto(Node *n)
{
	int r;

	r = 0;

loop:
	if(n == Z)
		return r;
	switch(n->op) {
	case OLIST:
		r |= anyproto(n->left);
		n = n->right;
		goto loop;

	case ODOTDOT:
	case OPROTO:
		return r | ANSIPROTO;
	}
	return r | OLDPROTO;
}

Type*
fnproto1(Node *n)
{
	Type *t;

	if(n == Z)
		return T;
	switch(n->op) {
	case OLIST:
		t = fnproto1(n->left);
		if(t != T)
			t->down = fnproto1(n->right);
		return t;

	case OPROTO:
		lastdcl = T;
		n = dodecl(NODECL, CXXX, n->type, n->left, 1);
		t = typ(TXXX, T);
		if(lastdcl != T)
			*t = *paramconv(lastdcl, 1);
		if(n != Z && n->op == ONAME)
			t->sym = n->sym;
		return t;

	case ONAME:
		diag(n, "incomplete argument prototype");
		return typ(TINT, T);

	case ODOTDOT:
		return typ(TDOT, T);
	}
	diag(n, "unknown op in fnproto");
	return T;
}

void
dbgdecl(Sym *s)
{

	print("decl \"%s\": C=%s [B=%d:O=%ld] T=%T\n",
		s->name, cnames[s->class], s->block, s->offset, s->type);
}

Decl*
push(void)
{
	static Decl zdecl;
	Decl *d;

	d = alloc(sizeof(*d));
	*d = zdecl;
	d->link = dclstack;
	dclstack = d;
	return d;
}

Decl*
push1(Sym *s)
{
	Decl *d;

	d = push();
	d->sym = s;
	d->val = DAUTO;
	d->type = s->type;
	d->class = s->class;
	d->offset = s->offset;
	d->block = s->block;
	d->lineno = s->lineno;
	return d;
}

int
sametype(Type *t1, Type *t2)
{

	if(t1 == t2)
		return 1;
	return rsametype(t1, t2, 5, 1);
}

int
rsametype(Type *t1, Type *t2, int n, int f)
{
	int et;

	n--;
	for(;;) {
		if(t1 == t2)
			return 1;
		if(t1 == T || t2 == T)
			return 0;
		if(n <= 0)
			return 1;
		et = t1->etype;
		if(et != t2->etype)
			return 0;
		if(et == TFUNC) {
			if(!rsametype(t1->link, t2->link, n, 0))
				return 0;
			t1 = t1->down;
			t2 = t2->down;
			while(t1 != T && t2 != T) {
				if(t1->etype == TOLD) {
					t1 = t1->down;
					continue;
				}
				if(t2->etype == TOLD) {
					t2 = t2->down;
					continue;
				}
				while(t1 != T || t2 != T) {
					if(!rsametype(t1, t2, n, 0))
						return 0;
					t1 = t1->down;
					t2 = t2->down;
				}
				break;
			}
			return 1;
		}
		if(et == TARRAY)
			if(t1->width != t2->width && t1->width != 0 && t2->width != 0)
				return 0;
		if(typesu[et] || et == TTUPLE) {
			if(t1->link == T)
				snap(t1);
			if(t2->link == T)
				snap(t2);
			t1 = t1->link;
			t2 = t2->link;
			for(;;) {
				if(t1 == t2)
					return 1;
				if(!rsametype(t1, t2, n, 0))
					return 0;
				t1 = t1->down;
				t2 = t2->down;
			}
		}
		t1 = t1->link;
		t2 = t2->link;
		if((f || 1) && et == TIND) {
			if(t1 != T && t1->etype == TVOID)
				return 1;
			if(t2 != T && t2->etype == TVOID)
				return 1;
		}
	}
	return 0;
}

ulong
signature(Type *t, int n)
{
	Type *t1;
	long s;

	s = 0;
	if(n > 0)
	for(; t; t=t->link) {
		s = s*thash1 + thash[t->etype];
		switch(t->etype) {
		default:
			return s;
		case TARRAY:
			s = s*thash2 + t->width;
			break;
		case TFUNC:
		case TSTRUCT:
		case TUNION:
			for(t1=t; t1; t1=t1->down)
				s = s*thash3 + signature(t1, n-1);
		case TIND:
			break;
		}
	}
	return s;
}

void
snap(Type *t)
{
	if(typesu[t->etype])
	if(t->link == T && t->tag && t->tag->suetag) {
		t->link = t->tag->suetag->link;
		t->width = t->tag->suetag->width;
	}
}

Type*
dotag(Sym *s, int et, int bn)
{
	Decl *d;

	if(bn != 0 && bn != s->sueblock) {
		d = push();
		d->sym = s;
		d->val = DSUE;
		d->type = s->suetag;
		d->block = s->sueblock;
		d->lineno = s->lineno;
		s->suetag = T;
	}
	if(s->suetag == T) {
		s->suetag = typ(et, T);
		s->sueblock = autobn;
	}
	if(s->suetag->etype != et)
		diag(Z, "tag used for more than one type: %s",
			s->name);
	if(s->suetag->tag == S)
		s->suetag->tag = s;
	return s->suetag;
}

Node*
dcllabel(Sym *s, int f)
{
	Decl *d, d1;
	Node *n;

	n = s->label;
	if(n != Z) {
		if(f) {
			if(0)
				diag(Z, "label reused: %s", s->name);
		}
		return n;
	}

	d = push();
	d->sym = s;
	d->val = DLABEL;
	d->lineno = s->lineno;
	dclstack = d->link;

	d1 = *firstdcl;
	*firstdcl = *d;
	*d = d1;

	firstdcl->link = d;
	firstdcl = d;

	n = new(OXXX, Z, Z);
	n->sym = s;
	s->label = n;
	return n;
}

Type*
paramconv(Type *t, int f)
{
	f = 1;
	switch(t->etype) {
	case TUNION:
	case TSTRUCT:
		if(t->width <= 0)
			diag(Z, "incomplete structure: %s", t->tag->name);
		break;

	case TARRAY:
		t = typ(TIND, t->link);
		t->width = types[TIND]->width;
		break;

	case TFUNC:
		t = typ(TIND, t);
		t->width = types[TIND]->width;
		break;

	case TFLOAT:
		if(!f)
			t = types[TDOUBLE];
		break;

	case TCHAR:
	case TSHORT:
		if(!f)
			t = types[TINT];
		break;

	case TUCHAR:
	case TUSHORT:
		if(!f)
			t = types[TUINT];
		break;
	}
	return t;
}

void
adecl(int c, Type *t, Sym *s)
{

	if(c == CSTATIC)
		c = CLOCAL;
	if(t->etype == TFUNC) {
		if(c == CXXX)
			c = CEXTERN;
		if(c == CLOCAL)
			c = CSTATIC;
		if(c == CAUTO || c == CEXREG)
			diag(Z, "function cannot be %s %s", cnames[c], s->name);
	}
	if(c == CXXX)
		c = CAUTO;
	if(s) {
		if(s->class == CSTATIC)
			if(c == CEXTERN || c == CGLOBL) {
				warn(Z, "just say static: %s", s->name);
				c = CSTATIC;
			}
		if(s->class == CAUTO || s->class == CPARAM || s->class == CLOCAL)
		if(s->block == autobn)
			diag(Z, "auto redeclaration of: %s", s->name);
		if(c != CPARAM)
			push1(s);
		s->block = autobn;
		s->offset = 0;
		s->type = t;
		s->class = c;
	}
	switch(c) {
	case CAUTO:
		autoffset = align(autoffset, t, Aaut3);
		s->offset = -autoffset;
		break;

	case CPARAM:
		autoffset = align(autoffset, t, Aarg1);
		if(s)
			s->offset = autoffset;
		autoffset = align(autoffset, t, Aarg2);
		break;
	}
	if(s)
		s->lineno = lineno;
}

void
pdecl(int c, Type *t, Sym *s)
{
	if(s && s->offset != -1) {
		diag(Z, "not a parameter: %s", s->name);
		return;
	}
	t = paramconv(t, c==CPARAM);
	if(c == CXXX)
		c = CPARAM;
	if(c != CPARAM) {
		diag(Z, "parameter cannot have class: %s", s->name);
		c = CPARAM;
	}
	adecl(c, t, s);
	if(s)
		s->lineno = lineno;
}

void
xdecl(int c, Type *t, Sym *s)
{
	long o;

	o = 0;
	if(c == CEXREG)
		c = CEXTERN;
	if(c == CXXX) {
		c = CGLOBL;
		if(s->class == CEXTERN)
			s->class = c;
	}
	if(c == CEXTERN)
		if(s->class == CGLOBL)
			c = CGLOBL;
	if(c == CAUTO) {
		diag(Z, "overspecified class: %s %s %s", s->name, cnames[c], cnames[s->class]);
		c = CEXTERN;
	}
	if(s->class == CSTATIC)
		if(c == CEXTERN || c == CGLOBL) {
			warn(Z, "overspecified class: %s %s %s", s->name, cnames[c], cnames[s->class]);
			c = CSTATIC;
		}
	if(s->type != T)
		if(s->class != c || !sametype(t, s->type) || t->etype == TENUM) {
			diag(Z, "external redeclaration of: %s", s->name);
			print("	%s %T; %s %T\n", cnames[c], t, cnames[s->class], s->type);
		}
	tmerge(t, s);
	s->type = t;
	s->class = c;
	s->block = 0;
	s->offset = o;
}

void
tmerge(Type *t1, Sym *s)
{
	Type *ta, *tb, *t2;

	t2 = s->type;
/*print("merge	%T; %T\n", t1, t2);/**/
	for(;;) {
		if(t1 == T || t2 == T || t1 == t2)
			break;
		if(t1->etype != t2->etype)
			break;
		switch(t1->etype) {
		case TFUNC:
			ta = t1->down;
			tb = t2->down;
			if(ta == T) {
				t1->down = tb;
				break;
			}
			if(tb == T)
				break;
			while(ta != T && tb != T) {
				if(ta == tb)
					break;
				/* ignore old-style flag */
				if(ta->etype == TOLD) {
					ta = ta->down;
					continue;
				}
				if(tb->etype == TOLD) {
					tb = tb->down;
					continue;
				}
				/* checking terminated by ... */
				if(ta->etype == TDOT && tb->etype == TDOT) {
					ta = T;
					tb = T;
					break;
				}
				if(!sametype(ta, tb))
					break;
				ta = ta->down;
				tb = tb->down;
			}
			if(ta != tb)
				diag(Z, "function inconsistently declared: %s", s->name);

			/* take new-style over old-style */
			ta = t1->down;
			tb = t2->down;
			if(ta != T && ta->etype == TOLD)
				if(tb != T && tb->etype != TOLD)
					t1->down = tb;
			break;

		case TARRAY:
			/* should we check array size change? */
			if(t2->width > t1->width)
				t1->width = t2->width;
			break;

		case TUNION:
		case TSTRUCT:
			return;
		}
		t1 = t1->link;
		t2 = t2->link;
	}
}

void
edecl(int c, Type *t, Sym *s)
{
	long l;
	Type *t1;

	if(s == S) {
		if(!typesu[t->etype])
			diag(Z, "unnamed structure element must be struct/union");
		if(c != CXXX)
			diag(Z, "unnamed structure element cannot have class");
	} else
		if(c != CXXX)
			diag(Z, "structure element cannot have class: %s", s->name);
	t1 = t;
	t = typ(TXXX, T);
	l = t->lineno;
	*t = *t1;
	t->lineno = l;
	t->sym = s;
	t->down = T;
	if(lastfield) {
		t->shift = lastbit - lastfield;
		t->nbits = lastfield;
		if(firstbit)
			t->shift = -t->shift;
		if(typeu[t->etype])
			t->etype = tufield->etype;
		else
			t->etype = tfield->etype;
	}
	if(strf == T)
		strf = t;
	else
		strl->down = t;
	strl = t;
}

/*
 * this routine is very suspect.
 * ansi requires the enum type to
 * be represented as an 'int'
 * this means that 0x81234567
 * would be illegal. this routine
 * makes signed and unsigned go
 * to unsigned.
 */
Type*
maxtype(Type *t1, Type *t2)
{

	if(t1 == T)
		return t2;
	if(t2 == T)
		return t1;
	if(t1->etype > t2->etype)
		return t1;
	return t2;
}

void
doenum(Sym *s, Node *n)
{
	int k = KDEC;
	Node *nc;

	nc = Z;
	if(n) {
		k = n->kind;
		complex(n);
		if(n->op != OCONST && n->op != OSTRING && n->op != OLSTRING) {
			diag(n, "enum not a constant: %s", s->name);
			return;
		}
		nc = n->left;
		en.cenum = n->type;
		en.tenum = maxtype(en.cenum, en.tenum);

		if(!typefd[en.cenum->etype])
			en.lastenum = n->vconst;
		else
			en.floatenum = n->fconst;
	}
	if(dclstack)
		push1(s);
	xdecl(CXXX, types[TENUM], s);

	if(en.cenum == T) {
		en.tenum = types[TINT];
		en.cenum = types[TINT];
		en.lastenum = 0;
	}
	s->tenum = en.cenum;

	if(s->tenum->etype == TIND){		/* string */
		nc = n;
		s->tenum = n->type;
	}
	else if(!typefd[s->tenum->etype]) {
		s->vconst = convvtox(en.lastenum, s->tenum->etype);
		en.lastenum++;
		s->tenum = types[TINT];
	} else {
		s->fconst = en.floatenum;
		if(n)
			s->cstring = n->cstring;
		else
			s->cstring = nil;
		en.floatenum++;
		s->tenum = types[TDOUBLE];
	}
	s->nconst = nc;

	acidvar(s);
	s->kind = k;
	etgen(s);
}

void
symadjust(Sym *s, Node *n, long del)
{

	switch(n->op) {
	default:
		if(n->left)
			symadjust(s, n->left, del);
		if(n->right)
			symadjust(s, n->right, del);
		return;

	case ONAME:
		return;

	case OCONST:
	case OSTRING:
	case OLSTRING:
	case OINDREG:
	case OREGISTER:
		return;
	}
}
