/* exptree.c - expression tree routines for bcc */

/* Copyright (C) 1992 Bruce Evans */

#include "bcc.h"
#include "byteord.h"
#include "gencode.h"
#include "parse.h"
#include "reg.h"
#include "sc.h"
#include "scan.h"
#include "sizes.h"
#include "type.h"

#ifdef VERY_SMALL_MEMORY
#define ETREESIZE 300
#else
#define ETREESIZE 1200
#endif
#define errtype itype
#define redtype ctype
#define uredtype uctype

PRIVATE struct nodestruct etree[ETREESIZE];	/* expression tree */
PRIVATE struct nodestruct *ettop;

FORWARD void badlvalue P((struct nodestruct *nodeptr));
FORWARD void binconvert P((struct nodestruct *nodeptr));
FORWARD void castiright P((struct nodestruct *nodeptr));
FORWARD void etreefull P((void));
FORWARD void fixnode P((struct nodestruct *nodeptr));
FORWARD bool_pt isconst0 P((struct nodestruct *nodeptr));
FORWARD bool_pt isnodecharconst P((struct nodestruct *nodeptr));
FORWARD void needint P((struct nodestruct *nodeptr));
FORWARD void neednonstruct P((struct nodestruct *nodeptr));
FORWARD void needscalar P((struct nodestruct *nodeptr));
FORWARD void needspv P((struct nodestruct *nodeptr));
FORWARD struct typestruct *nodetype P((struct nodestruct *nodeptr));
FORWARD int redscalar P((struct nodestruct *nodeptr));
FORWARD struct nodestruct *unconvert P((struct nodestruct *nodeptr));

PRIVATE void badlvalue(nodeptr)
struct nodestruct *nodeptr;
{
    error("invalid lvalue");
    fixnode(nodeptr);
}

PRIVATE void binconvert(nodeptr)
register struct nodestruct *nodeptr;
{
    bool_t bothscalar;
    value_t divisor;
    scalar_t lscalar;
    register struct nodestruct *right;
    scalar_t rscalar;

    rscalar = (right = nodeptr->right)->nodetype->scalar;
    lscalar = nodeptr->left.nodeptr->nodetype->scalar;
    if ((bothscalar = lscalar | rscalar) & RSCALAR)
    {
	nodeptr->nodetype = dtype;
	if (!(lscalar & DOUBLE))
	    nodeptr->left.nodeptr = castnode(dtype, nodeptr->left.nodeptr);
	if (!(rscalar & DOUBLE))
	    nodeptr->right = castnode(dtype, right);
    }
    else if (!(bothscalar & DLONG) && ((nodeptr->tag == ANDOP &&
	     (redscalar(nodeptr->left.nodeptr) | redscalar(right)) & CHAR) ||
	     ((nodeptr->tag == EOROP || nodeptr->tag == OROP) &&
	     redscalar(nodeptr->left.nodeptr) & redscalar(right) & CHAR) ||
	     (nodeptr->tag == MODOP && right->tag == LEAF &&
	     right->left.symptr->storage == CONSTANT &&
	     (divisor = right->left.symptr->offset.offv,
	      (uvalue_t) divisor <= MAXUCHTO + 1) &&
	     bitcount((uvalue_t) divisor) <= 1)))
    {
	/* result fits in char and extends correctly */
	if (bothscalar & UNSIGNED)
	    nodeptr->nodetype = uredtype;
	else
	    nodeptr->nodetype = redtype;
    }
    else if (bothscalar & LONG)
    {
	nodeptr->nodetype = ltype;
	if (bothscalar & UNSIGNED)
	    nodeptr->nodetype = ultype;
	if (bothscalar & (DLONG | SHORT | INT))
	{
	    /* XXX - otherwise both are long, or one is long with the same
	     * size as int and the other is char, and we want the lower level
	     * routines to handle the char.
	     */
	    if (!(lscalar & LONG))
		nodeptr->left.nodeptr = castnode(nodeptr->nodetype,
						 nodeptr->left.nodeptr);
	    if (!(rscalar & LONG))
		nodeptr->right = castnode(nodeptr->nodetype, right);
	}
    }
#ifdef I80386
    else if (i386_32 && bothscalar & SHORT)
    {
	nodeptr->nodetype = itype;
	if (bothscalar & UNSIGNED)
	    nodeptr->nodetype = uitype;
	if (lscalar & SHORT)
	    nodeptr->left.nodeptr = castnode(nodeptr->nodetype,
					     nodeptr->left.nodeptr);
	if (rscalar & SHORT)
	    nodeptr->right = castnode(nodeptr->nodetype, right);
    }
#endif
    else if (bothscalar & UNSIGNED)
	nodeptr->nodetype = uitype;
    else
	nodeptr->nodetype = itype;
}

PRIVATE void castiright(nodeptr)
struct nodestruct *nodeptr;
{
    nodeptr->right = castnode(itype, nodeptr->right);
}

PUBLIC struct nodestruct *castnode(type, nodeptr)
struct typestruct *type;
struct nodestruct *nodeptr;
{
    struct symstruct *symptr;

    (symptr = constsym((value_t) 0))->type = type;
    return node(CASTOP, nodeptr, leafnode(symptr));
}

PRIVATE void etreefull()
{
#if ETREESIZE == 1200
    limiterror("expression too complex (1201 nodes)");
#else
    limiterror("expression too complex (ETREESIZE)");
#endif
}

PUBLIC void etreeinit()
{
    ettop = (etptr = etree) + ETREESIZE;
}

PRIVATE void fixnode(nodeptr)
register struct nodestruct *nodeptr;
{
    nodeptr->tag = LEAF;
    nodeptr->flags = nodeptr->weight = 0;
    nodeptr->left.symptr = constsym((value_t) 0);  /* this has type errtype */
    nodeptr->nodetype = errtype;
}

PRIVATE bool_pt isconst0(nodeptr)
register struct nodestruct *nodeptr;
{
    register struct symstruct *symptr;

    return nodeptr->tag == LEAF &&
	   (symptr = nodeptr->left.symptr)->storage == CONSTANT &&
	   symptr->offset.offv == 0 &&
	   symptr->type->scalar & ISCALAR;
}

PRIVATE bool_pt isnodecharconst(nodeptr)
register struct nodestruct *nodeptr;
{
    register struct symstruct *symptr;

    if (nodeptr->tag == LEAF &&
	(symptr = nodeptr->left.symptr)->storage == CONSTANT &&
	ischarconst(symptr->offset.offv) &&
	symptr->type->scalar & ISCALAR)
	return TRUE;
    return FALSE;
}

PUBLIC struct nodestruct *leafnode(source)
struct symstruct *source;
{
    register struct nodestruct *leafptr;

    if ((leafptr = etptr++) >= ettop)
	etreefull();
    leafptr->tag = LEAF;
    leafptr->flags = leafptr->weight = 0;
    leafptr->nodetype = source->type;
    leafptr->left.symptr = source;
    leafptr->right = NULL;
    return leafptr;
}

PRIVATE void needint(nodeptr)
struct nodestruct *nodeptr;
{
    if (!(nodeptr->nodetype->scalar & ISCALAR))
    {
	error("need integral type");
	fixnode(nodeptr);
    }
}

PRIVATE void neednonstruct(nodeptr)
struct nodestruct *nodeptr;
{
    if (nodeptr->nodetype->constructor & STRUCTU)
    {
	error("need non-structure type");
	fixnode(nodeptr);
    }
}

PRIVATE void needscalar(nodeptr)
struct nodestruct *nodeptr;
{
    if (!nodeptr->nodetype->scalar)
    {
	error("need scalar");
	fixnode(nodeptr);
    }
}

PRIVATE void needspv(nodeptr)
struct nodestruct *nodeptr;
{
    if (nodeptr->nodetype->constructor & (ARRAY | FUNCTION | STRUCTU))
    {
	error("need scalar or pointer or void");
	fixnode(nodeptr);
    }
}

PUBLIC struct nodestruct *node(t, p1, p2)
op_pt t;
struct nodestruct *p1;
struct nodestruct *p2;
{
#if MAXREGS != 1
    weight_t rightweight;
#endif
    double dsourceval = 0; /* for -Wall */
    double dtargval = 0 ; /* for -Wall */
    bool_t lflag;
    scalar_t lscalar;
    struct nodestruct *pswap;
    struct symstruct *source = NULL; /* for -Wall */
    value_t sourceval = 0 ;  /* for -Wall */
    struct symstruct *target;
    offset_T targszdelta;
    value_t targval = 0; /* for -Wall */
    scalar_t rscalar = 0; /* for -Wall */
    bool_t uflag;

    switch ((op_t) t)
    {
    case ADDABOP:
	if (p1->nodetype->constructor & (ARRAY | POINTER))
	    t = PTRADDABOP;
	break;
    case ADDOP:
	if (p2->nodetype->constructor & (ARRAY | POINTER))
	{
	    pswap = p1;
	    p1 = p2;
	    p2 = pswap;
	}
	if (p1->nodetype->constructor & (ARRAY | POINTER))
	    t = PTRADDOP;
	break;
    case CONDOP:
	/* Change structs to struct pointers so condop() doesn't have to know
	 * about structs.
	 */
	if (p2->nodetype->constructor & STRUCTU)
	{
	    p2 = node(COLONOP, node(ADDRESSOP, p2->left.nodeptr, NULLNODE),
		      node(ADDRESSOP, p2->right, NULLNODE));
	    return node(INDIRECTOP, node(CONDOP, p1, p2), NULLNODE);
	}
	break;
    case FUNCOP:
	if (p1->nodetype->constructor & (ARRAY | POINTER))
	    p1 = node(INDIRECTOP, p1, NULLNODE);
	break;
    case SUBABOP:
	if (p1->nodetype->constructor & (ARRAY | POINTER))
	{
	    t = PTRADDABOP;
	    p2 = node(NEGOP, p2, NULLNODE);
	    break;
	}
    case SUBOP:
#if 0
	/* This works but sometimes pessimizes the code by converting small
	 * constants into large negative constants.  It makes the most
	 * difference for longs by avoiding stack tangles.  longop could be
	 * more careful.  Only the case (regpair) - constant is naturally
	 * tangled.
	 */
	if (p2->tag == LEAF && p2->nodetype->scalar
	    && (source = p2->left.symptr)->storage == CONSTANT)
	{
	    if (source->type->scalar & RSCALAR)
		*source->offset.offd = -*source->offset.offd;
	    else
		source->offset.offv = -source->offset.offv;
	    return node((op_t) t == SUBOP ? ADDOP : ADDABOP, p1, p2);
	}
#endif
	if (p1->nodetype->constructor & (ARRAY | POINTER))
	{
	    if (p2->nodetype->nexttype == p1->nodetype->nexttype)
		t = PTRSUBOP;
	    else
	    {
		t = PTRADDOP;
		p2 = node(NEGOP, p2, NULLNODE);
	    }
	}
	break;
    }
    if (((op_t) t == PTRADDABOP || (op_t) t == PTRADDOP) &&
	p1->nodetype->nexttype->constructor & (FUNCTION | VOID))
	error("arithmetic on pointer to function or void");
    if (p1->tag != LEAF)
	goto node1;
    target = p1->left.symptr;
    switch ((op_t) t)
    {
    case ADDRESSOP:
	if (target->indcount == 0 && target->flags == REGVAR)
	    error("register variable addressed");
	else if (target->type->constructor & ARRAY)
	{
	    if (target->indcount != 0)
		bugerror("botched array indirection count");
	    target->type = pointype(target->type);
	}
	else if (!(p1->flags & LVALUE))
	    badlvalue(p1);
	else
	{
	    address(target);	/* change p1, no code generated */
	    if (target->indcount == 2)	/* XXX - MAXINDIRECT? */
		/* just became direct enough */
		p1->weight = 1;
	}
	p1->flags &= ~LVALUE;
	p1->nodetype = target->type;
	return p1;
    case CASTOP:
	needspv(p2);
	if (p1->nodetype == p2->nodetype)
	{
/* No ancient switch on low mem systems */
#ifndef VERY_SMALL_MEMORY
	    /* In ancient UNIX C, casts remain lvalues */
	    if (!ancient)
#endif
		p1->flags &= ~LVALUE;
	    return p1;
	}
	if ((rscalar = p2->nodetype->scalar) & ISCALAR)
	    neednonstruct(p1);
	else if (rscalar & RSCALAR)
	    needscalar(p1);
	else			/* POINTER */
	    neednonstruct(p1);	/* H & S say functions & arrays must match */
	if (((lscalar = p1->nodetype->scalar) | rscalar) & RSCALAR)
	{
	    if (target->storage != CONSTANT)
		goto node1;
	    if (lscalar & RSCALAR && !(rscalar & RSCALAR))
	    {
		double val;
		/* XXX: Gcc warns about ansi vs k&r problem with this */
		static double MAXULONG = (double)0xFFFFFFFFL +1;

		val = *target->offset.offd;
		if (val > maxlongto)
		    val -= MAXULONG;
		target->offset.offv = (value_t) val;
	    }
	    if (!(lscalar & RSCALAR) && rscalar & RSCALAR)
	    {
		value_t val;

		val = target->offset.offv;
		target->offset.offd = qmalloc(sizeof *target->offset.offd);
		if (lscalar & UNSIGNED)
		    *target->offset.offd = (uvalue_t) val;
		else
		    *target->offset.offd = val;
	    }
	}
	if (target->storage == CONSTANT)
	{
	    if (rscalar & CHAR)
	    {
		target->offset.offv &= CHMASKTO;
		if (p2->nodetype == sctype &&
			target->offset.offv&((~CHMASKTO)>>1))
		    target->offset.offv |= ~CHMASKTO;
	    }
	    else if (rscalar & SHORT)
	    {
		target->offset.offv &= shortmaskto;
		if (!(rscalar & UNSIGNED) && target->offset.offv > maxshortto)
		    target->offset.offv -= (maxushortto + 1);
	    }
	    else if (rscalar & INT)
	    {
		target->offset.offv &= intmaskto;
		if (!(rscalar & UNSIGNED) && target->offset.offv > maxintto)
		    target->offset.offv -= (maxuintto + 1);
	    }
	    else if (rscalar & FLOAT)
		*target->offset.offd = (float) *target->offset.offd;
	}
	else if ((targszdelta =
		  ((p1->nodetype->constructor & (ARRAY | POINTER)) ?
		   ptypesize : p1->nodetype->typesize) -
		  p2->nodetype->typesize) == 0)
	    ;
	else if (target->indcount == 1 && targszdelta > 0 &&
		 target->flags != REGVAR)
	{
#if DYNAMIC_LONG_ORDER
	    if (long_big_endian)
#endif
#if DYNAMIC_LONG_ORDER || LONG_BIG_ENDIAN
# if INT_BIG_ENDIAN
		target->offset.offi += targszdelta;
# else
	    {
		if (lscalar & DLONG)
		    target->offset.offi += itypesize;	/* discard msword */
	    }
# endif
#endif
#if DYNAMIC_LONG_ORDER
	    else
#endif
#if DYNAMIC_LONG_ORDER || LONG_BIG_ENDIAN == 0
# if INT_BIG_ENDIAN
	    {
		if (rscalar & CHAR)
		    target->offset.offi += ctypesize;
	    }
# else
	    ;
# endif
#endif
	}
	else
	    goto node1;
/* No ancient switch on low mem systems */
#ifndef VERY_SMALL_MEMORY
	/* In ancient UNIX C, casts remain lvalues */
	if (!ancient)
#endif
	    p1->flags &= ~LVALUE;
	p1->nodetype = target->type = p2->nodetype;
	return p1;
    case INDIRECTOP:
	if (!(target->type->constructor & (ARRAY | POINTER)))
	    error("illegal indirection");
	else
	{
	    indirec(target);
	    if (target->indcount == 3)	/* XXX - MAXINDIRECT + 1? */
		/* just became too indirect */
		p1->weight = 1;
	    p1->flags |= LVALUE;
	    p1->nodetype = target->type;
	}
	return p1;
    case STRUCELTOP:		/* p2 known leaf */
	if (target->indcount != 1)
	    break;		/* struc() would generate code */
	struc(p2->left.symptr, target);
	p1->flags |= LVALUE;
	p1->nodetype = target->type;
	return p1;
    }
    if (target->indcount != 0)
	goto node1;
    lscalar = target->type->scalar;
    if (target->storage == CONSTANT)
    {
	if (lscalar & RSCALAR)
	    dtargval = *target->offset.offd;
	else
	    targval = target->offset.offv;
	switch ((op_t) t)
	{
	case COMMAOP:
	    return p2;
	case CONDOP:
	    if ((lscalar & RSCALAR && dtargval != FALSE)
		|| (!(lscalar & RSCALAR) && targval != FALSE))
	    {
		p2->left.nodeptr->nodetype = p2->nodetype;
		return p2->left.nodeptr;
	    }
	    p2->right->nodetype = p2->nodetype;
	    return p2->right;
	case LOGANDOP:
	    if ((lscalar & RSCALAR && dtargval != FALSE)
		|| (!(lscalar & RSCALAR) && targval != FALSE))
		break;
	    p1->nodetype = target->type = itype;
	    return p1;
	case LOGOROP:
	    if ((lscalar & RSCALAR && dtargval == FALSE)
		|| (!(lscalar & RSCALAR) && targval == FALSE))
		break;
	    target->offset.offv = TRUE;
	    p1->nodetype = target->type = itype;
	    return p1;
	}
    }
    switch ((op_t) t)
    {
    case COLONOP:
    case FUNCOP:
    case LISTOP:
    case ROOTLISTOP:
	goto node1;
    case PTRADDOP:
	if (p2->tag == LEAF &&
	    (source = p2->left.symptr)->storage == CONSTANT &&
	    source->type->scalar & ISCALAR)
	{
	    indexadr(source, target);
	    p1->flags &= ~LVALUE;
	    p1->nodetype = target->type;
	    return p1;
	}
    }
    if (target->storage != CONSTANT ||
	!((lscalar & (ISCALAR | RSCALAR)) && (op_t) t != PTRSUBOP) ||
	(p2 != NULL &&
	(p2->tag != LEAF || (source = p2->left.symptr)->storage != CONSTANT ||
	 (!((rscalar = source->type->scalar) & (ISCALAR | RSCALAR))
	 && (op_t) t != PTRSUBOP))))
	goto node1;
    lflag = lscalar & LONG;
    uflag = lscalar & UNSIGNED;
    if (p2 != NULL)
    {
	if (rscalar & RSCALAR)
	    dsourceval = *source->offset.offd;
	else
	{
	    sourceval = source->offset.offv;
	    lflag |= rscalar & LONG;
	    uflag |= rscalar & UNSIGNED;
	}
    }
    if (lscalar & RSCALAR || (p2 != NULL && rscalar & RSCALAR))
    {
    if (!(lscalar & RSCALAR))
    {
	if (uflag)
	    dtargval = (uvalue_t) targval;
	else
	    dtargval = targval;
    }
    if (p2 != NULL && !(rscalar & RSCALAR))
    {
	if (rscalar & UNSIGNED)
	    dsourceval = (uvalue_t) sourceval;
	else
	    dsourceval = sourceval;
    }
    switch ((op_t) t)
    {
    case ADDOP:
	dtargval += dsourceval;
	break;
    case DIVOP:
	if (dsourceval == 0)
	{
	    error("divison by 0");
	    dsourceval = 1;
	}
	dtargval /= dsourceval;
	break;
    case EQOP:
	targval = dtargval == dsourceval;
	goto intconst;
    case GEOP:
	targval = dtargval >= dsourceval;
	goto intconst;
    case GTOP:
	targval = dtargval > dsourceval;
	goto intconst;
    case LEOP:
	targval = dtargval <= dsourceval;
	goto intconst;
    case LOGANDOP:
	targval = dtargval && dsourceval;
	goto intconst;
    case LOGNOTOP:
	targval = !dtargval;
	goto intconst;
    case LOGOROP:
	targval = dtargval || dsourceval;
	goto intconst;
    case LTOP:
	targval = dtargval < dsourceval;
	goto intconst;
    case MULOP:
	dtargval *= dsourceval;
	break;
    case NEGOP:
	dtargval = -dtargval;
	break;
    case NEOP:
	targval = dtargval != dsourceval;
	goto intconst;
    case SUBOP:
	dtargval -= dsourceval;
	break;
    }
    /* XXX - leaks memory */
    target->offset.offd = qmalloc(sizeof *target->offset.offd);
    *target->offset.offd = dtargval;
    p1->nodetype = target->type = dtype;
    return p1;
    }
    switch ((op_t) t)
    {
    case ADDOP:
	targval += sourceval;
	break;
    case ANDOP:
	targval &= sourceval;
	break;
    case DIVOP:
	if (sourceval == 0)
	{
	    error("divison by 0");
	    sourceval = 1;
	}
	if (uflag)
	    targval = (uvalue_t) targval / sourceval;
	else
	    targval /= sourceval;
	break;
    case EOROP:
	targval ^= sourceval;
	break;
    case EQOP:
	targval = targval == sourceval;
	break;
    case GEOP:
	if (uflag)
	    targval = (uvalue_t) targval >= sourceval;
	else
	    targval = targval >= sourceval;
	goto intconst;
    case GTOP:
	if (uflag)
	    targval = (uvalue_t) targval > sourceval;
	else
	    targval = targval > sourceval;
	goto intconst;
    case LEOP:
	if (uflag)
	    targval = (uvalue_t) targval <= sourceval;
	else
	    targval = targval <= sourceval;
	goto intconst;
    case LOGANDOP:
	targval = targval && sourceval;
	goto intconst;
    case LOGNOTOP:
	targval = !targval;
	goto intconst;
    case LOGOROP:
	targval = targval || sourceval;
	goto intconst;
    case LTOP:
	if (uflag)
	    targval = (uvalue_t) targval < sourceval;
	else
	    targval = targval < sourceval;
	goto intconst;
	break;
    case MODOP:
	if (sourceval == 0)
	{
	    error("modulo by 0");
	    sourceval = 1;
	}
	if (uflag)
	    targval = (uvalue_t) targval % sourceval;
	else
	    targval %= sourceval;
	break;
    case MULOP:
	targval *= sourceval;
	break;
    case NEGOP:
	targval = -targval;
	break;
    case NEOP:
	targval = targval != sourceval;
	goto intconst;
    case NOTOP:
	targval = ~targval;
	break;
    case OROP:
	targval |= sourceval;
	break;
    case PTRSUBOP:
	if (targval < sourceval)
	    targval = -((uvalue_t) (sourceval - targval) /
			target->type->nexttype->typesize);
	else
	    targval = (uvalue_t) (targval - sourceval) /
			target->type->nexttype->typesize;
	uflag = FALSE;
	break;
    case SLOP:
	targval <<= sourceval;	/* assume shiftcount treated as unsigned */
	lflag = lscalar & LONG;
	uflag = lscalar & UNSIGNED;
	break;
    case SROP:
	if (uflag)
	    targval = (uvalue_t) targval >> sourceval;
	else
	    targval >>= sourceval;
	lflag = lscalar & LONG;
	uflag = lscalar & UNSIGNED;
	break;
    case SUBOP:
	targval -= sourceval;
	break;
    }
    if (lflag)
    {
	target->type = ltype;
	if (uflag)
	    target->type = ultype;
    }
    else
    {
	targval &= intmaskto;
	if (uflag)
	    target->type = uitype;
	else
	{
	    target->type = itype;
	    if (targval > maxintto)
		targval -= (maxuintto + 1);
	}
    }
    target->offset.offv = targval;
    p1->nodetype = target->type;
    return p1;

intconst:
    target->offset.offv = targval;
    p1->nodetype = target->type = itype;
    return p1;

node1:
    {
	register struct nodestruct *nodeptr;
	register struct nodestruct *regp2;

	if ((nodeptr = etptr++) >= ettop)
	    etreefull();
	regp2 = p2;
	nodeptr->tag = t;
	nodeptr->left.nodeptr = p1;
	nodeptr->right = regp2;
	if ((op_t) t == FUNCOP)
	    nodeptr->weight = MAXREGS + 1;
	else
	{
#if MAXREGS == 1
	    if ((nodeptr->weight = p1->weight) <= MAXREGS &&
		(regp2 == NULL ||
		 (nodeptr->weight = regp2->weight) < MAXREGS))
		nodeptr->weight = MAXREGS;
#else
	    nodeptr->weight = p1->weight;
	    if (regp2 == NULL)
		rightweight = 1;	/* could do POST-ops with 0 */
	    else		/* 0 more approp for LIST-ops but 1 OK */
		rightweight = regp2->weight;
	    if (nodeptr->weight < rightweight)
		nodeptr->weight = rightweight;
	    else if (nodeptr->weight == rightweight &&
		     nodeptr->weight < MAXREGS)
		++nodeptr->weight;
#endif
	}
	nodeptr->nodetype = nodetype(nodeptr);
	return nodeptr;
    }
}

PRIVATE struct typestruct *nodetype(nodeptr)
struct nodestruct *nodeptr;
{
    scalar_t bothscalar;
    scalar_t lscalar;
    scalar_t rscalar;
    struct nodestruct *left;
    struct nodestruct *right;
    struct typestruct *targtype;

    if (nodeptr->tag == LEAF)
	return nodeptr->left.symptr->type;
    targtype = (left = nodeptr->left.nodeptr)->nodetype;
    right = nodeptr->right;
    switch (nodeptr->tag)
    {
    case FUNCOP:
	if (!(targtype->constructor & FUNCTION))
	{
	    error("call of non-function");
	    fixnode(nodeptr);
	    return errtype;
	}
	return targtype->nexttype;
    case INDIRECTOP:
	if (!(targtype->constructor & (ARRAY | POINTER)))
	{
	    error("illegal indirection");
	    fixnode(nodeptr);
	    return errtype;
	}
	nodeptr->flags |= LVALUE;
	return targtype->nexttype;
    case LISTOP:
    case ROOTLISTOP:
	nodeptr->left.nodeptr = unconvert(left);
	return promote(targtype);
    case CASTOP:
	needspv(right);
    case COMMAOP:
    case CONDOP:
	return right->nodetype;

    case STRUCELTOP:
	nodeptr->flags |= LVALUE;
	return right->nodetype;

    case ADDRESSOP:
	if (!(left->flags & LVALUE))
	{
	    badlvalue(nodeptr);
	    return errtype;
	}
	return pointype(targtype);
    case LOGNOTOP:
	neednonstruct(left);
	return redtype;
    case NEGOP:
	needscalar(left);
	return promote(left->nodetype);
    case NOTOP:
	needint(left);
	return promote(left->nodetype);
    case PREDECOP:
    case PREINCOP:
    case POSTDECOP:
    case POSTINCOP:
	neednonstruct(left);
	if (!(left->flags & LVALUE))
	{
	    badlvalue(nodeptr);
	    return errtype;
	}
	break;

    case ANDOP:
    case EOROP:
    case MODOP:
    case OROP:
	needint(left);
	needint(right);
	/* fall through to redundant check and code to cast to same types */
    case ADDOP:
    case DIVOP:
    case MULOP:
    case SUBOP:
	needscalar(left);
	needscalar(right);
	binconvert(nodeptr);
	return nodeptr->nodetype;

    case PTRADDABOP:
	if (!(left->flags & LVALUE))
	{
	    badlvalue(nodeptr);
	    return errtype;
	}
    case PTRADDOP:		/* right type conversion to same size as ptr */
	needint(right);
	if (right->nodetype->scalar & LONG)
	    castiright(nodeptr);
	break;
    case PTRSUBOP:		/* already checked */
	return itype;
    case SLOP:			/* right type conversion always to int */
    case SROP:			/* ulong strictly to unsigned but no matter */
	needint(left);
	needint(right);
	if (right->nodetype->scalar & LONG)
	    castiright(nodeptr);
	return promote(left->nodetype);

    case LTOP:
    case LEOP:
    case GTOP:
    case GEOP:
    case EQOP:
    case LOGOROP:
    case LOGANDOP:
    case NEOP:
	neednonstruct(left);
	neednonstruct(right);
	nodeptr->left.nodeptr = left = unconvert(left);
	nodeptr->right = right = unconvert(right);
#ifdef MAYBE_DO_LATER /* OK if pointers fit in uitype */
	if (left->nodetype->constructor & POINTER)
	    nodeptr->left.nodeptr = left = castnode(uitype, left);
	if (right->nodetype->constructor & POINTER)
	    nodeptr->right = right = castnode(uitype, right);
#endif
	binconvert(nodeptr);
	return redtype;

    case COLONOP:
	if (isnodecharconst(left) && isnodecharconst(right))
	{
	    targtype = (targtype->scalar | right->nodetype->scalar) & UNSIGNED
		       ? uctype : ctype;
	    left = castnode(targtype, left);
	    right = castnode(targtype, right);
	}
	nodeptr->left.nodeptr = left = unconvert(left);
	nodeptr->right = right = unconvert(right);
	targtype = left->nodetype;
	if (
#ifdef I80386
	      i386_32 ||
#endif
	      ( targtype->scalar & FLOAT ) == 0)
	    if (targtype == right->nodetype)
	        return targtype;  /* XXX - reduced from promote(targtype) */
	if ((lscalar = targtype->scalar) != 0 &&
	    (rscalar = right->nodetype->scalar) != 0)
	{
	    /* promote minimally without using binconvert for char/short */
	    if ((bothscalar = lscalar | rscalar) & (LONG | RSCALAR))
	    {
		binconvert(nodeptr);
		return nodeptr->nodetype;
	    }
	    if (bothscalar & INT || (lscalar ^ rscalar) & UNSIGNED)
		targtype = itype;
	    else if (bothscalar & SHORT)
		targtype = stype;
	    else
		targtype = ctype;
	    if (bothscalar & UNSIGNED)
		targtype = tounsigned(targtype);
	    if ((!(bothscalar & INT) || (left->tag == LEAF && lscalar & INT)) ||
		(right->tag == LEAF && rscalar & INT))
	    {
		/* this is correct even when the if fails (the casts have to */
		/* be considered at a lower level) but gives worse code */
		if (left->nodetype != targtype)
		    nodeptr->left.nodeptr = castnode(targtype, left);
		if (right->nodetype != targtype)
		    nodeptr->right = castnode(targtype, right);
	    }
	    return targtype;	/* XXX - reduced from promote(targtype) */
	}
	if (targtype->constructor & POINTER &&
	    (isconst0(right) || (right->nodetype->constructor & POINTER &&
	     right->nodetype->nexttype->constructor & VOID)))
	    return targtype;
	if (right->nodetype->constructor & POINTER &&
	    (isconst0(left) || (targtype->constructor & POINTER &&
	     targtype->nexttype->constructor & VOID)))
	    return right->nodetype;
	error("mismatched types");
	fixnode(left);		/* XXX - better to fix the CONDOP */
	fixnode(right);
	return errtype;

    case ASSIGNOP:
	if (right->nodetype->constructor & VOID)
	{
	    error("assignment of void");
	    fixnode(right);
	}
	if (targtype->constructor & (ARRAY | FUNCTION | VOID))
	{
	    error("assignment to array, function or void");
	    fixnode(nodeptr);
	    return errtype;
	}
	if ((targtype->constructor | right->nodetype->constructor)
	    & STRUCTU && targtype != right->nodetype)
	{
	    error("assignment to/from struct/union of a different type");
	    fixnode(nodeptr);
	    return errtype;
	}
	if (!(left->flags & LVALUE))
	{
	    badlvalue(nodeptr);
	    return errtype;
	}
#if 0
	/* XXX - this is done at the lowest levels, with too many chances for
	 * errors.
	 */
	if (targtype != right->nodetype)
	    nodeptr->right = castnode(left->nodetype, right);
#endif
	break;
    case ANDABOP:
    case EORABOP:
    case MODABOP:
    case ORABOP:
	needint(right);
	needint(left);
	if (!(left->flags & LVALUE))
	{
	    badlvalue(nodeptr);
	    return errtype;
	}
	/* fall through to redundant check and code to cast to same types */
    case ADDABOP:
    case SUBABOP:
    case DIVABOP:
    case MULABOP:
	needscalar(right);
	needscalar(left);
	if (!(left->flags & LVALUE))
	{
	    badlvalue(nodeptr);
	    return errtype;
	}
	rscalar = right->nodetype->scalar;
	if ((lscalar = (targtype = left->nodetype)->scalar) & RSCALAR)
	{
	    if (!(rscalar & DOUBLE))
		nodeptr->right = castnode(dtype, right);
	}
	else if (lscalar & LONG)
	{
	    if (!(rscalar & LONG))
		nodeptr->right = castnode(rscalar & UNSIGNED
					  ? ultype : targtype, right);
	}
#ifdef I80386
	else if (i386_32 && lscalar & INT)
	{
	    if (rscalar & SHORT)
		nodeptr->right = castnode(rscalar & UNSIGNED
					  ? uitype : targtype, right);
	}
#endif
	break;
    case SLABOP:
    case SRABOP:
	needint(right);
	needint(left);
	if (!(left->flags & LVALUE))
	{
	    badlvalue(nodeptr);
	    return errtype;
	}
	if (!(right->nodetype->scalar & INT))
	    castiright(nodeptr);
	break;
    }
    return left->nodetype;
}

PRIVATE int redscalar(nodeptr)
register struct nodestruct *nodeptr;
{
    if (isnodecharconst(nodeptr))
	return CHAR;
    return nodeptr->nodetype->scalar;
}

PRIVATE struct nodestruct *unconvert(nodeptr)
struct nodestruct *nodeptr;
{
    if (nodeptr->nodetype->constructor & (ARRAY | FUNCTION))
	return castnode(pointype(nodeptr->nodetype->constructor & ARRAY ?
				 nodeptr->nodetype->nexttype :
				 nodeptr->nodetype), nodeptr);
    return nodeptr;
}
