[BACK]Return to tree.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / usr.bin / xlint / lint1

File: [cvs.NetBSD.org] / src / usr.bin / xlint / lint1 / tree.c (download)

Revision 1.115, Sat Jan 2 03:49:25 2021 UTC (3 years, 3 months ago) by rillig
Branch: MAIN
Changes since 1.114: +56 -98 lines

lint: fix buffer truncation for type names

Previously, most type names had been cut off after 63 significant
characters.  In some cases, 127 characters survived, or 255.  And for
the debugging messages, sometimes even 1023.  This inconsistency was
useless.

It was wrong in the first place to make the caller of the function
tyname responsible for handling the buffer.  That's not something a
caller of such a simple function should do.  These callers have better
things to do.

The API of the new function type_name is as simple as possible.

In the implementation, the name of the type is generated anew each time.
I just didn't know whether the type details could change, once the type
is initialized, and I didn't want to find out.  To be on the safe side,
the resulting type name is cached, independently of the type it was
generated for.  Using a trivial, unbalanced binary tree should be good
enough for now.

All this work is necessary to support adding new debug logging, without
being distracted by irrelevant implementation details such as these
buffer sizes.  Adding new debug messages should be fun and easy; up to
now, it was overly bureaucratic.

/*	$NetBSD: tree.c,v 1.115 2021/01/02 03:49:25 rillig Exp $	*/

/*
 * Copyright (c) 1994, 1995 Jochen Pohl
 * All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by Jochen Pohl for
 *	The NetBSD Project.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#if HAVE_NBTOOL_CONFIG_H
#include "nbtool_config.h"
#endif

#include <sys/cdefs.h>
#if defined(__RCSID) && !defined(lint)
__RCSID("$NetBSD: tree.c,v 1.115 2021/01/02 03:49:25 rillig Exp $");
#endif

#include <float.h>
#include <limits.h>
#include <math.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>

#include "lint1.h"
#include "cgram.h"
#include "externs1.h"

static	tnode_t	*new_int_const_node(tspec_t, int64_t);
static	void	check_pointer_comparison(op_t, tnode_t *, tnode_t *);
static	int	check_assign_types_compatible(op_t, int, tnode_t *, tnode_t *);
static	void	check_bad_enum_operation(op_t, tnode_t *, tnode_t *);
static	void	check_enum_type_mismatch(op_t, int, tnode_t *, tnode_t *);
static	void	check_enum_int_mismatch(op_t, int, tnode_t *, tnode_t *);
static	tnode_t	*new_tnode(op_t, type_t *, tnode_t *, tnode_t *);
static	void	balance(op_t, tnode_t **, tnode_t **);
static	void	warn_incompatible_types(op_t, tspec_t, tspec_t);
static	void	warn_incompatible_pointers(mod_t *, type_t *, type_t *);
static	void	merge_qualifiers(type_t **, type_t *, type_t *);
static	int	has_constant_member(type_t *);
static	void	check_prototype_conversion(int, tspec_t, tspec_t, type_t *,
					   tnode_t *);
static	void	check_integer_conversion(op_t, int, tspec_t, tspec_t, type_t *,
					 tnode_t *);
static	void	check_pointer_integer_conversion(op_t, tspec_t, type_t *,
						 tnode_t *);
static	void	check_pointer_conversion(op_t, tnode_t *, type_t *);
static	tnode_t	*build_struct_access(op_t, tnode_t *, tnode_t *);
static	tnode_t	*build_prepost_incdec(op_t, tnode_t *);
static	tnode_t	*build_real_imag(op_t, tnode_t *);
static	tnode_t	*build_ampersand(tnode_t *, int);
static	tnode_t	*build_plus_minus(op_t, tnode_t *, tnode_t *);
static	tnode_t	*build_bit_shift(op_t, tnode_t *, tnode_t *);
static	tnode_t	*build_colon(tnode_t *, tnode_t *);
static	tnode_t	*build_assignment(op_t, tnode_t *, tnode_t *);
static	tnode_t	*plength(type_t *);
static	tnode_t	*fold(tnode_t *);
static	tnode_t	*fold_test(tnode_t *);
static	tnode_t	*fold_float(tnode_t *);
static	tnode_t	*check_function_arguments(type_t *, tnode_t *);
static	tnode_t	*check_prototype_argument(int, type_t *, tnode_t *);
static	void	check_null_effect(tnode_t *);
static	void	display_expression(tnode_t *, int);
static	void	check_array_index(tnode_t *, int);
static	void	check_integer_comparison(op_t, tnode_t *, tnode_t *);
static	void	check_precedence_confusion(tnode_t *);

extern sig_atomic_t fpe;

#if 0
static char *
dumpnode(char *buf, size_t len, tnode_t *tn) {
	const char *n = getopname(tn->tn_op);
	const char *s;

	switch (tn->tn_op) {
	case NAME:
		s = tn->tn_sym->s_name;
		break;
	case CON:
	case STRING:
		s = "*";	/* todo */
		break;
	default:
		s = NULL;
		break;
	}
	char lb[1024];
	char rb[1024];

	if (s == NULL && tn->tn_left != NULL)
		dumpnode(lb, sizeof(lb), tn->tn_left);
	else
		strcpy(lb, "(null)");

	if (s == NULL && tn->tn_right != NULL)
		dumpnode(rb, sizeof(rb), tn->tn_right);
	else
		strcpy(rb, "(null)");


	snprintf(buf, len, "%s: (%s) = %s [%s, %s]", n,
	    type_name(tn->tn_type), s, lb, rb);
	return buf;
}
#endif

/*
 * Increase degree of reference.
 * This is most often used to change type "T" in type "pointer to T".
 */
type_t *
incref(type_t *tp, tspec_t t)
{
	type_t	*tp2;

	tp2 = getblk(sizeof (type_t));
	tp2->t_tspec = t;
	tp2->t_subt = tp;
	return tp2;
}

/*
 * same for use in expressions
 */
type_t *
tincref(type_t *tp, tspec_t t)
{
	type_t	*tp2;

	tp2 = tgetblk(sizeof (type_t));
	tp2->t_tspec = t;
	tp2->t_subt = tp;
	return tp2;
}

/*
 * Create a node for a constant.
 */
tnode_t *
getcnode(type_t *tp, val_t *v)
{
	tnode_t	*n;

	n = getnode();
	n->tn_op = CON;
	n->tn_type = tp;
	n->tn_val = tgetblk(sizeof (val_t));
	n->tn_val->v_tspec = tp->t_tspec;
	n->tn_val->v_ansiu = v->v_ansiu;
	n->tn_val->v_u = v->v_u;
	free(v);
	return n;
}

/*
 * Create a node for a integer constant.
 */
static tnode_t *
new_int_const_node(tspec_t t, int64_t q)
{
	tnode_t	*n;

	n = getnode();
	n->tn_op = CON;
	n->tn_type = gettyp(t);
	n->tn_val = tgetblk(sizeof (val_t));
	n->tn_val->v_tspec = t;
	n->tn_val->v_quad = q;
	return n;
}

/*
 * Create a node for a name (symbol table entry).
 * ntok is the token which follows the name.
 */
tnode_t *
getnnode(sym_t *sym, int ntok)
{
	tnode_t	*n;

	if (sym->s_scl == NOSCL) {
		sym->s_scl = EXTERN;
		sym->s_def = DECL;
		if (ntok == T_LPAREN) {
			if (sflag) {
				/* function implicitly declared to ... */
				warning(215);
			}
			/*
			 * XXX if tflag is set the symbol should be
			 * exported to level 0
			 */
			sym->s_type = incref(sym->s_type, FUNC);
		} else {
			if (!blklev) {
				/* %s undefined */
				error(99, sym->s_name);
			} else {
				int fixtype;
				if (strcmp(sym->s_name, "__FUNCTION__") == 0 ||
				    strcmp(sym->s_name, "__PRETTY_FUNCTION__")
				    == 0) {
					/* __FUNCTION__/__PRETTY_FUNCTION... */
					gnuism(316);
					fixtype = 1;
				} else if (strcmp(sym->s_name, "__func__") == 0) {
					if (!Sflag)
						/* __func__ is a C9X feature */
						warning(317);
					fixtype = 1;
				} else {
					/* %s undefined */
					error(99, sym->s_name);
					fixtype = 0;
				}
				if (fixtype) {
					sym->s_type = incref(gettyp(CHAR), PTR);
					sym->s_type->t_const = 1;
				}
			}
		}
	}

	if (sym->s_kind != FVFT && sym->s_kind != FMEMBER)
		LERROR("getnnode(%d)", sym->s_kind);

	n = getnode();
	n->tn_type = sym->s_type;
	if (sym->s_scl != ENUMCON) {
		n->tn_op = NAME;
		n->tn_sym = sym;
		if (sym->s_kind == FVFT && sym->s_type->t_tspec != FUNC)
			n->tn_lvalue = 1;
	} else {
		n->tn_op = CON;
		n->tn_val = tgetblk(sizeof (val_t));
		*n->tn_val = sym->s_value;
	}

	return n;
}

/*
 * Create a node for a string.
 */
tnode_t *
getsnode(strg_t *strg)
{
	size_t	len;
	tnode_t	*n;

	len = strg->st_len;

	n = getnode();

	n->tn_op = STRING;
	n->tn_type = tincref(gettyp(strg->st_tspec), ARRAY);
	n->tn_type->t_dim = len + 1;
	n->tn_lvalue = 1;

	n->tn_string = tgetblk(sizeof (strg_t));
	n->tn_string->st_tspec = strg->st_tspec;
	n->tn_string->st_len = len;

	if (strg->st_tspec == CHAR) {
		n->tn_string->st_cp = tgetblk(len + 1);
		(void)memcpy(n->tn_string->st_cp, strg->st_cp, len + 1);
		free(strg->st_cp);
	} else {
		n->tn_string->st_wcp = tgetblk((len + 1) * sizeof (wchar_t));
		(void)memcpy(n->tn_string->st_wcp, strg->st_wcp,
			     (len + 1) * sizeof (wchar_t));
		free(strg->st_wcp);
	}
	free(strg);

	return n;
}

/*
 * Returns a symbol which has the same name as the msym argument and is a
 * member of the struct or union specified by the tn argument.
 */
sym_t *
struct_or_union_member(tnode_t *tn, op_t op, sym_t *msym)
{
	str_t	*str;
	type_t	*tp;
	sym_t	*sym, *csym;
	int	eq;
	tspec_t	t;

	/*
	 * Remove the member if it was unknown until now (Which means
	 * that no defined struct or union has a member with the same name).
	 */
	if (msym->s_scl == NOSCL) {
		/* undefined struct/union member: %s */
		error(101, msym->s_name);
		rmsym(msym);
		msym->s_kind = FMEMBER;
		msym->s_scl = MOS;
		msym->s_styp = tgetblk(sizeof (str_t));
		msym->s_styp->stag = tgetblk(sizeof (sym_t));
		msym->s_styp->stag->s_name = unnamed;
		msym->s_value.v_tspec = INT;
		return msym;
	}

	/* Set str to the tag of which msym is expected to be a member. */
	str = NULL;
	t = (tp = tn->tn_type)->t_tspec;
	if (op == POINT) {
		if (t == STRUCT || t == UNION)
			str = tp->t_str;
	} else if (op == ARROW && t == PTR) {
		t = (tp = tp->t_subt)->t_tspec;
		if (t == STRUCT || t == UNION)
			str = tp->t_str;
	}

	/*
	 * If this struct/union has a member with the name of msym, return
	 * return this it.
	 */
	if (str != NULL) {
		for (sym = msym; sym != NULL; sym = sym->s_link) {
			if (sym->s_scl != MOS && sym->s_scl != MOU)
				continue;
			if (sym->s_styp != str)
				continue;
			if (strcmp(sym->s_name, msym->s_name) != 0)
				continue;
			return sym;
		}
	}

	/*
	 * Set eq to 0 if there are struct/union members with the same name
	 * and different types and/or offsets.
	 */
	eq = 1;
	for (csym = msym; csym != NULL; csym = csym->s_link) {
		if (csym->s_scl != MOS && csym->s_scl != MOU)
			continue;
		if (strcmp(msym->s_name, csym->s_name) != 0)
			continue;
		for (sym = csym->s_link ; sym != NULL; sym = sym->s_link) {
			int w;

			if (sym->s_scl != MOS && sym->s_scl != MOU)
				continue;
			if (strcmp(csym->s_name, sym->s_name) != 0)
				continue;
			if (csym->s_value.v_quad != sym->s_value.v_quad) {
				eq = 0;
				break;
			}
			w = 0;
			eq = eqtype(csym->s_type, sym->s_type, 0, 0, &w) && !w;
			if (!eq)
				break;
			if (csym->s_bitfield != sym->s_bitfield) {
				eq = 0;
				break;
			}
			if (csym->s_bitfield) {
				type_t	*tp1, *tp2;

				tp1 = csym->s_type;
				tp2 = sym->s_type;
				if (tp1->t_flen != tp2->t_flen) {
					eq = 0;
					break;
				}
				if (tp1->t_foffs != tp2->t_foffs) {
					eq = 0;
					break;
				}
			}
		}
		if (!eq)
			break;
	}

	/*
	 * Now handle the case in which the left operand refers really
	 * to a struct/union, but the right operand is not member of it.
	 */
	if (str != NULL) {
		if (eq && tflag) {
			/* illegal member use: %s */
			warning(102, msym->s_name);
		} else {
			/* illegal member use: %s */
			error(102, msym->s_name);
		}
		return msym;
	}

	/*
	 * Now the left operand of ARROW does not point to a struct/union
	 * or the left operand of POINT is no struct/union.
	 */
	if (eq) {
		if (op == POINT) {
			if (tflag) {
				/* left operand of '.' must be struct/... */
				warning(103);
			} else {
				/* left operand of '.' must be struct/... */
				error(103);
			}
		} else {
			/* left operand of "->" must be pointer to ... */
			if (tflag && tn->tn_type->t_tspec == PTR) {
				/* left operand of '->' must be pointer ... */
				warning(104, type_name(tn->tn_type));
			} else {
				/* left operand of '->' must be pointer ... */
				error(104, type_name(tn->tn_type));
			}
		}
	} else {
		if (tflag) {
			/* non-unique member requires struct/union %s */
			error(105, op == POINT ? "object" : "pointer");
		} else {
			/* unacceptable operand of '%s' */
			error(111, modtab[op].m_name);
		}
	}

	return msym;
}

/*
 * Create a tree node. Called for most operands except function calls,
 * sizeof and casts.
 *
 * op	operator
 * ln	left operand
 * rn	if not NULL, right operand
 */
tnode_t *
build(op_t op, tnode_t *ln, tnode_t *rn)
{
	mod_t	*mp;
	tnode_t	*ntn;
	type_t	*rtp;

	mp = &modtab[op];

	/* If there was an error in one of the operands, return. */
	if (ln == NULL || (mp->m_binary && rn == NULL))
		return NULL;

	/*
	 * Apply class conversions to the left operand, but only if its
	 * value is needed or it is compared with null.
	 */
	if (mp->m_vctx || mp->m_tctx)
		ln = cconv(ln);
	/*
	 * The right operand is almost always in a test or value context,
	 * except if it is a struct or union member.
	 */
	if (mp->m_binary && op != ARROW && op != POINT)
		rn = cconv(rn);

	/*
	 * Print some warnings for comparisons of unsigned values with
	 * constants lower than or equal to null. This must be done
	 * before promote() because otherwise unsigned char and unsigned
	 * short would be promoted to int. Also types are tested to be
	 * CHAR, which would also become int.
	 */
	if (mp->m_comp)
		check_integer_comparison(op, ln, rn);

	/*
	 * Promote the left operand if it is in a test or value context
	 */
	if (mp->m_vctx || mp->m_tctx)
		ln = promote(op, 0, ln);
	/*
	 * Promote the right operand, but only if it is no struct or
	 * union member, or if it is not to be assigned to the left operand
	 */
	if (mp->m_binary && op != ARROW && op != POINT &&
	    op != ASSIGN && op != RETURN) {
		rn = promote(op, 0, rn);
	}

	/*
	 * If the result of the operation is different for signed or
	 * unsigned operands and one of the operands is signed only in
	 * ANSI C, print a warning.
	 */
	if (mp->m_tlansiu && ln->tn_op == CON && ln->tn_val->v_ansiu) {
		/* ANSI C treats constant as unsigned, op %s */
		warning(218, mp->m_name);
		ln->tn_val->v_ansiu = 0;
	}
	if (mp->m_transiu && rn->tn_op == CON && rn->tn_val->v_ansiu) {
		/* ANSI C treats constant as unsigned, op %s */
		warning(218, mp->m_name);
		rn->tn_val->v_ansiu = 0;
	}

	/* Make sure both operands are of the same type */
	if (mp->m_balance || (tflag && (op == SHL || op == SHR)))
		balance(op, &ln, &rn);

	/*
	 * Check types for compatibility with the operation and mutual
	 * compatibility. Return if there are serious problems.
	 */
	if (!typeok(op, 0, ln, rn))
		return NULL;

	/* And now create the node. */
	switch (op) {
	case POINT:
	case ARROW:
		ntn = build_struct_access(op, ln, rn);
		break;
	case INCAFT:
	case DECAFT:
	case INCBEF:
	case DECBEF:
		ntn = build_prepost_incdec(op, ln);
		break;
	case AMPER:
		ntn = build_ampersand(ln, 0);
		break;
	case STAR:
		ntn = new_tnode(STAR, ln->tn_type->t_subt, ln, NULL);
		break;
	case PLUS:
	case MINUS:
		ntn = build_plus_minus(op, ln, rn);
		break;
	case SHL:
	case SHR:
		ntn = build_bit_shift(op, ln, rn);
		break;
	case COLON:
		ntn = build_colon(ln, rn);
		break;
	case ASSIGN:
	case MULASS:
	case DIVASS:
	case MODASS:
	case ADDASS:
	case SUBASS:
	case SHLASS:
	case SHRASS:
	case ANDASS:
	case XORASS:
	case ORASS:
	case RETURN:
		ntn = build_assignment(op, ln, rn);
		break;
	case COMMA:
	case QUEST:
		ntn = new_tnode(op, rn->tn_type, ln, rn);
		break;
	case REAL:
	case IMAG:
		ntn = build_real_imag(op, ln);
		break;
	default:
		rtp = mp->m_logical ? gettyp(INT) : ln->tn_type;
		lint_assert(mp->m_binary || rn == NULL);
		ntn = new_tnode(op, rtp, ln, rn);
		break;
	}

	/* Return if an error occurred. */
	if (ntn == NULL)
		return NULL;

	/* Print a warning if precedence confusion is possible */
	if (mp->m_tpconf)
		check_precedence_confusion(ntn);

	/*
	 * Print a warning if one of the operands is in a context where
	 * it is compared with null and if this operand is a constant.
	 */
	if (mp->m_tctx) {
		if (ln->tn_op == CON ||
		    ((mp->m_binary && op != QUEST) && rn->tn_op == CON)) {
			if (hflag && !constcond_flag)
				/* constant in conditional context */
				warning(161);
		}
	}

	/* Fold if the operator requires it */
	if (mp->m_fold) {
		if (ln->tn_op == CON && (!mp->m_binary || rn->tn_op == CON)) {
			if (mp->m_tctx) {
				ntn = fold_test(ntn);
			} else if (tspec_is_float(ntn->tn_type->t_tspec)) {
				ntn = fold_float(ntn);
			} else {
				ntn = fold(ntn);
			}
		} else if (op == QUEST && ln->tn_op == CON) {
			ntn = ln->tn_val->v_quad ? rn->tn_left : rn->tn_right;
		}
	}

	return ntn;
}

/*
 * Perform class conversions.
 *
 * Arrays of type T are converted into pointers to type T.
 * Functions are converted to pointers to functions.
 * Lvalues are converted to rvalues.
 */
tnode_t *
cconv(tnode_t *tn)
{
	type_t	*tp;

	/*
	 * Array-lvalue (array of type T) is converted into rvalue
	 * (pointer to type T)
	 */
	if (tn->tn_type->t_tspec == ARRAY) {
		if (!tn->tn_lvalue) {
			/* %soperand of '%s' must be lvalue */
			/* XXX print correct operator */
			(void)gnuism(114, "", modtab[AMPER].m_name);
		}
		tn = new_tnode(AMPER, tincref(tn->tn_type->t_subt, PTR),
			     tn, NULL);
	}

	/*
	 * Expression of type function (function with return value of type T)
	 * in rvalue-expression (pointer to function with return value
	 * of type T)
	 */
	if (tn->tn_type->t_tspec == FUNC)
		tn = build_ampersand(tn, 1);

	/* lvalue to rvalue */
	if (tn->tn_lvalue) {
		tp = tduptyp(tn->tn_type);
		tp->t_const = tp->t_volatile = 0;
		tn = new_tnode(LOAD, tp, tn, NULL);
	}

	return tn;
}

/*
 * Perform most type checks. First the types are checked using
 * the information from modtab[]. After that it is done by hand for
 * more complicated operators and type combinations.
 *
 * If the types are ok, typeok() returns 1, otherwise 0.
 */
int
typeok(op_t op, int arg, tnode_t *ln, tnode_t *rn)
{
	mod_t	*mp;
	tspec_t	lt, rt = NOTSPEC, lst = NOTSPEC, rst = NOTSPEC, olt = NOTSPEC,
	    ort = NOTSPEC;
	type_t	*ltp, *rtp = NULL, *lstp = NULL, *rstp = NULL;
	tnode_t	*tn;

	mp = &modtab[op];

	lint_assert((ltp = ln->tn_type) != NULL);

	if ((lt = ltp->t_tspec) == PTR)
		lst = (lstp = ltp->t_subt)->t_tspec;
	if (mp->m_binary) {
		lint_assert((rtp = rn->tn_type) != NULL);
		if ((rt = rtp->t_tspec) == PTR)
			rst = (rstp = rtp->t_subt)->t_tspec;
	}

	if (mp->m_requires_integer) {
		if (!tspec_is_int(lt) || (mp->m_binary && !tspec_is_int(rt))) {
			warn_incompatible_types(op, lt, rt);
			return 0;
		}
	} else if (mp->m_requires_integer_or_complex) {
		if ((!tspec_is_int(lt) && !tspec_is_complex(lt)) ||
		    (mp->m_binary &&
		     (!tspec_is_int(rt) && !tspec_is_complex(rt)))) {
			warn_incompatible_types(op, lt, rt);
			return 0;
		}
	} else if (mp->m_requires_scalar) {
		if (!tspec_is_scalar(lt) ||
		    (mp->m_binary && !tspec_is_scalar(rt))) {
			warn_incompatible_types(op, lt, rt);
			return 0;
		}
	} else if (mp->m_requires_arith) {
		if (!tspec_is_arith(lt) ||
		    (mp->m_binary && !tspec_is_arith(rt))) {
			warn_incompatible_types(op, lt, rt);
			return 0;
		}
	}

	if (op == SHL || op == SHR || op == SHLASS || op == SHRASS) {
		/*
		 * For these operations we need the types before promotion
		 * and balancing.
		 */
		for (tn=ln; tn->tn_op==CVT && !tn->tn_cast; tn=tn->tn_left)
			continue;
		olt = tn->tn_type->t_tspec;
		for (tn=rn; tn->tn_op==CVT && !tn->tn_cast; tn=tn->tn_left)
			continue;
		ort = tn->tn_type->t_tspec;
	}

	switch (op) {
	case POINT:
		/*
		 * Most errors required by ANSI C are reported in
		 * struct_or_union_member().
		 * Here we only must check for totally wrong things.
		 */
		if (lt == FUNC || lt == VOID || ltp->t_isfield ||
		    ((lt != STRUCT && lt != UNION) && !ln->tn_lvalue)) {
			/* Without tflag we got already an error */
			if (tflag)
				/* unacceptable operand of '%s' */
				error(111, mp->m_name);
			return 0;
		}
		/* Now we have an object we can create a pointer to */
		break;
	case ARROW:
		if (lt != PTR && !(tflag && tspec_is_int(lt))) {
			/* Without tflag we got already an error */
			if (tflag)
				/* unacceptable operand of '%s' */
				error(111, mp->m_name);
			return 0;
		}
		break;
	case INCAFT:
	case DECAFT:
	case INCBEF:
	case DECBEF:
		/* operands have scalar types (checked above) */
		if (!ln->tn_lvalue) {
			if (ln->tn_op == CVT && ln->tn_cast &&
			    ln->tn_left->tn_op == LOAD) {
				if (ln->tn_type->t_tspec == PTR)
					break;
				/* a cast does not yield an lvalue */
				error(163);
			}
			/* %soperand of '%s' must be lvalue */
			error(114, "", mp->m_name);
			return 0;
		} else if (ltp->t_const) {
			if (!tflag)
				/* %soperand of '%s' must be modifiable ... */
				warning(115, "", mp->m_name);
		}
		break;
	case AMPER:
		if (lt == ARRAY || lt == FUNC) {
			/* ok, a warning comes later (in build_ampersand()) */
		} else if (!ln->tn_lvalue) {
			if (ln->tn_op == CVT && ln->tn_cast &&
			    ln->tn_left->tn_op == LOAD) {
				if (ln->tn_type->t_tspec == PTR)
					break;
				/* a cast does not yield an lvalue */
				error(163);
			}
			/* %soperand of '%s' must be lvalue */
			error(114, "", mp->m_name);
			return 0;
		} else if (tspec_is_scalar(lt)) {
			if (ltp->t_isfield) {
				/* cannot take address of bit-field */
				error(112);
				return 0;
			}
		} else if (lt != STRUCT && lt != UNION) {
			/* unacceptable operand of '%s' */
			error(111, mp->m_name);
			return 0;
		}
		if (ln->tn_op == NAME && ln->tn_sym->s_reg) {
			/* cannot take address of register %s */
			error(113, ln->tn_sym->s_name);
			return 0;
		}
		break;
	case STAR:
		/* until now there were no type checks for this operator */
		if (lt != PTR) {
			/* cannot dereference non-pointer type */
			error(96);
			return 0;
		}
		break;
	case PLUS:
		/* operands have scalar types (checked above) */
		if ((lt == PTR && !tspec_is_int(rt)) ||
		    (rt == PTR && !tspec_is_int(lt))) {
			warn_incompatible_types(op, lt, rt);
			return 0;
		}
		break;
	case MINUS:
		/* operands have scalar types (checked above) */
		if (lt == PTR && (!tspec_is_int(rt) && rt != PTR)) {
			warn_incompatible_types(op, lt, rt);
			return 0;
		} else if (rt == PTR && lt != PTR) {
			warn_incompatible_types(op, lt, rt);
			return 0;
		}
		if (lt == PTR && rt == PTR) {
			if (!eqtype(lstp, rstp, 1, 0, NULL)) {
				/* illegal pointer subtraction */
				error(116);
			}
		}
		break;
	case SHR:
		/* operands have integer types (checked above) */
		if (pflag && !tspec_is_uint(lt)) {
			/*
			 * The left operand is signed. This means that
			 * the operation is (possibly) nonportable.
			 */
			if (ln->tn_op != CON) {
				/* bitop. on signed value poss. nonportable */
				warning(117);
			} else if (ln->tn_val->v_quad < 0) {
				/* bitop. on signed value nonportable */
				warning(120);
			}
		} else if (!tflag && !sflag &&
		    !tspec_is_uint(olt) && tspec_is_uint(ort)) {
			/*
			 * The left operand would become unsigned in
			 * traditional C.
			 */
			if (hflag &&
			    (ln->tn_op != CON || ln->tn_val->v_quad < 0)) {
				/* semantics of '%s' change in ANSI C; ... */
				warning(118, mp->m_name);
			}
		} else if (!tflag && !sflag &&
		    !tspec_is_uint(olt) && !tspec_is_uint(ort) &&
			   psize(lt) < psize(rt)) {
			/*
			 * In traditional C the left operand would be extended,
			 * possibly with 1, and then shifted.
			 */
			if (hflag &&
			    (ln->tn_op != CON || ln->tn_val->v_quad < 0)) {
				/* semantics of '%s' change in ANSI C; ... */
				warning(118, mp->m_name);
			}
		}
		goto shift;
	case SHL:
		/*
		 * ANSI C does not perform balancing for shift operations,
		 * but traditional C does. If the width of the right operand
		 * is greater than the width of the left operand, than in
		 * traditional C the left operand would be extended to the
		 * width of the right operand. For SHL this may result in
		 * different results.
		 */
		if (psize(lt) < psize(rt)) {
			/*
			 * XXX If both operands are constant, make sure
			 * that there is really a difference between
			 * ANSI C and traditional C.
			 */
			if (hflag)
				/* semantics of '%s' change in ANSI C; ... */
				warning(118, mp->m_name);
		}
	shift:
		if (rn->tn_op == CON) {
			if (!tspec_is_uint(rt) && rn->tn_val->v_quad < 0) {
				/* negative shift */
				warning(121);
			} else if ((uint64_t)rn->tn_val->v_quad == (uint64_t)size(lt)) {
				/* shift equal to size of object */
				warning(267);
			} else if ((uint64_t)rn->tn_val->v_quad > (uint64_t)size(lt)) {
				/* shift greater than size of object */
				warning(122);
			}
		}
		break;
	case EQ:
	case NE:
		/*
		 * Accept some things which are allowed with EQ and NE,
		 * but not with ordered comparisons.
		 */
		if (lt == PTR && ((rt == PTR && rst == VOID) ||
				  tspec_is_int(rt))) {
			if (rn->tn_op == CON && rn->tn_val->v_quad == 0)
				break;
		}
		if (rt == PTR && ((lt == PTR && lst == VOID) ||
				  tspec_is_int(lt))) {
			if (ln->tn_op == CON && ln->tn_val->v_quad == 0)
				break;
		}
		/* FALLTHROUGH */
	case LT:
	case GT:
	case LE:
	case GE:
		if ((lt == PTR || rt == PTR) && lt != rt) {
			if (tspec_is_int(lt) || tspec_is_int(rt)) {
				const char *lx = lt == PTR ?
				    "pointer" : "integer";
				const char *rx = rt == PTR ?
				    "pointer" : "integer";
				/* illegal combination of %s (%s) and ... */
				warning(123, lx, type_name(ltp),
				    rx, type_name(rtp), mp->m_name);
			} else {
				warn_incompatible_types(op, lt, rt);
				return 0;
			}
		} else if (lt == PTR && rt == PTR) {
			check_pointer_comparison(op, ln, rn);
		}
		break;
	case QUEST:
		if (!tspec_is_scalar(lt)) {
			/* first operand must have scalar type, op ? : */
			error(170);
			return 0;
		}
		while (rn->tn_op == CVT)
			rn = rn->tn_left;
		lint_assert(rn->tn_op == COLON);
		break;
	case COLON:
		if (tspec_is_arith(lt) && tspec_is_arith(rt))
			break;

		if (lt == STRUCT && rt == STRUCT && ltp->t_str == rtp->t_str)
			break;
		if (lt == UNION && rt == UNION && ltp->t_str == rtp->t_str)
			break;

		/* combination of any pointer and 0, 0L or (void *)0 is ok */
		if (lt == PTR && ((rt == PTR && rst == VOID) ||
				  tspec_is_int(rt))) {
			if (rn->tn_op == CON && rn->tn_val->v_quad == 0)
				break;
		}
		if (rt == PTR && ((lt == PTR && lst == VOID) ||
				  tspec_is_int(lt))) {
			if (ln->tn_op == CON && ln->tn_val->v_quad == 0)
				break;
		}

		if ((lt == PTR && tspec_is_int(rt)) ||
		    (tspec_is_int(lt) && rt == PTR)) {
			const char *lx = lt == PTR ?  "pointer" : "integer";
			const char *rx = rt == PTR ?  "pointer" : "integer";
			/* illegal combination of %s (%s) and %s (%s), op %s */
			warning(123, lx, type_name(ltp),
			    rx, type_name(rtp), mp->m_name);
			break;
		}

		if (lt == VOID || rt == VOID) {
			if (lt != VOID || rt != VOID)
				/* incompatible types in conditional */
				warning(126);
			break;
		}

		if (lt == PTR && rt == PTR && ((lst == VOID && rst == FUNC) ||
					       (lst == FUNC && rst == VOID))) {
			/* (void *)0 handled above */
			if (sflag)
				/* ANSI C forbids conv. of %s to %s, op %s */
				warning(305, "function pointer", "'void *'",
					mp->m_name);
			break;
		}

		if (rt == PTR && lt == PTR) {
			if (eqptrtype(lstp, rstp, 1))
				break;
			if (!eqtype(lstp, rstp, 1, 0, NULL))
				warn_incompatible_pointers(mp, ltp, rtp);
			break;
		}

		/* incompatible types in conditional */
		error(126);
		return 0;

	case ASSIGN:
	case INIT:
	case FARG:
	case RETURN:
		if (!check_assign_types_compatible(op, arg, ln, rn))
			return 0;
		goto assign;
	case MULASS:
	case DIVASS:
	case MODASS:
		goto assign;
	case ADDASS:
	case SUBASS:
		/* operands have scalar types (checked above) */
		if ((lt == PTR && !tspec_is_int(rt)) || rt == PTR) {
			warn_incompatible_types(op, lt, rt);
			return 0;
		}
		goto assign;
	case SHLASS:
		goto assign;
	case SHRASS:
		if (pflag && !tspec_is_uint(lt) &&
		    !(tflag && tspec_is_uint(rt))) {
			/* bitop. on signed value possibly nonportable */
			warning(117);
		}
		goto assign;
	case ANDASS:
	case XORASS:
	case ORASS:
		goto assign;
	assign:
		if (!ln->tn_lvalue) {
			if (ln->tn_op == CVT && ln->tn_cast &&
			    ln->tn_left->tn_op == LOAD) {
				if (ln->tn_type->t_tspec == PTR)
					break;
				/* a cast does not yield an lvalue */
				error(163);
			}
			/* %soperand of '%s' must be lvalue */
			error(114, "left ", mp->m_name);
			return 0;
		} else if (ltp->t_const || ((lt == STRUCT || lt == UNION) &&
					    has_constant_member(ltp))) {
			if (!tflag)
				/* %soperand of '%s' must be modifiable lvalue */
				warning(115, "left ", mp->m_name);
		}
		break;
	case COMMA:
		if (!modtab[ln->tn_op].m_sideeff)
			check_null_effect(ln);
		break;
		/* LINTED206: (enumeration values not handled in switch) */
	case CON:
	case CASE:
	case PUSH:
	case LOAD:
	case ICALL:
	case CVT:
	case CALL:
	case FSEL:
	case STRING:
	case NAME:
	case LOGOR:
	case LOGAND:
	case OR:
	case XOR:
	case AND:
	case MOD:
	case DIV:
	case MULT:
	case UMINUS:
	case UPLUS:
	case DEC:
	case INC:
	case COMPL:
	case NOT:
	case NOOP:
	case REAL:
	case IMAG:
		break;
	}

	if (mp->m_bad_on_enum &&
	    (ltp->t_isenum || (mp->m_binary && rtp->t_isenum))) {
		check_bad_enum_operation(op, ln, rn);
	} else if (mp->m_valid_on_enum &&
	    (ltp->t_isenum && rtp && rtp->t_isenum)) {
		check_enum_type_mismatch(op, arg, ln, rn);
	} else if (mp->m_valid_on_enum &&
	    (ltp->t_isenum || (rtp && rtp->t_isenum))) {
		check_enum_int_mismatch(op, arg, ln, rn);
	}

	return 1;
}

static void
check_pointer_comparison(op_t op, tnode_t *ln, tnode_t *rn)
{
	type_t	*ltp, *rtp;
	tspec_t	lt, rt;
	const	char *lts, *rts;

	lt = (ltp = ln->tn_type)->t_subt->t_tspec;
	rt = (rtp = rn->tn_type)->t_subt->t_tspec;

	if (lt == VOID || rt == VOID) {
		if (sflag && (lt == FUNC || rt == FUNC)) {
			/* (void *)0 already handled in typeok() */
			*(lt == FUNC ? &lts : &rts) = "function pointer";
			*(lt == VOID ? &lts : &rts) = "'void *'";
			/* ANSI C forbids comparison of %s with %s */
			warning(274, lts, rts);
		}
		return;
	}

	if (!eqtype(ltp->t_subt, rtp->t_subt, 1, 0, NULL)) {
		warn_incompatible_pointers(&modtab[op], ltp, rtp);
		return;
	}

	if (lt == FUNC && rt == FUNC) {
		if (sflag && op != EQ && op != NE)
			/* ANSI C forbids ordered comparisons of ... */
			warning(125);
	}
}

/*
 * Checks type compatibility for ASSIGN, INIT, FARG and RETURN
 * and prints warnings/errors if necessary.
 * If the types are (almost) compatible, 1 is returned, otherwise 0.
 */
static int
check_assign_types_compatible(op_t op, int arg, tnode_t *ln, tnode_t *rn)
{
	tspec_t	lt, rt, lst = NOTSPEC, rst = NOTSPEC;
	type_t	*ltp, *rtp, *lstp = NULL, *rstp = NULL;
	mod_t	*mp;
	const	char *lts, *rts;

	if ((lt = (ltp = ln->tn_type)->t_tspec) == PTR)
		lst = (lstp = ltp->t_subt)->t_tspec;
	if ((rt = (rtp = rn->tn_type)->t_tspec) == PTR)
		rst = (rstp = rtp->t_subt)->t_tspec;
	mp = &modtab[op];

	if (tspec_is_arith(lt) && tspec_is_arith(rt))
		return 1;

	if ((lt == STRUCT || lt == UNION) && (rt == STRUCT || rt == UNION))
		/* both are struct or union */
		return ltp->t_str == rtp->t_str;

	/* 0, 0L and (void *)0 may be assigned to any pointer */
	if (lt == PTR && ((rt == PTR && rst == VOID) || tspec_is_int(rt))) {
		if (rn->tn_op == CON && rn->tn_val->v_quad == 0)
			return 1;
	}

	if (lt == PTR && rt == PTR && (lst == VOID || rst == VOID)) {
		/* two pointers, at least one pointer to void */
		if (sflag && (lst == FUNC || rst == FUNC)) {
			/* comb. of ptr to func and ptr to void */
			*(lst == FUNC ? &lts : &rts) = "function pointer";
			*(lst == VOID ? &lts : &rts) = "'void *'";
			switch (op) {
			case INIT:
			case RETURN:
				/* ANSI C forbids conversion of %s to %s */
				warning(303, rts, lts);
				break;
			case FARG:
				/* ANSI C forbids conv. of %s to %s, arg #%d */
				warning(304, rts, lts, arg);
				break;
			default:
				/* ANSI C forbids conv. of %s to %s, op %s */
				warning(305, rts, lts, mp->m_name);
				break;
			}
		}
	}

	if (lt == PTR && rt == PTR && (lst == VOID || rst == VOID ||
				       eqtype(lstp, rstp, 1, 0, NULL))) {
		/* compatible pointer types (qualifiers ignored) */
		if (!tflag &&
		    ((!lstp->t_const && rstp->t_const) ||
		     (!lstp->t_volatile && rstp->t_volatile))) {
			/* left side has not all qualifiers of right */
			switch (op) {
			case INIT:
			case RETURN:
				/* incompatible pointer types (%s != %s) */
				warning(182, type_name(lstp), type_name(rstp));
				break;
			case FARG:
				/* argument has incompatible pointer type... */
				warning(153,
				    arg, type_name(lstp), type_name(rstp));
				break;
			default:
				/* operands have incompatible pointer type... */
				warning(128, mp->m_name,
				    type_name(lstp), type_name(rstp));
				break;
			}
		}
		return 1;
	}

	if ((lt == PTR && tspec_is_int(rt)) ||
	    (tspec_is_int(lt) && rt == PTR)) {
		const char *lx = lt == PTR ? "pointer" : "integer";
		const char *rx = rt == PTR ? "pointer" : "integer";

		switch (op) {
		case INIT:
		case RETURN:
			/* illegal combination of %s (%s) and %s (%s) */
			warning(183, lx, type_name(ltp), rx, type_name(rtp));
			break;
		case FARG:
			/* illegal comb. of %s (%s) and %s (%s), arg #%d */
			warning(154,
			    lx, type_name(ltp), rx, type_name(rtp), arg);
			break;
		default:
			/* illegal combination of %s (%s) and %s (%s), op %s */
			warning(123,
			    lx, type_name(ltp), rx, type_name(rtp), mp->m_name);
			break;
		}
		return 1;
	}

	if (lt == PTR && rt == PTR) {
		switch (op) {
		case INIT:
		case RETURN:
			warn_incompatible_pointers(NULL, ltp, rtp);
			break;
		case FARG:
			/* arg. has incomp. pointer type, arg #%d (%s != %s) */
			warning(153, arg, type_name(ltp), type_name(rtp));
			break;
		default:
			warn_incompatible_pointers(mp, ltp, rtp);
			break;
		}
		return 1;
	}

	switch (op) {
	case INIT:
		/* initialisation type mismatch (%s) and (%s) */
		error(185, type_name(ltp), type_name(rtp));
		break;
	case RETURN:
		/* return value type mismatch (%s) and (%s) */
		error(211, type_name(ltp), type_name(rtp));
		break;
	case FARG:
		/* argument is incompatible with prototype, arg #%d */
		warning(155, arg);
		break;
	default:
		warn_incompatible_types(op, lt, rt);
		break;
	}

	return 0;
}

/*
 * Prints a warning if an operator, which should be senseless for an
 * enum type, is applied to an enum type.
 */
static void
check_bad_enum_operation(op_t op, tnode_t *ln, tnode_t *rn)
{
	mod_t	*mp;

	if (!eflag)
		return;

	mp = &modtab[op];

	if (!(ln->tn_type->t_isenum ||
	      (mp->m_binary && rn->tn_type->t_isenum))) {
		return;
	}

	/*
	 * Enum as offset to a pointer is an exception (otherwise enums
	 * could not be used as array indizes).
	 */
	if (op == PLUS &&
	    ((ln->tn_type->t_isenum && rn->tn_type->t_tspec == PTR) ||
	     (rn->tn_type->t_isenum && ln->tn_type->t_tspec == PTR))) {
		return;
	}

	/* dubious operation on enum, op %s */
	warning(241, mp->m_name);

}

/*
 * Prints a warning if an operator is applied to two different enum types.
 */
static void
check_enum_type_mismatch(op_t op, int arg, tnode_t *ln, tnode_t *rn)
{
	mod_t	*mp;

	mp = &modtab[op];

	if (ln->tn_type->t_enum != rn->tn_type->t_enum) {
		switch (op) {
		case INIT:
			/* enum type mismatch in initialisation */
			warning(210);
			break;
		case FARG:
			/* enum type mismatch, arg #%d */
			warning(156, arg);
			break;
		case RETURN:
			/* return value type mismatch (%s) and (%s) */
			warning(211,
			    type_name(ln->tn_type), type_name(rn->tn_type));
			break;
		default:
			/* enum type mismatch, op %s */
			warning(130, mp->m_name);
			break;
		}
	} else if (Pflag && mp->m_comp && op != EQ && op != NE) {
		if (eflag)
			/* dubious comparison of enums, op %s */
			warning(243, mp->m_name);
	}
}

/*
 * Prints a warning if an operator has both enum and other integer
 * types.
 */
static void
check_enum_int_mismatch(op_t op, int arg, tnode_t *ln, tnode_t *rn)
{

	if (!eflag)
		return;

	switch (op) {
	case INIT:
		/*
		 * Initializations with 0 should be allowed. Otherwise,
		 * we should complain about all uninitialized enums,
		 * consequently.
		 */
		if (!rn->tn_type->t_isenum && rn->tn_op == CON &&
		    tspec_is_int(rn->tn_type->t_tspec) &&
		    rn->tn_val->v_quad == 0) {
			return;
		}
		/* initialisation of '%s' with '%s' */
		warning(277, type_name(ln->tn_type), type_name(rn->tn_type));
		break;
	case FARG:
		/* combination of '%s' and '%s', arg #%d */
		warning(278,
		    type_name(ln->tn_type), type_name(rn->tn_type), arg);
		break;
	case RETURN:
		/* combination of '%s' and '%s' in return */
		warning(279, type_name(ln->tn_type), type_name(rn->tn_type));
		break;
	default:
		/* combination of '%s' and '%s', op %s */
		warning(242, type_name(ln->tn_type), type_name(rn->tn_type),
		    modtab[op].m_name);
		break;
	}
}

/*
 * Build and initialize a new node.
 */
static tnode_t *
new_tnode(op_t op, type_t *type, tnode_t *ln, tnode_t *rn)
{
	tnode_t	*ntn;
	tspec_t	t;
#ifdef notyet
	size_t l;
	uint64_t rnum;
#endif

	ntn = getnode();

	ntn->tn_op = op;
	ntn->tn_type = type;
	ntn->tn_left = ln;
	ntn->tn_right = rn;

	switch (op) {
#ifdef notyet
	case SHR:
		if (rn->tn_op != CON)
			break;
		rnum = rn->tn_val->v_quad;
		l = tsize(ln->tn_type) / CHAR_BIT;
		t = ln->tn_type->t_tspec;
		switch (l) {
		case 8:
			if (rnum >= 56)
				t = UCHAR;
			else if (rnum >= 48)
				t = USHORT;
			else if (rnum >= 32)
				t = UINT;
			break;
		case 4:
			if (rnum >= 24)
				t = UCHAR;
			else if (rnum >= 16)
				t = USHORT;
			break;
		case 2:
			if (rnum >= 8)
				t = UCHAR;
			break;
		default:
			break;
		}
		if (t != ln->tn_type->t_tspec)
			ntn->tn_type->t_tspec = t;
		break;
#endif
	case STAR:
	case FSEL:
		lint_assert(ln->tn_type->t_tspec == PTR);
		t = ln->tn_type->t_subt->t_tspec;
		if (t != FUNC && t != VOID)
			ntn->tn_lvalue = 1;
		break;
	default:
		break;
	}

	return ntn;
}

/*
 * Performs usual conversion of operands to (unsigned) int.
 *
 * If tflag is set or the operand is a function argument with no
 * type information (no prototype or variable # of args), convert
 * float to double.
 */
tnode_t *
promote(op_t op, int farg, tnode_t *tn)
{
	tspec_t	t;
	type_t	*ntp;
	u_int	len;

	t = tn->tn_type->t_tspec;

	if (!tspec_is_arith(t))
		return tn;

	if (!tflag) {
		/*
		 * ANSI C requires that the result is always of type INT
		 * if INT can represent all possible values of the previous
		 * type.
		 */
		if (tn->tn_type->t_isfield) {
			len = tn->tn_type->t_flen;
			if (size(INT) > len) {
				t = INT;
			} else {
				lint_assert(len == size(INT));
				if (tspec_is_uint(t)) {
					t = UINT;
				} else {
					t = INT;
				}
			}
		} else if (t == CHAR || t == UCHAR || t == SCHAR) {
			t = (size(CHAR) < size(INT) || t != UCHAR) ?
				INT : UINT;
		} else if (t == SHORT || t == USHORT) {
			t = (size(SHORT) < size(INT) || t == SHORT) ?
				INT : UINT;
		} else if (t == ENUM) {
			t = INT;
		} else if (farg && t == FLOAT) {
			t = DOUBLE;
		}
	} else {
		/*
		 * In traditional C, keep unsigned and promote FLOAT
		 * to DOUBLE.
		 */
		if (t == UCHAR || t == USHORT) {
			t = UINT;
		} else if (t == CHAR || t == SCHAR || t == SHORT) {
			t = INT;
		} else if (t == FLOAT) {
			t = DOUBLE;
		} else if (t == ENUM) {
			t = INT;
		}
	}

	if (t != tn->tn_type->t_tspec) {
		ntp = tduptyp(tn->tn_type);
		ntp->t_tspec = t;
		/*
		 * Keep t_isenum so we are later able to check compatibility
		 * of enum types.
		 */
		tn = convert(op, 0, ntp, tn);
	}

	return tn;
}

/*
 * Insert conversions which are necessary to give both operands the same
 * type. This is done in different ways for traditional C and ANIS C.
 */
static void
balance(op_t op, tnode_t **lnp, tnode_t **rnp)
{
	tspec_t	lt, rt, t;
	int	i, u;
	type_t	*ntp;
	static	tspec_t	tl[] = {
		LDOUBLE, DOUBLE, FLOAT, UQUAD, QUAD, ULONG, LONG, UINT, INT,
	};

	lt = (*lnp)->tn_type->t_tspec;
	rt = (*rnp)->tn_type->t_tspec;

	if (!tspec_is_arith(lt) || !tspec_is_arith(rt))
		return;

	if (!tflag) {
		if (lt == rt) {
			t = lt;
		} else if (lt == LCOMPLEX || rt == LCOMPLEX) {
			t = LCOMPLEX;
		} else if (lt == DCOMPLEX || rt == DCOMPLEX) {
			t = DCOMPLEX;
		} else if (lt == COMPLEX || rt == COMPLEX) {
			t = COMPLEX;
		} else if (lt == FCOMPLEX || rt == FCOMPLEX) {
			t = FCOMPLEX;
		} else if (lt == LDOUBLE || rt == LDOUBLE) {
			t = LDOUBLE;
		} else if (lt == DOUBLE || rt == DOUBLE) {
			t = DOUBLE;
		} else if (lt == FLOAT || rt == FLOAT) {
			t = FLOAT;
		} else {
			/*
			 * If type A has more bits than type B it should
			 * be able to hold all possible values of type B.
			 */
			if (size(lt) > size(rt)) {
				t = lt;
			} else if (size(lt) < size(rt)) {
				t = rt;
			} else {
				for (i = 3; tl[i] != INT; i++) {
					if (tl[i] == lt || tl[i] == rt)
						break;
				}
				if ((tspec_is_uint(lt) || tspec_is_uint(rt)) &&
				    !tspec_is_uint(tl[i])) {
					i--;
				}
				t = tl[i];
			}
		}
	} else {
		/* Keep unsigned in traditional C */
		u = tspec_is_uint(lt) || tspec_is_uint(rt);
		for (i = 0; tl[i] != INT; i++) {
			if (lt == tl[i] || rt == tl[i])
				break;
		}
		t = tl[i];
		if (u && tspec_is_int(t) && !tspec_is_uint(t))
			t = unsigned_type(t);
	}

	if (t != lt) {
		ntp = tduptyp((*lnp)->tn_type);
		ntp->t_tspec = t;
		*lnp = convert(op, 0, ntp, *lnp);
	}
	if (t != rt) {
		ntp = tduptyp((*rnp)->tn_type);
		ntp->t_tspec = t;
		*rnp = convert(op, 0, ntp, *rnp);
	}
}

/*
 * Insert a conversion operator, which converts the type of the node
 * to another given type.
 * If op is FARG, arg is the number of the argument (used for warnings).
 */
tnode_t *
convert(op_t op, int arg, type_t *tp, tnode_t *tn)
{
	tnode_t	*ntn;
	tspec_t	nt, ot, ost = NOTSPEC;

	nt = tp->t_tspec;
	if ((ot = tn->tn_type->t_tspec) == PTR)
		ost = tn->tn_type->t_subt->t_tspec;

	if (!tflag && !sflag && op == FARG)
		check_prototype_conversion(arg, nt, ot, tp, tn);
	if (tspec_is_int(nt) && tspec_is_int(ot)) {
		check_integer_conversion(op, arg, nt, ot, tp, tn);
	} else if (nt == PTR && ((ot == PTR && ost == VOID) ||
	    tspec_is_int(ot)) && tn->tn_op == CON && tn->tn_val->v_quad == 0) {
		/* 0, 0L and (void *)0 may be assigned to any pointer. */
	} else if (tspec_is_int(nt) && ot == PTR) {
		check_pointer_integer_conversion(op, nt, tp, tn);
	} else if (nt == PTR && ot == PTR) {
		check_pointer_conversion(op, tn, tp);
	}

	ntn = getnode();
	ntn->tn_op = CVT;
	ntn->tn_type = tp;
	ntn->tn_cast = op == CVT;
	ntn->tn_right = NULL;
	if (tn->tn_op != CON || nt == VOID) {
		ntn->tn_left = tn;
	} else {
		ntn->tn_op = CON;
		ntn->tn_val = tgetblk(sizeof (val_t));
		cvtcon(op, arg, ntn->tn_type, ntn->tn_val, tn->tn_val);
	}

	return ntn;
}

/*
 * Print a warning if a prototype causes a type conversion that is
 * different from what would happen to the same argument in the
 * absence of a prototype.
 *
 * Errors/Warnings about illegal type combinations are already printed
 * in check_assign_types_compatible().
 */
static void
check_prototype_conversion(int arg, tspec_t nt, tspec_t ot, type_t *tp,
			   tnode_t *tn)
{
	tnode_t	*ptn;

	if (!tspec_is_arith(nt) || !tspec_is_arith(ot))
		return;

	/*
	 * If the type of the formal parameter is char/short, a warning
	 * would be useless, because functions declared the old style
	 * can't expect char/short arguments.
	 */
	if (nt == CHAR || nt == UCHAR || nt == SHORT || nt == USHORT)
		return;

	/* get default promotion */
	ptn = promote(NOOP, 1, tn);
	ot = ptn->tn_type->t_tspec;

	/* return if types are the same with and without prototype */
	if (nt == ot || (nt == ENUM && ot == INT))
		return;

	if (tspec_is_float(nt) != tspec_is_float(ot) ||
	    psize(nt) != psize(ot)) {
		/* representation and/or width change */
		if (!tspec_is_int(ot) || psize(ot) > psize(INT)) {
			/* conversion to '%s' due to prototype, arg #%d */
			warning(259, type_name(tp), arg);
		}
	} else if (hflag) {
		/*
		 * they differ in sign or base type (char, short, int,
		 * long, long long, float, double, long double)
		 *
		 * if they differ only in sign and the argument is a constant
		 * and the msb of the argument is not set, print no warning
		 */
		if (ptn->tn_op == CON && tspec_is_int(nt) &&
		    signed_type(nt) == signed_type(ot) &&
		    msb(ptn->tn_val->v_quad, ot, -1) == 0) {
			/* ok */
		} else {
			/* conversion to '%s' due to prototype, arg #%d */
			warning(259, type_name(tp), arg);
		}
	}
}

/*
 * Print warnings for conversions of integer types which may cause problems.
 */
/* ARGSUSED */
static void
check_integer_conversion(op_t op, int arg, tspec_t nt, tspec_t ot, type_t *tp,
			 tnode_t *tn)
{
	char opbuf[16];

	if (tn->tn_op == CON)
		return;

	if (op == CVT)
		return;

	if (Pflag && psize(nt) > psize(ot) &&
	    tspec_is_uint(nt) != tspec_is_uint(ot)) {
		if (aflag && pflag) {
			if (op == FARG) {
				/* conversion to '%s' may sign-extend ... */
				warning(297, type_name(tp), arg);
			} else {
				/* conversion to '%s' may sign-extend ... */
				warning(131, type_name(tp));
			}
		}
	}

	if (Pflag && psize(nt) > psize(ot)) {
		switch (tn->tn_op) {
		case PLUS:
		case MINUS:
		case MULT:
		case SHL:
			/* suggest cast from '%s' to '%s' on op %s to ... */
			warning(324, type_name(gettyp(ot)), type_name(tp),
			    print_tnode(opbuf, sizeof(opbuf), tn));
			break;
		default:
			break;
		}
	}

	if (psize(nt) < psize(ot) &&
	    (ot == LONG || ot == ULONG || ot == QUAD || ot == UQUAD ||
	     aflag > 1)) {
		/* conversion from '%s' may lose accuracy */
		if (aflag) {
			if (op == FARG) {
				/* conv. from '%s' to '%s' may lose ... */
				warning(298,
				    type_name(tn->tn_type), type_name(tp), arg);
			} else {
				/* conv. from '%s' to '%s' may lose accuracy */
				warning(132,
				    type_name(tn->tn_type), type_name(tp));
			}
		}
	}
}

/*
 * Print warnings for dubious conversions of pointer to integer.
 */
static void
check_pointer_integer_conversion(op_t op, tspec_t nt, type_t *tp, tnode_t *tn)
{

	if (tn->tn_op == CON)
		return;

	if (op != CVT) {
		/* We got already an error. */
		return;
	}

	if (psize(nt) < psize(PTR)) {
		if (pflag && size(nt) >= size(PTR)) {
			/* conv. of pointer to '%s' may lose bits */
			warning(134, type_name(tp));
		} else {
			/* conv. of pointer to '%s' loses bits */
			warning(133, type_name(tp));
		}
	}
}

/*
 * Print warnings for questionable pointer conversions.
 */
static void
check_pointer_conversion(op_t op, tnode_t *tn, type_t *tp)
{
	tspec_t nt, ot;
	const	char *nts, *ots;

	/*
	 * We got already an error (pointers of different types
	 * without a cast) or we will not get a warning.
	 */
	if (op != CVT)
		return;

	nt = tp->t_subt->t_tspec;
	ot = tn->tn_type->t_subt->t_tspec;

	if (nt == VOID || ot == VOID) {
		if (sflag && (nt == FUNC || ot == FUNC)) {
			/* (void *)0 already handled in convert() */
			*(nt == FUNC ? &nts : &ots) = "function pointer";
			*(nt == VOID ? &nts : &ots) = "'void *'";
			/* ANSI C forbids conversion of %s to %s */
			warning(303, ots, nts);
		}
		return;
	} else if (nt == FUNC && ot == FUNC) {
		return;
	} else if (nt == FUNC || ot == FUNC) {
		/* questionable conversion of function pointer */
		warning(229);
		return;
	}

	if (getbound(tp->t_subt) > getbound(tn->tn_type->t_subt)) {
		if (hflag)
			/* possible pointer alignment problem */
			warning(135);
	}
	if (((nt == STRUCT || nt == UNION) &&
	     tp->t_subt->t_str != tn->tn_type->t_subt->t_str) ||
	    psize(nt) != psize(ot)) {
		if (cflag) {
			/* pointer casts may be troublesome */
			warning(247);
		}
	}
}

/*
 * Converts a typed constant in a constant of another type.
 *
 * op		operator which requires conversion
 * arg		if op is FARG, # of argument
 * tp		type in which to convert the constant
 * nv		new constant
 * v		old constant
 */
void
cvtcon(op_t op, int arg, type_t *tp, val_t *nv, val_t *v)
{
	tspec_t	ot, nt;
	ldbl_t	max = 0.0, min = 0.0;
	int	sz, rchk;
	int64_t	xmask, xmsk1;
	int	osz, nsz;

	ot = v->v_tspec;
	nt = nv->v_tspec = tp->t_tspec;
	rchk = 0;

	if (ot == FLOAT || ot == DOUBLE || ot == LDOUBLE) {
		switch (nt) {
		case BOOL:
			max = 1;		min = 0;		break;
		case CHAR:
			max = TARG_CHAR_MAX;	min = TARG_CHAR_MIN;	break;
		case UCHAR:
			max = TARG_UCHAR_MAX;	min = 0;		break;
		case SCHAR:
			max = TARG_SCHAR_MAX;	min = TARG_SCHAR_MIN;	break;
		case SHORT:
			max = TARG_SHRT_MAX;	min = TARG_SHRT_MIN;	break;
		case USHORT:
			max = TARG_USHRT_MAX;	min = 0;		break;
		case ENUM:
		case INT:
			max = TARG_INT_MAX;	min = TARG_INT_MIN;	break;
		case UINT:
			max = (u_int)TARG_UINT_MAX;min = 0;		break;
		case LONG:
			max = TARG_LONG_MAX;	min = TARG_LONG_MIN;	break;
		case ULONG:
			max = (u_long)TARG_ULONG_MAX; min = 0;		break;
		case QUAD:
			max = QUAD_MAX;		min = QUAD_MIN;		break;
		case UQUAD:
			max = (uint64_t)UQUAD_MAX; min = 0;		break;
		case FLOAT:
		case FCOMPLEX:
			max = FLT_MAX;		min = -FLT_MAX;		break;
		case DOUBLE:
		case DCOMPLEX:
			max = DBL_MAX;		min = -DBL_MAX;		break;
		case PTR:
			/* Got already an error because of float --> ptr */
		case LDOUBLE:
		case LCOMPLEX:
			max = LDBL_MAX;		min = -LDBL_MAX;	break;
		default:
			lint_assert(0);
		}
		if (v->v_ldbl > max || v->v_ldbl < min) {
			lint_assert(nt != LDOUBLE);
			if (op == FARG) {
				/* conv. of '%s' to '%s' is out of range, ... */
				warning(295,
				    type_name(gettyp(ot)), type_name(tp), arg);
			} else {
				/* conversion of '%s' to '%s' is out of range */
				warning(119,
				    type_name(gettyp(ot)), type_name(tp));
			}
			v->v_ldbl = v->v_ldbl > 0 ? max : min;
		}
		if (nt == FLOAT) {
			nv->v_ldbl = (float)v->v_ldbl;
		} else if (nt == DOUBLE) {
			nv->v_ldbl = (double)v->v_ldbl;
		} else if (nt == LDOUBLE) {
			nv->v_ldbl = v->v_ldbl;
		} else {
			nv->v_quad = (nt == PTR || tspec_is_uint(nt)) ?
				(int64_t)v->v_ldbl : (int64_t)v->v_ldbl;
		}
	} else {
		if (nt == FLOAT) {
			nv->v_ldbl = (ot == PTR || tspec_is_uint(ot)) ?
			       (float)(uint64_t)v->v_quad : (float)v->v_quad;
		} else if (nt == DOUBLE) {
			nv->v_ldbl = (ot == PTR || tspec_is_uint(ot)) ?
			       (double)(uint64_t)v->v_quad : (double)v->v_quad;
		} else if (nt == LDOUBLE) {
			nv->v_ldbl = (ot == PTR || tspec_is_uint(ot)) ?
			       (ldbl_t)(uint64_t)v->v_quad : (ldbl_t)v->v_quad;
		} else {
			rchk = 1;		/* Check for lost precision. */
			nv->v_quad = v->v_quad;
		}
	}

	if (v->v_ansiu && tspec_is_float(nt)) {
		/* ANSI C treats constant as unsigned */
		warning(157);
		v->v_ansiu = 0;
	} else if (v->v_ansiu && (tspec_is_int(nt) && !tspec_is_uint(nt) &&
				  psize(nt) > psize(ot))) {
		/* ANSI C treats constant as unsigned */
		warning(157);
		v->v_ansiu = 0;
	}

	switch (nt) {
	case FLOAT:
	case FCOMPLEX:
	case DOUBLE:
	case DCOMPLEX:
	case LDOUBLE:
	case LCOMPLEX:
		break;
	default:
		sz = tp->t_isfield ? tp->t_flen : size(nt);
		nv->v_quad = xsign(nv->v_quad, nt, sz);
		break;
	}

	if (rchk && op != CVT) {
		osz = size(ot);
		nsz = tp->t_isfield ? tp->t_flen : size(nt);
		xmask = qlmasks[nsz] ^ qlmasks[osz];
		xmsk1 = qlmasks[nsz] ^ qlmasks[osz - 1];
		/*
		 * For bitwise operations we are not interested in the
		 * value, but in the bits itself.
		 */
		if (op == ORASS || op == OR || op == XOR) {
			/*
			 * Print a warning if bits which were set are
			 * lost due to the conversion.
			 * This can happen with operator ORASS only.
			 */
			if (nsz < osz && (v->v_quad & xmask) != 0) {
				/* constant truncated by conv., op %s */
				warning(306, modtab[op].m_name);
			}
		} else if (op == ANDASS || op == AND) {
			/*
			 * Print a warning if additional bits are not all 1
			 * and the most significant bit of the old value is 1,
			 * or if at least one (but not all) removed bit was 0.
			 */
			if (nsz > osz &&
			    (nv->v_quad & qbmasks[osz - 1]) != 0 &&
			    (nv->v_quad & xmask) != xmask) {
				/* extra bits set to 0 in conv. of '%s' ... */
				warning(309, type_name(gettyp(ot)),
				    type_name(tp), modtab[op].m_name);
			} else if (nsz < osz &&
				   (v->v_quad & xmask) != xmask &&
				   (v->v_quad & xmask) != 0) {
				/* constant truncated by conv., op %s */
				warning(306, modtab[op].m_name);
			}
		} else if ((nt != PTR && tspec_is_uint(nt)) &&
			   (ot != PTR && !tspec_is_uint(ot)) && v->v_quad < 0) {
			if (op == ASSIGN) {
				/* assignment of negative constant to ... */
				warning(164);
			} else if (op == INIT) {
				/* initialisation of unsigned with neg... */
				warning(221);
			} else if (op == FARG) {
				/* conversion of negative constant to ... */
				warning(296, arg);
			} else if (modtab[op].m_comp) {
				/* handled by check_integer_comparison() */
			} else {
				/* conversion of negative constant to ... */
				warning(222);
			}
		} else if (nv->v_quad != v->v_quad && nsz <= osz &&
			   (v->v_quad & xmask) != 0 &&
			   (tspec_is_uint(ot) ||
			    (v->v_quad & xmsk1) != xmsk1)) {
			/*
			 * Loss of significant bit(s). All truncated bits
			 * of unsigned types or all truncated bits plus the
			 * msb of the target for signed types are considered
			 * to be significant bits. Loss of significant bits
			 * means that at least on of the bits was set in an
			 * unsigned type or that at least one, but not all of
			 * the bits was set in an signed type.
			 * Loss of significant bits means that it is not
			 * possible, also not with necessary casts, to convert
			 * back to the original type. A example for a
			 * necessary cast is:
			 *	char c;	int	i; c = 128;
			 *	i = c;			** yields -128 **
			 *	i = (unsigned char)c;	** yields 128 **
			 */
			if (op == ASSIGN && tp->t_isfield) {
				/* precision lost in bit-field assignment */
				warning(166);
			} else if (op == ASSIGN) {
				/* constant truncated by assignment */
				warning(165);
			} else if (op == INIT && tp->t_isfield) {
				/* bit-field initializer does not fit */
				warning(180);
			} else if (op == INIT) {
				/* initializer does not fit */
				warning(178);
			} else if (op == CASE) {
				/* case label affected by conversion */
				warning(196);
			} else if (op == FARG) {
				/* conv. of '%s' to '%s' is out of range, ... */
				warning(295,
				    type_name(gettyp(ot)), type_name(tp), arg);
			} else {
				/* conversion of '%s' to '%s' is out of range */
				warning(119,
				    type_name(gettyp(ot)), type_name(tp));
			}
		} else if (nv->v_quad != v->v_quad) {
			if (op == ASSIGN && tp->t_isfield) {
				/* precision lost in bit-field assignment */
				warning(166);
			} else if (op == INIT && tp->t_isfield) {
				/* bit-field initializer out of range */
				warning(11);
			} else if (op == CASE) {
				/* case label affected by conversion */
				warning(196);
			} else if (op == FARG) {
				/* conv. of '%s' to '%s' is out of range, ... */
				warning(295,
				    type_name(gettyp(ot)), type_name(tp), arg);
			} else {
				/* conversion of '%s' to '%s' is out of range */
				warning(119,
				    type_name(gettyp(ot)), type_name(tp));
			}
		}
	}
}

/*
 * Called if incompatible types were detected.
 * Prints a appropriate warning.
 */
static void
warn_incompatible_types(op_t op, tspec_t lt, tspec_t rt)
{
	mod_t	*mp;

	mp = &modtab[op];

	if (lt == VOID || (mp->m_binary && rt == VOID)) {
		/* void type illegal in expression */
		error(109);
	} else if (op == ASSIGN) {
		if ((lt == STRUCT || lt == UNION) &&
		    (rt == STRUCT || rt == UNION)) {
			/* assignment of different structures (%s != %s) */
			error(240, tspec_name(lt), tspec_name(rt));
		} else {
			/* assignment type mismatch (%s != %s) */
			error(171, tspec_name(lt), tspec_name(rt));
		}
	} else if (mp->m_binary) {
		/* operands of '%s' have incompatible types (%s != %s) */
		error(107, mp->m_name, tspec_name(lt), tspec_name(rt));
	} else {
		/* operand of '%s' has incompatible type (%s != %s) */
		error(108, mp->m_name, tspec_name(lt), tspec_name(rt));
	}
}

/*
 * Called if incompatible pointer types are detected.
 * Print an appropriate warning.
 */
static void
warn_incompatible_pointers(mod_t *mp, type_t *ltp, type_t *rtp)
{
	tspec_t	lt, rt;

	lint_assert(ltp->t_tspec == PTR);
	lint_assert(rtp->t_tspec == PTR);

	lt = ltp->t_subt->t_tspec;
	rt = rtp->t_subt->t_tspec;

	if ((lt == STRUCT || lt == UNION) && (rt == STRUCT || rt == UNION)) {
		if (mp == NULL) {
			/* illegal structure pointer combination */
			warning(244);
		} else {
			/* illegal structure pointer combination, op %s */
			warning(245, mp->m_name);
		}
	} else {
		if (mp == NULL) {
			/* illegal pointer combination */
			warning(184);
		} else {
			/* illegal pointer combination, op %s */
			warning(124, mp->m_name);
		}
	}
}

/*
 * Make sure type (*tpp)->t_subt has at least the qualifiers
 * of tp1->t_subt and tp2->t_subt.
 */
static void
merge_qualifiers(type_t **tpp, type_t *tp1, type_t *tp2)
{

	lint_assert((*tpp)->t_tspec == PTR);
	lint_assert(tp1->t_tspec == PTR);
	lint_assert(tp2->t_tspec == PTR);

	if ((*tpp)->t_subt->t_const ==
	    (tp1->t_subt->t_const | tp2->t_subt->t_const) &&
	    (*tpp)->t_subt->t_volatile ==
	    (tp1->t_subt->t_volatile | tp2->t_subt->t_volatile)) {
		return;
	}

	*tpp = tduptyp(*tpp);
	(*tpp)->t_subt = tduptyp((*tpp)->t_subt);
	(*tpp)->t_subt->t_const =
		tp1->t_subt->t_const | tp2->t_subt->t_const;
	(*tpp)->t_subt->t_volatile =
		tp1->t_subt->t_volatile | tp2->t_subt->t_volatile;
}

/*
 * Returns 1 if the given structure or union has a constant member
 * (maybe recursively).
 */
static int
has_constant_member(type_t *tp)
{
	sym_t	*m;
	tspec_t	t;

	lint_assert((t = tp->t_tspec) == STRUCT || t == UNION);

	for (m = tp->t_str->memb; m != NULL; m = m->s_next) {
		tp = m->s_type;
		if (tp->t_const)
			return 1;
		if ((t = tp->t_tspec) == STRUCT || t == UNION) {
			if (has_constant_member(m->s_type))
				return 1;
		}
	}
	return 0;
}

/*
 * Create a new node for one of the operators POINT and ARROW.
 */
static tnode_t *
build_struct_access(op_t op, tnode_t *ln, tnode_t *rn)
{
	tnode_t	*ntn, *ctn;
	int	nolval;

	lint_assert(rn->tn_op == NAME);
	lint_assert(rn->tn_sym->s_value.v_tspec == INT);
	lint_assert(rn->tn_sym->s_scl == MOS || rn->tn_sym->s_scl == MOU);

	/*
	 * Remember if the left operand is an lvalue (structure members
	 * are lvalues if and only if the structure itself is an lvalue).
	 */
	nolval = op == POINT && !ln->tn_lvalue;

	if (op == POINT) {
		ln = build_ampersand(ln, 1);
	} else if (ln->tn_type->t_tspec != PTR) {
		lint_assert(tflag);
		lint_assert(tspec_is_int(ln->tn_type->t_tspec));
		ln = convert(NOOP, 0, tincref(gettyp(VOID), PTR), ln);
	}

#if PTRDIFF_IS_LONG
	ctn = new_int_const_node(LONG, rn->tn_sym->s_value.v_quad / CHAR_BIT);
#else
	ctn = new_int_const_node(INT, rn->tn_sym->s_value.v_quad / CHAR_BIT);
#endif

	ntn = new_tnode(PLUS, tincref(rn->tn_type, PTR), ln, ctn);
	if (ln->tn_op == CON)
		ntn = fold(ntn);

	if (rn->tn_type->t_isfield) {
		ntn = new_tnode(FSEL, ntn->tn_type->t_subt, ntn, NULL);
	} else {
		ntn = new_tnode(STAR, ntn->tn_type->t_subt, ntn, NULL);
	}

	if (nolval)
		ntn->tn_lvalue = 0;

	return ntn;
}

/*
 * Create a node for INCAFT, INCBEF, DECAFT and DECBEF.
 */
static tnode_t *
build_prepost_incdec(op_t op, tnode_t *ln)
{
	tnode_t	*cn, *ntn;

	lint_assert(ln != NULL);

	if (ln->tn_type->t_tspec == PTR) {
		cn = plength(ln->tn_type);
	} else {
		cn = new_int_const_node(INT, (int64_t)1);
	}
	ntn = new_tnode(op, ln->tn_type, ln, cn);

	return ntn;
}

/*
 * Create a node for REAL, IMAG
 */
static tnode_t *
build_real_imag(op_t op, tnode_t *ln)
{
	tnode_t	*cn, *ntn;

	lint_assert(ln != NULL);

	switch (ln->tn_type->t_tspec) {
	case LCOMPLEX:
		cn = new_int_const_node(LDOUBLE, (int64_t)1);
		break;
	case DCOMPLEX:
		cn = new_int_const_node(DOUBLE, (int64_t)1);
		break;
	case FCOMPLEX:
		cn = new_int_const_node(FLOAT, (int64_t)1);
		break;
	default:
		/* __%s__ is illegal for type %s */
		error(276, op == REAL ? "real" : "imag",
		    type_name(ln->tn_type));
		return NULL;
	}
	ntn = new_tnode(op, cn->tn_type, ln, cn);
	ntn->tn_lvalue = 1;

	return ntn;
}
/*
 * Create a tree node for the & operator
 */
static tnode_t *
build_ampersand(tnode_t *tn, int noign)
{
	tnode_t	*ntn;
	tspec_t	t;

	if (!noign && ((t = tn->tn_type->t_tspec) == ARRAY || t == FUNC)) {
		if (tflag)
			/* '&' before array or function: ignored */
			warning(127);
		return tn;
	}

	/* eliminate &* */
	if (tn->tn_op == STAR &&
	    tn->tn_left->tn_type->t_tspec == PTR &&
	    tn->tn_left->tn_type->t_subt == tn->tn_type) {
		return tn->tn_left;
	}

	ntn = new_tnode(AMPER, tincref(tn->tn_type, PTR), tn, NULL);

	return ntn;
}

/*
 * Create a node for operators PLUS and MINUS.
 */
static tnode_t *
build_plus_minus(op_t op, tnode_t *ln, tnode_t *rn)
{
	tnode_t	*ntn, *ctn;
	type_t	*tp;

	/* If pointer and integer, then pointer to the lhs. */
	if (rn->tn_type->t_tspec == PTR &&
	    tspec_is_int(ln->tn_type->t_tspec)) {
		ntn = ln;
		ln = rn;
		rn = ntn;
	}

	if (ln->tn_type->t_tspec == PTR && rn->tn_type->t_tspec != PTR) {

		lint_assert(tspec_is_int(rn->tn_type->t_tspec));

		ctn = plength(ln->tn_type);
		if (rn->tn_type->t_tspec != ctn->tn_type->t_tspec)
			rn = convert(NOOP, 0, ctn->tn_type, rn);
		rn = new_tnode(MULT, rn->tn_type, rn, ctn);
		if (rn->tn_left->tn_op == CON)
			rn = fold(rn);
		ntn = new_tnode(op, ln->tn_type, ln, rn);

	} else if (rn->tn_type->t_tspec == PTR) {

		lint_assert(ln->tn_type->t_tspec == PTR);
		lint_assert(op == MINUS);
#if PTRDIFF_IS_LONG
		tp = gettyp(LONG);
#else
		tp = gettyp(INT);
#endif
		ntn = new_tnode(op, tp, ln, rn);
		if (ln->tn_op == CON && rn->tn_op == CON)
			ntn = fold(ntn);
		ctn = plength(ln->tn_type);
		balance(NOOP, &ntn, &ctn);
		ntn = new_tnode(DIV, tp, ntn, ctn);

	} else {

		ntn = new_tnode(op, ln->tn_type, ln, rn);

	}
	return ntn;
}

/*
 * Create a node for operators SHL and SHR.
 */
static tnode_t *
build_bit_shift(op_t op, tnode_t *ln, tnode_t *rn)
{
	tspec_t	t;
	tnode_t	*ntn;

	if ((t = rn->tn_type->t_tspec) != INT && t != UINT)
		rn = convert(CVT, 0, gettyp(INT), rn);
	ntn = new_tnode(op, ln->tn_type, ln, rn);
	return ntn;
}

/*
 * Create a node for COLON.
 */
static tnode_t *
build_colon(tnode_t *ln, tnode_t *rn)
{
	tspec_t	lt, rt, pdt;
	type_t	*rtp;
	tnode_t	*ntn;

	lt = ln->tn_type->t_tspec;
	rt = rn->tn_type->t_tspec;
#if PTRDIFF_IS_LONG
	pdt = LONG;
#else
	pdt = INT;
#endif

	/*
	 * Arithmetic types are balanced, all other type combinations
	 * still need to be handled.
	 */
	if (tspec_is_arith(lt) && tspec_is_arith(rt)) {
		rtp = ln->tn_type;
	} else if (lt == VOID || rt == VOID) {
		rtp = gettyp(VOID);
	} else if (lt == STRUCT || lt == UNION) {
		/* Both types must be identical. */
		lint_assert(rt == STRUCT || rt == UNION);
		lint_assert(ln->tn_type->t_str == rn->tn_type->t_str);
		if (incompl(ln->tn_type)) {
			/* unknown operand size, op %s */
			error(138, modtab[COLON].m_name);
			return NULL;
		}
		rtp = ln->tn_type;
	} else if (lt == PTR && tspec_is_int(rt)) {
		if (rt != pdt) {
			rn = convert(NOOP, 0, gettyp(pdt), rn);
			rt = pdt;
		}
		rtp = ln->tn_type;
	} else if (rt == PTR && tspec_is_int(lt)) {
		if (lt != pdt) {
			ln = convert(NOOP, 0, gettyp(pdt), ln);
			lt = pdt;
		}
		rtp = rn->tn_type;
	} else if (lt == PTR && ln->tn_type->t_subt->t_tspec == VOID) {
		lint_assert(rt == PTR);
		rtp = rn->tn_type;
		merge_qualifiers(&rtp, ln->tn_type, rn->tn_type);
	} else if (rt == PTR && rn->tn_type->t_subt->t_tspec == VOID) {
		lint_assert(lt == PTR);
		rtp = ln->tn_type;
		merge_qualifiers(&rtp, ln->tn_type, rn->tn_type);
	} else {
		lint_assert(lt == PTR);
		lint_assert(rt == PTR);
		/*
		 * XXX For now we simply take the left type. This is
		 * probably wrong, if one type contains a function prototype
		 * and the other one, at the same place, only an old style
		 * declaration.
		 */
		rtp = ln->tn_type;
		merge_qualifiers(&rtp, ln->tn_type, rn->tn_type);
	}

	ntn = new_tnode(COLON, rtp, ln, rn);

	return ntn;
}

/*
 * Create a node for an assignment operator (both = and op= ).
 */
static tnode_t *
build_assignment(op_t op, tnode_t *ln, tnode_t *rn)
{
	tspec_t	lt, rt;
	tnode_t	*ntn, *ctn;

	lint_assert(ln != NULL);
	lint_assert(rn != NULL);

	lt = ln->tn_type->t_tspec;
	rt = rn->tn_type->t_tspec;

	if ((op == ADDASS || op == SUBASS) && lt == PTR) {
		lint_assert(tspec_is_int(rt));
		ctn = plength(ln->tn_type);
		if (rn->tn_type->t_tspec != ctn->tn_type->t_tspec)
			rn = convert(NOOP, 0, ctn->tn_type, rn);
		rn = new_tnode(MULT, rn->tn_type, rn, ctn);
		if (rn->tn_left->tn_op == CON)
			rn = fold(rn);
	}

	if ((op == ASSIGN || op == RETURN) && (lt == STRUCT || rt == STRUCT)) {
		lint_assert(lt == rt);
		lint_assert(ln->tn_type->t_str == rn->tn_type->t_str);
		if (incompl(ln->tn_type)) {
			if (op == RETURN) {
				/* cannot return incomplete type */
				error(212);
			} else {
				/* unknown operand size, op %s */
				error(138, modtab[op].m_name);
			}
			return NULL;
		}
	}

	if (op == SHLASS) {
		if (psize(lt) < psize(rt)) {
			if (hflag)
				/* semantics of '%s' change in ANSI C; ... */
				warning(118, "<<=");
		}
	} else if (op != SHRASS) {
		if (op == ASSIGN || lt != PTR) {
			if (lt != rt ||
			    (ln->tn_type->t_isfield && rn->tn_op == CON)) {
				rn = convert(op, 0, ln->tn_type, rn);
				rt = lt;
			}
		}
	}

	ntn = new_tnode(op, ln->tn_type, ln, rn);

	return ntn;
}

/*
 * Get length of type tp->t_subt.
 */
static tnode_t *
plength(type_t *tp)
{
	int	elem, elsz;
	tspec_t	st;

	lint_assert(tp->t_tspec == PTR);
	tp = tp->t_subt;

	elem = 1;
	elsz = 0;

	while (tp->t_tspec == ARRAY) {
		elem *= tp->t_dim;
		tp = tp->t_subt;
	}

	switch (tp->t_tspec) {
	case FUNC:
		/* pointer to function is not allowed here */
		error(110);
		break;
	case VOID:
		/* cannot do pointer arithmetic on operand of ... */
		(void)gnuism(136);
		break;
	case STRUCT:
	case UNION:
		if ((elsz = tp->t_str->size) == 0)
			/* cannot do pointer arithmetic on operand of ... */
			error(136);
		break;
	case ENUM:
		if (incompl(tp)) {
			/* cannot do pointer arithmetic on operand of ... */
			warning(136);
		}
		/* FALLTHROUGH */
	default:
		if ((elsz = size(tp->t_tspec)) == 0) {
			/* cannot do pointer arithmetic on operand of ... */
			error(136);
		} else {
			lint_assert(elsz != -1);
		}
		break;
	}

	if (elem == 0 && elsz != 0) {
		/* cannot do pointer arithmetic on operand of ... */
		error(136);
	}

	if (elsz == 0)
		elsz = CHAR_BIT;

#if PTRDIFF_IS_LONG
	st = LONG;
#else
	st = INT;
#endif

	return new_int_const_node(st, (int64_t)(elem * elsz / CHAR_BIT));
}

/*
 * XXX
 * Note: There appear to be a number of bugs in detecting overflow in
 * this function. An audit and a set of proper regression tests are needed.
 *     --Perry Metzger, Nov. 16, 2001
 */
/*
 * Do only as much as necessary to compute constant expressions.
 * Called only if the operator allows folding and (both) operands
 * are constants.
 */
static tnode_t *
fold(tnode_t *tn)
{
	val_t	*v;
	tspec_t	t;
	int	utyp, ovfl;
	int64_t	sl, sr = 0, q = 0, mask;
	uint64_t ul, ur = 0;
	tnode_t	*cn;

	v = xcalloc(1, sizeof (val_t));
	v->v_tspec = t = tn->tn_type->t_tspec;

	utyp = t == PTR || tspec_is_uint(t);
	ul = sl = tn->tn_left->tn_val->v_quad;
	if (modtab[tn->tn_op].m_binary)
		ur = sr = tn->tn_right->tn_val->v_quad;

	mask = qlmasks[size(t)];
	ovfl = 0;

	switch (tn->tn_op) {
	case UPLUS:
		q = sl;
		break;
	case UMINUS:
		q = -sl;
		if (sl != 0 && msb(q, t, -1) == msb(sl, t, -1))
			ovfl = 1;
		break;
	case COMPL:
		q = ~sl;
		break;
	case MULT:
		if (utyp) {
			q = ul * ur;
			if (q != (q & mask))
				ovfl = 1;
			else if ((ul != 0) && ((q / ul) != ur))
				ovfl = 1;
		} else {
			q = sl * sr;
			if (msb(q, t, -1) != (msb(sl, t, -1) ^ msb(sr, t, -1)))
				ovfl = 1;
		}
		break;
	case DIV:
		if (sr == 0) {
			/* division by 0 */
			error(139);
			q = utyp ? UQUAD_MAX : QUAD_MAX;
		} else {
			q = utyp ? (int64_t)(ul / ur) : sl / sr;
		}
		break;
	case MOD:
		if (sr == 0) {
			/* modulus by 0 */
			error(140);
			q = 0;
		} else {
			q = utyp ? (int64_t)(ul % ur) : sl % sr;
		}
		break;
	case PLUS:
		q = utyp ? (int64_t)(ul + ur) : sl + sr;
		if (msb(sl, t, -1)  != 0 && msb(sr, t, -1) != 0) {
			if (msb(q, t, -1) == 0)
				ovfl = 1;
		} else if (msb(sl, t, -1) == 0 && msb(sr, t, -1) == 0) {
			if (msb(q, t, -1) != 0)
				ovfl = 1;
		}
		break;
	case MINUS:
		q = utyp ? (int64_t)(ul - ur) : sl - sr;
		if (msb(sl, t, -1) != 0 && msb(sr, t, -1) == 0) {
			if (msb(q, t, -1) == 0)
				ovfl = 1;
		} else if (msb(sl, t, -1) == 0 && msb(sr, t, -1) != 0) {
			if (msb(q, t, -1) != 0)
				ovfl = 1;
		}
		break;
	case SHL:
		q = utyp ? (int64_t)(ul << sr) : sl << sr;
		break;
	case SHR:
		/*
		 * The sign must be explicitly extended because
		 * shifts of signed values are implementation dependent.
		 */
		q = ul >> sr;
		q = xsign(q, t, size(t) - (int)sr);
		break;
	case LT:
		q = utyp ? ul < ur : sl < sr;
		break;
	case LE:
		q = utyp ? ul <= ur : sl <= sr;
		break;
	case GE:
		q = utyp ? ul >= ur : sl >= sr;
		break;
	case GT:
		q = utyp ? ul > ur : sl > sr;
		break;
	case EQ:
		q = utyp ? ul == ur : sl == sr;
		break;
	case NE:
		q = utyp ? ul != ur : sl != sr;
		break;
	case AND:
		q = utyp ? (int64_t)(ul & ur) : sl & sr;
		break;
	case XOR:
		q = utyp ? (int64_t)(ul ^ ur) : sl ^ sr;
		break;
	case OR:
		q = utyp ? (int64_t)(ul | ur) : sl | sr;
		break;
	default:
		lint_assert(0);
	}

	/* XXX does not work for quads. */
	if (ovfl || ((uint64_t)(q | mask) != ~(uint64_t)0 &&
	    (q & ~mask) != 0)) {
		if (hflag)
			/* integer overflow detected, op %s */
			warning(141, modtab[tn->tn_op].m_name);
	}

	v->v_quad = xsign(q, t, -1);

	cn = getcnode(tn->tn_type, v);

	return cn;
}

/*
 * Same for operators whose operands are compared with 0 (test context).
 */
static tnode_t *
fold_test(tnode_t *tn)
{
	int	l, r = 0;
	val_t	*v;

	v = xcalloc(1, sizeof (val_t));
	v->v_tspec = tn->tn_type->t_tspec;
	lint_assert(tn->tn_type->t_tspec == INT);

	if (tspec_is_float(tn->tn_left->tn_type->t_tspec)) {
		l = tn->tn_left->tn_val->v_ldbl != 0.0;
	} else {
		l = tn->tn_left->tn_val->v_quad != 0;
	}

	if (modtab[tn->tn_op].m_binary) {
		if (tspec_is_float(tn->tn_right->tn_type->t_tspec)) {
			r = tn->tn_right->tn_val->v_ldbl != 0.0;
		} else {
			r = tn->tn_right->tn_val->v_quad != 0;
		}
	}

	switch (tn->tn_op) {
	case NOT:
		if (hflag && !constcond_flag)
			/* constant argument to NOT */
			warning(239);
		v->v_quad = !l;
		break;
	case LOGAND:
		v->v_quad = l && r;
		break;
	case LOGOR:
		v->v_quad = l || r;
		break;
	default:
		lint_assert(0);
	}

	return getcnode(tn->tn_type, v);
}

/*
 * Same for operands with floating point type.
 */
static tnode_t *
fold_float(tnode_t *tn)
{
	val_t	*v;
	tspec_t	t;
	ldbl_t	l, r = 0;

	fpe = 0;
	v = xcalloc(1, sizeof (val_t));
	v->v_tspec = t = tn->tn_type->t_tspec;

	lint_assert(tspec_is_float(t));
	lint_assert(t == tn->tn_left->tn_type->t_tspec);
	lint_assert(!modtab[tn->tn_op].m_binary ||
	    t == tn->tn_right->tn_type->t_tspec);

	l = tn->tn_left->tn_val->v_ldbl;
	if (modtab[tn->tn_op].m_binary)
		r = tn->tn_right->tn_val->v_ldbl;

	switch (tn->tn_op) {
	case UPLUS:
		v->v_ldbl = l;
		break;
	case UMINUS:
		v->v_ldbl = -l;
		break;
	case MULT:
		v->v_ldbl = l * r;
		break;
	case DIV:
		if (r == 0.0) {
			/* division by 0 */
			error(139);
			if (t == FLOAT) {
				v->v_ldbl = l < 0 ? -FLT_MAX : FLT_MAX;
			} else if (t == DOUBLE) {
				v->v_ldbl = l < 0 ? -DBL_MAX : DBL_MAX;
			} else {
				v->v_ldbl = l < 0 ? -LDBL_MAX : LDBL_MAX;
			}
		} else {
			v->v_ldbl = l / r;
		}
		break;
	case PLUS:
		v->v_ldbl = l + r;
		break;
	case MINUS:
		v->v_ldbl = l - r;
		break;
	case LT:
		v->v_quad = l < r;
		break;
	case LE:
		v->v_quad = l <= r;
		break;
	case GE:
		v->v_quad = l >= r;
		break;
	case GT:
		v->v_quad = l > r;
		break;
	case EQ:
		v->v_quad = l == r;
		break;
	case NE:
		v->v_quad = l != r;
		break;
	default:
		lint_assert(0);
	}

	lint_assert(fpe || !isnan((double)v->v_ldbl));
	if (fpe || !finite((double)v->v_ldbl) ||
	    (t == FLOAT &&
	     (v->v_ldbl > FLT_MAX || v->v_ldbl < -FLT_MAX)) ||
	    (t == DOUBLE &&
	     (v->v_ldbl > DBL_MAX || v->v_ldbl < -DBL_MAX))) {
		/* floating point overflow detected, op %s */
		warning(142, modtab[tn->tn_op].m_name);
		if (t == FLOAT) {
			v->v_ldbl = v->v_ldbl < 0 ? -FLT_MAX : FLT_MAX;
		} else if (t == DOUBLE) {
			v->v_ldbl = v->v_ldbl < 0 ? -DBL_MAX : DBL_MAX;
		} else {
			v->v_ldbl = v->v_ldbl < 0 ? -LDBL_MAX: LDBL_MAX;
		}
	    fpe = 0;
	}

	return getcnode(tn->tn_type, v);
}


/*
 * Create a constant node for sizeof.
 */
tnode_t *
build_sizeof(type_t *tp)
{
	tspec_t	st;
#if SIZEOF_IS_ULONG
	st = ULONG;
#else
	st = UINT;
#endif
	return new_int_const_node(st, tsize(tp) / CHAR_BIT);
}

/*
 * Create a constant node for offsetof.
 */
tnode_t *
build_offsetof(type_t *tp, sym_t *sym)
{
	tspec_t	st;
#if SIZEOF_IS_ULONG
	st = ULONG;
#else
	st = UINT;
#endif
	tspec_t t = tp->t_tspec;
	if (t != STRUCT && t != UNION)
		/* unacceptable operand of '%s' */
		error(111, "offsetof");

	// XXX: wrong size, no checking for sym fixme
	return new_int_const_node(st, tsize(tp) / CHAR_BIT);
}

int64_t
tsize(type_t *tp)
{
	int	elem, elsz, flex;

	elem = 1;
	flex = 0;
	while (tp->t_tspec == ARRAY) {
		flex = 1;	/* allow c99 flex arrays [] [0] */
		elem *= tp->t_dim;
		tp = tp->t_subt;
	}
	if (elem == 0) {
		if (!flex) {
			/* cannot take size/alignment of incomplete type */
			error(143);
			elem = 1;
		}
	}
	switch (tp->t_tspec) {
	case FUNC:
		/* cannot take size/alignment of function */
		error(144);
		elsz = 1;
		break;
	case STRUCT:
	case UNION:
		if (incompl(tp)) {
			/* cannot take size/alignment of incomplete type */
			error(143);
			elsz = 1;
		} else {
			elsz = tp->t_str->size;
		}
		break;
	case ENUM:
		if (incompl(tp)) {
			/* cannot take size/alignment of incomplete type */
			warning(143);
		}
		/* FALLTHROUGH */
	default:
		if (tp->t_isfield) {
			/* cannot take size/alignment of bit-field */
			error(145);
		}
		if (tp->t_tspec == VOID) {
			/* cannot take size/alignment of void */
			error(146);
			elsz = 1;
		} else {
			elsz = size(tp->t_tspec);
			lint_assert(elsz > 0);
		}
		break;
	}

	/* XXX: type conversion is too late */
	return (int64_t)(elem * elsz);
}

/*
 */
tnode_t *
build_alignof(type_t *tp)
{
	tspec_t	st;

	switch (tp->t_tspec) {
	case ARRAY:
		break;

	case FUNC:
		/* cannot take size/alignment of function */
		error(144);
		return 0;

	case STRUCT:
	case UNION:
		if (incompl(tp)) {
			/* cannot take size/alignment of incomplete type */
			error(143);
			return 0;
		}
		break;
	case ENUM:
		break;
	default:
		if (tp->t_isfield) {
			/* cannot take size/alignment of bit-field */
			error(145);
			return 0;
		}
		if (tp->t_tspec == VOID) {
			/* cannot take size/alignment of void */
			error(146);
			return 0;
		}
		break;
	}

#if SIZEOF_IS_ULONG
	st = ULONG;
#else
	st = UINT;
#endif

	return new_int_const_node(st, (int64_t)getbound(tp) / CHAR_BIT);
}

/*
 * Type casts.
 */
tnode_t *
cast(tnode_t *tn, type_t *tp)
{
	tspec_t	nt, ot;

	if (tn == NULL)
		return NULL;

	tn = cconv(tn);

	nt = tp->t_tspec;
	ot = tn->tn_type->t_tspec;

	if (nt == VOID) {
		/*
		 * XXX ANSI C requires scalar types or void (Plauger & Brodie).
		 * But this seams really questionable.
		 */
	} else if (nt == UNION) {
		sym_t *m;
		str_t *str = tp->t_str;
		if (!Sflag) {
			/* union cast is a C9X feature */
			error(328);
			return NULL;
		}
		for (m = str->memb; m != NULL; m = m->s_next) {
			if (sametype(m->s_type, tn->tn_type)) {
				tn = getnode();
				tn->tn_op = CVT;
				tn->tn_type = tp;
				tn->tn_cast = 1;
				tn->tn_right = NULL;
				return tn;
			}
		}
		/* type '%s' is not a member of '%s' */
		error(329, type_name(tn->tn_type), type_name(tp));
		return NULL;
	} else if (nt == STRUCT || nt == ARRAY || nt == FUNC) {
		if (!Sflag || nt == ARRAY || nt == FUNC) {
			/* invalid cast expression */
			error(147);
			return NULL;
		}
	} else if (ot == STRUCT || ot == UNION) {
		/* invalid cast expression */
		error(147);
		return NULL;
	} else if (ot == VOID) {
		/* improper cast of void expression */
		error(148);
		return NULL;
	} else if (tspec_is_int(nt) && tspec_is_scalar(ot)) {
		/* ok */
	} else if (tspec_is_float(nt) && tspec_is_arith(ot)) {
		/* ok */
	} else if (nt == PTR && tspec_is_int(ot)) {
		/* ok */
	} else if (nt == PTR && ot == PTR) {
		if (!tp->t_subt->t_const && tn->tn_type->t_subt->t_const) {
			if (hflag)
				/* cast discards 'const' from ... */
				warning(275);
		}
	} else {
		/* invalid cast expression */
		error(147);
		return NULL;
	}

	tn = convert(CVT, 0, tp, tn);
	tn->tn_cast = 1;

	return tn;
}

/*
 * Create the node for a function argument.
 * All necessary conversions and type checks are done in funccall(), because
 * in funcarg() we have no information about expected argument types.
 */
tnode_t *
funcarg(tnode_t *args, tnode_t *arg)
{
	tnode_t	*ntn;

	/*
	 * If there was a serious error in the expression for the argument,
	 * create a dummy argument so the positions of the remaining arguments
	 * will not change.
	 */
	if (arg == NULL)
		arg = new_int_const_node(INT, (int64_t)0);

	ntn = new_tnode(PUSH, arg->tn_type, arg, args);

	return ntn;
}

/*
 * Create the node for a function call. Also check types of
 * function arguments and insert conversions, if necessary.
 */
tnode_t *
funccall(tnode_t *func, tnode_t *args)
{
	tnode_t	*ntn;
	op_t	fcop;

	if (func == NULL)
		return NULL;

	if (func->tn_op == NAME && func->tn_type->t_tspec == FUNC) {
		fcop = CALL;
	} else {
		fcop = ICALL;
	}

	/*
	 * after cconv() func will always be a pointer to a function
	 * if it is a valid function designator.
	 */
	func = cconv(func);

	if (func->tn_type->t_tspec != PTR ||
	    func->tn_type->t_subt->t_tspec != FUNC) {
		/* illegal function (type %s) */
		error(149, type_name(func->tn_type));
		return NULL;
	}

	args = check_function_arguments(func->tn_type->t_subt, args);

	ntn = new_tnode(fcop, func->tn_type->t_subt->t_subt, func, args);

	return ntn;
}

/*
 * Check types of all function arguments and insert conversions,
 * if necessary.
 */
static tnode_t *
check_function_arguments(type_t *ftp, tnode_t *args)
{
	tnode_t	*arg;
	sym_t	*asym;
	tspec_t	at;
	int	narg, npar, n, i;

	/* get # of args in the prototype */
	npar = 0;
	for (asym = ftp->t_args; asym != NULL; asym = asym->s_next)
		npar++;

	/* get # of args in function call */
	narg = 0;
	for (arg = args; arg != NULL; arg = arg->tn_right)
		narg++;

	asym = ftp->t_args;
	if (ftp->t_proto && npar != narg && !(ftp->t_vararg && npar < narg)) {
		/* argument mismatch: %d arg%s passed, %d expected */
		error(150, narg, narg > 1 ? "s" : "", npar);
		asym = NULL;
	}

	for (n = 1; n <= narg; n++) {

		/*
		 * The rightmost argument is at the top of the argument
		 * subtree.
		 */
		for (i = narg, arg = args; i > n; i--, arg = arg->tn_right)
			continue;

		/* some things which are always not allowed */
		if ((at = arg->tn_left->tn_type->t_tspec) == VOID) {
			/* void expressions may not be arguments, arg #%d */
			error(151, n);
			return NULL;
		} else if ((at == STRUCT || at == UNION) &&
			   incompl(arg->tn_left->tn_type)) {
			/* argument cannot have unknown size, arg #%d */
			error(152, n);
			return NULL;
		} else if (tspec_is_int(at) &&
			   arg->tn_left->tn_type->t_isenum &&
			   incompl(arg->tn_left->tn_type)) {
			/* argument cannot have unknown size, arg #%d */
			warning(152, n);
		}

		/* class conversions (arg in value context) */
		arg->tn_left = cconv(arg->tn_left);

		if (asym != NULL) {
			arg->tn_left = check_prototype_argument(
			    n, asym->s_type, arg->tn_left);
		} else {
			arg->tn_left = promote(NOOP, 1, arg->tn_left);
		}
		arg->tn_type = arg->tn_left->tn_type;

		if (asym != NULL)
			asym = asym->s_next;
	}

	return args;
}

/*
 * Compare the type of an argument with the corresponding type of a
 * prototype parameter. If it is a valid combination, but both types
 * are not the same, insert a conversion to convert the argument into
 * the type of the parameter.
 */
static tnode_t *
check_prototype_argument(
	int	n,		/* pos of arg */
	type_t	*tp,		/* expected type (from prototype) */
	tnode_t	*tn)		/* argument */
{
	tnode_t	*ln;
	int	dowarn;

	ln = xcalloc(1, sizeof (tnode_t));
	ln->tn_type = tduptyp(tp);
	ln->tn_type->t_const = 0;
	ln->tn_lvalue = 1;
	if (typeok(FARG, n, ln, tn)) {
		if (!eqtype(tp, tn->tn_type, 1, 0, (dowarn = 0, &dowarn)) || dowarn)
			tn = convert(FARG, n, tp, tn);
	}
	free(ln);
	return tn;
}

/*
 * Return the value of an integral constant expression.
 * If the expression is not constant or its type is not an integer
 * type, an error message is printed.
 */
val_t *
constant(tnode_t *tn, int required)
{
	val_t	*v;

	if (tn != NULL)
		tn = cconv(tn);
	if (tn != NULL)
		tn = promote(NOOP, 0, tn);

	v = xcalloc(1, sizeof (val_t));

	if (tn == NULL) {
		lint_assert(nerr != 0);
		v->v_tspec = INT;
		v->v_quad = 1;
		return v;
	}

	v->v_tspec = tn->tn_type->t_tspec;

	if (tn->tn_op == CON) {
		lint_assert(tn->tn_type->t_tspec == tn->tn_val->v_tspec);
		if (tspec_is_int(tn->tn_val->v_tspec)) {
			v->v_ansiu = tn->tn_val->v_ansiu;
			v->v_quad = tn->tn_val->v_quad;
			return v;
		}
		v->v_quad = tn->tn_val->v_ldbl;
	} else {
		v->v_quad = 1;
	}

	if (required)
		/* integral constant expression expected */
		error(55);
	else
		/* variable array dimension is a C99/GCC extension */
		c99ism(318);

	if (!tspec_is_int(v->v_tspec))
		v->v_tspec = INT;

	return v;
}

/*
 * Perform some tests on expressions which can't be done in build() and
 * functions called by build(). These tests must be done here because
 * we need some information about the context in which the operations
 * are performed.
 * After all tests are performed, expr() frees the memory which is used
 * for the expression.
 */
void
expr(tnode_t *tn, int vctx, int tctx, int dofreeblk)
{

	lint_assert(tn != NULL || nerr != 0);

	if (tn == NULL) {
		tfreeblk();
		return;
	}

	/* expr() is also called in global initialisations */
	if (dcs->d_ctx != EXTERN)
		check_statement_reachable();

	check_expr_misc(tn, vctx, tctx, !tctx, 0, 0, 0);
	if (tn->tn_op == ASSIGN) {
		if (hflag && tctx)
			/* assignment in conditional context */
			warning(159);
	} else if (tn->tn_op == CON) {
		if (hflag && tctx && !constcond_flag)
			/* constant in conditional context */
			warning(161);
	}
	if (!modtab[tn->tn_op].m_sideeff) {
		/*
		 * for left operands of COMMA this warning is already
		 * printed
		 */
		if (tn->tn_op != COMMA && !vctx && !tctx)
			check_null_effect(tn);
	}
	if (dflag)
		display_expression(tn, 0);

	/* free the tree memory */
	if (dofreeblk)
		tfreeblk();
}

static void
check_null_effect(tnode_t *tn)
{

	if (!hflag)
		return;

	while (!modtab[tn->tn_op].m_sideeff) {
		if (tn->tn_op == CVT && tn->tn_type->t_tspec == VOID) {
			tn = tn->tn_left;
		} else if (tn->tn_op == LOGAND || tn->tn_op == LOGOR) {
			/*
			 * && and || have a side effect if the right operand
			 * has a side effect.
			 */
			tn = tn->tn_right;
		} else if (tn->tn_op == QUEST) {
			/*
			 * ? has a side effect if at least one of its right
			 * operands has a side effect
			 */
			tn = tn->tn_right;
		} else if (tn->tn_op == COLON || tn->tn_op == COMMA) {
			/*
			 * : has a side effect if at least one of its operands
			 * has a side effect
			 */
			if (modtab[tn->tn_left->tn_op].m_sideeff) {
				tn = tn->tn_left;
			} else if (modtab[tn->tn_right->tn_op].m_sideeff) {
				tn = tn->tn_right;
			} else {
				break;
			}
		} else {
			break;
		}
	}
	if (!modtab[tn->tn_op].m_sideeff)
		/* expression has null effect */
		warning(129);
}

/*
 * Dump an expression to stdout
 * only used for debugging
 */
static void
display_expression(tnode_t *tn, int offs)
{
	uint64_t uq;

	if (tn == NULL) {
		(void)printf("%*s%s\n", offs, "", "NULL");
		return;
	}
	(void)printf("%*sop %s  ", offs, "", modtab[tn->tn_op].m_name);

	if (tn->tn_op == NAME) {
		(void)printf("%s: %s ",
		    tn->tn_sym->s_name,
		    storage_class_name(tn->tn_sym->s_scl));
	} else if (tn->tn_op == CON && tspec_is_float(tn->tn_type->t_tspec)) {
		(void)printf("%#g ", (double)tn->tn_val->v_ldbl);
	} else if (tn->tn_op == CON && tspec_is_int(tn->tn_type->t_tspec)) {
		uq = tn->tn_val->v_quad;
		(void)printf("0x %08lx %08lx ",
		    (long)(uq >> 32) & 0xffffffffl,
		    (long)uq & 0xffffffffl);
	} else if (tn->tn_op == CON) {
		lint_assert(tn->tn_type->t_tspec == PTR);
		(void)printf("0x%0*lx ", (int)(sizeof (void *) * CHAR_BIT / 4),
			     (u_long)tn->tn_val->v_quad);
	} else if (tn->tn_op == STRING) {
		if (tn->tn_string->st_tspec == CHAR) {
			(void)printf("\"%s\"", tn->tn_string->st_cp);
		} else {
			char	*s;
			size_t	n;
			n = MB_CUR_MAX * (tn->tn_string->st_len + 1);
			s = xmalloc(n);
			(void)wcstombs(s, tn->tn_string->st_wcp, n);
			(void)printf("L\"%s\"", s);
			free(s);
		}
		(void)printf(" ");
	} else if (tn->tn_op == FSEL) {
		(void)printf("o=%d, l=%d ", tn->tn_type->t_foffs,
			     tn->tn_type->t_flen);
	}
	(void)printf("%s\n", ttos(tn->tn_type));
	if (tn->tn_op == NAME || tn->tn_op == CON || tn->tn_op == STRING)
		return;
	display_expression(tn->tn_left, offs + 2);
	if (modtab[tn->tn_op].m_binary ||
	    (tn->tn_op == PUSH && tn->tn_right != NULL)) {
		display_expression(tn->tn_right, offs + 2);
	}
}

/*
 * Called by expr() to recursively perform some tests.
 */
/* ARGSUSED */
void
check_expr_misc(tnode_t *tn, int vctx, int tctx, int eqwarn, int fcall, int rvdisc,
	int szof)
{
	tnode_t	*ln, *rn;
	mod_t	*mp;
	int	nrvdisc, cvctx, ctctx;
	op_t	op;
	scl_t	sc;
	dinfo_t	*di;

	if (tn == NULL)
		return;

	ln = tn->tn_left;
	rn = tn->tn_right;
	mp = &modtab[op = tn->tn_op];

	switch (op) {
	case AMPER:
		if (ln->tn_op == NAME && (reached || rchflg)) {
			if (!szof)
				mark_as_set(ln->tn_sym);
			mark_as_used(ln->tn_sym, fcall, szof);
		}
		if (ln->tn_op == STAR && ln->tn_left->tn_op == PLUS)
			/* check the range of array indices */
			check_array_index(ln->tn_left, 1);
		break;
	case LOAD:
		if (ln->tn_op == STAR && ln->tn_left->tn_op == PLUS)
			/* check the range of array indices */
			check_array_index(ln->tn_left, 0);
		/* FALLTHROUGH */
	case PUSH:
	case INCBEF:
	case DECBEF:
	case INCAFT:
	case DECAFT:
	case ADDASS:
	case SUBASS:
	case MULASS:
	case DIVASS:
	case MODASS:
	case ANDASS:
	case ORASS:
	case XORASS:
	case SHLASS:
	case SHRASS:
	case REAL:
	case IMAG:
		if (ln->tn_op == NAME && (reached || rchflg)) {
			sc = ln->tn_sym->s_scl;
			/*
			 * Look if there was a asm statement in one of the
			 * compound statements we are in. If not, we don't
			 * print a warning.
			 */
			for (di = dcs; di != NULL; di = di->d_next) {
				if (di->d_asm)
					break;
			}
			if (sc != EXTERN && sc != STATIC &&
			    !ln->tn_sym->s_set && !szof && di == NULL) {
				/* %s may be used before set */
				warning(158, ln->tn_sym->s_name);
				mark_as_set(ln->tn_sym);
			}
			mark_as_used(ln->tn_sym, 0, 0);
		}
		break;
	case ASSIGN:
		if (ln->tn_op == NAME && !szof && (reached || rchflg)) {
			mark_as_set(ln->tn_sym);
			if (ln->tn_sym->s_scl == EXTERN)
				outusg(ln->tn_sym);
		}
		if (ln->tn_op == STAR && ln->tn_left->tn_op == PLUS)
			/* check the range of array indices */
			check_array_index(ln->tn_left, 0);
		break;
	case CALL:
		if (ln->tn_op != AMPER || ln->tn_left->tn_op != NAME)
			LERROR("check_expr_misc(op=%s != %s || %s != %s)",
			    getopname(ln->tn_op), getopname(AMPER),
			    getopname(ln->tn_left->tn_op), getopname(NAME));
		if (!szof)
			outcall(tn, vctx || tctx, rvdisc);
		break;
	case EQ:
		if (hflag && eqwarn)
			/* operator '==' found where '=' was expected */
			warning(160);
		break;
	case CON:
	case NAME:
	case STRING:
		return;
		/* LINTED206: (enumeration values not handled in switch) */
	case OR:
	case XOR:
	case NE:
	case GE:
	case GT:
	case LE:
	case LT:
	case SHR:
	case SHL:
	case MINUS:
	case PLUS:
	case MOD:
	case DIV:
	case MULT:
	case STAR:
	case UMINUS:
	case UPLUS:
	case DEC:
	case INC:
	case COMPL:
	case NOT:
	case POINT:
	case ARROW:
	case NOOP:
	case AND:
	case FARG:
	case CASE:
	case INIT:
	case RETURN:
	case ICALL:
	case CVT:
	case COMMA:
	case FSEL:
	case COLON:
	case QUEST:
	case LOGOR:
	case LOGAND:
		break;
	}

	cvctx = mp->m_vctx;
	ctctx = mp->m_tctx;
	/*
	 * values of operands of ':' are not used if the type of at least
	 * one of the operands (for gcc compatibility) is void
	 * XXX test/value context of QUEST should probably be used as
	 * context for both operands of COLON
	 */
	if (op == COLON && tn->tn_type->t_tspec == VOID)
		cvctx = ctctx = 0;
	nrvdisc = op == CVT && tn->tn_type->t_tspec == VOID;
	check_expr_misc(ln, cvctx, ctctx, mp->m_eqwarn, op == CALL, nrvdisc, szof);

	switch (op) {
	case PUSH:
		if (rn != NULL)
			check_expr_misc(rn, 0, 0, mp->m_eqwarn, 0, 0, szof);
		break;
	case LOGAND:
	case LOGOR:
		check_expr_misc(rn, 0, 1, mp->m_eqwarn, 0, 0, szof);
		break;
	case COLON:
		check_expr_misc(rn, cvctx, ctctx, mp->m_eqwarn, 0, 0, szof);
		break;
	case COMMA:
		check_expr_misc(rn, vctx, tctx, mp->m_eqwarn, 0, 0, szof);
		break;
	default:
		if (mp->m_binary)
			check_expr_misc(rn, 1, 0, mp->m_eqwarn, 0, 0, szof);
		break;
	}

}

/*
 * Checks the range of array indices, if possible.
 * amper is set if only the address of the element is used. This
 * means that the index is allowed to refer to the first element
 * after the array.
 */
static void
check_array_index(tnode_t *tn, int amper)
{
	int	dim;
	tnode_t	*ln, *rn;
	int	elsz;
	int64_t	con;

	ln = tn->tn_left;
	rn = tn->tn_right;

	/* We can only check constant indices. */
	if (rn->tn_op != CON)
		return;

	/* Return if the left node does not stem from an array. */
	if (ln->tn_op != AMPER)
		return;
	if (ln->tn_left->tn_op != STRING && ln->tn_left->tn_op != NAME)
		return;
	if (ln->tn_left->tn_type->t_tspec != ARRAY)
		return;

	/*
	 * For incomplete array types, we can print a warning only if
	 * the index is negative.
	 */
	if (incompl(ln->tn_left->tn_type) && rn->tn_val->v_quad >= 0)
		return;

	/* Get the size of one array element */
	if ((elsz = length(ln->tn_type->t_subt, NULL)) == 0)
		return;
	elsz /= CHAR_BIT;

	/* Change the unit of the index from bytes to element size. */
	if (tspec_is_uint(rn->tn_type->t_tspec)) {
		con = (uint64_t)rn->tn_val->v_quad / elsz;
	} else {
		con = rn->tn_val->v_quad / elsz;
	}

	dim = ln->tn_left->tn_type->t_dim + (amper ? 1 : 0);

	if (!tspec_is_uint(rn->tn_type->t_tspec) && con < 0) {
		/* array subscript cannot be negative: %ld */
		warning(167, (long)con);
	} else if (dim > 0 && (uint64_t)con >= (uint64_t)dim) {
		/* array subscript cannot be > %d: %ld */
		warning(168, dim - 1, (long)con);
	}
}

/*
 * Check for ordered comparisons of unsigned values with 0.
 */
static void
check_integer_comparison(op_t op, tnode_t *ln, tnode_t *rn)
{
	tspec_t	lt, rt;
	mod_t	*mp;

	lt = ln->tn_type->t_tspec;
	rt = rn->tn_type->t_tspec;
	mp = &modtab[op];

	if (ln->tn_op != CON && rn->tn_op != CON)
		return;

	if (!tspec_is_int(lt) || !tspec_is_int(rt))
		return;

	if ((hflag || pflag) && lt == CHAR && rn->tn_op == CON &&
	    (rn->tn_val->v_quad < 0 ||
	     rn->tn_val->v_quad > (int)~(~0U << (CHAR_BIT - 1)))) {
		/* nonportable character comparison, op %s */
		warning(230, mp->m_name);
		return;
	}
	if ((hflag || pflag) && rt == CHAR && ln->tn_op == CON &&
	    (ln->tn_val->v_quad < 0 ||
	     ln->tn_val->v_quad > (int)~(~0U << (CHAR_BIT - 1)))) {
		/* nonportable character comparison, op %s */
		warning(230, mp->m_name);
		return;
	}
	if (tspec_is_uint(lt) && !tspec_is_uint(rt) &&
	    rn->tn_op == CON && rn->tn_val->v_quad <= 0) {
		if (rn->tn_val->v_quad < 0) {
			/* comparison of %s with %s, op %s */
			warning(162, type_name(ln->tn_type),
			    "negative constant", mp->m_name);
		} else if (op == LT || op == GE || (hflag && op == LE)) {
			/* comparison of %s with %s, op %s */
			warning(162, type_name(ln->tn_type), "0", mp->m_name);
		}
		return;
	}
	if (tspec_is_uint(rt) && !tspec_is_uint(lt) &&
	    ln->tn_op == CON && ln->tn_val->v_quad <= 0) {
		if (ln->tn_val->v_quad < 0) {
			/* comparison of %s with %s, op %s */
			warning(162, "negative constant",
			    type_name(rn->tn_type), mp->m_name);
		} else if (op == GT || op == LE || (hflag && op == GE)) {
			/* comparison of %s with %s, op %s */
			warning(162, "0", type_name(rn->tn_type), mp->m_name);
		}
		return;
	}
}

/*
 * Takes an expression an returns 0 if this expression can be used
 * for static initialisation, otherwise -1.
 *
 * Constant initialisation expressions must be constant or an address
 * of a static object with an optional offset. In the first case,
 * the result is returned in *offsp. In the second case, the static
 * object is returned in *symp and the offset in *offsp.
 *
 * The expression can consist of PLUS, MINUS, AMPER, NAME, STRING and
 * CON. Type conversions are allowed if they do not change binary
 * representation (including width).
 */
int
conaddr(tnode_t *tn, sym_t **symp, ptrdiff_t *offsp)
{
	sym_t	*sym;
	ptrdiff_t offs1, offs2;
	tspec_t	t, ot;

	switch (tn->tn_op) {
	case MINUS:
		if (tn->tn_right->tn_op == CVT)
			return conaddr(tn->tn_right, symp, offsp);
		else if (tn->tn_right->tn_op != CON)
			return -1;
		/* FALLTHROUGH */
	case PLUS:
		offs1 = offs2 = 0;
		if (tn->tn_left->tn_op == CON) {
			offs1 = (ptrdiff_t)tn->tn_left->tn_val->v_quad;
			if (conaddr(tn->tn_right, &sym, &offs2) == -1)
				return -1;
		} else if (tn->tn_right->tn_op == CON) {
			offs2 = (ptrdiff_t)tn->tn_right->tn_val->v_quad;
			if (tn->tn_op == MINUS)
				offs2 = -offs2;
			if (conaddr(tn->tn_left, &sym, &offs1) == -1)
				return -1;
		} else {
			return -1;
		}
		*symp = sym;
		*offsp = offs1 + offs2;
		break;
	case AMPER:
		if (tn->tn_left->tn_op == NAME) {
			*symp = tn->tn_left->tn_sym;
			*offsp = 0;
		} else if (tn->tn_left->tn_op == STRING) {
			/*
			 * If this would be the front end of a compiler we
			 * would return a label instead of 0.
			 */
			*offsp = 0;
		}
		break;
	case CVT:
		t = tn->tn_type->t_tspec;
		ot = tn->tn_left->tn_type->t_tspec;
		if ((!tspec_is_int(t) && t != PTR) ||
		    (!tspec_is_int(ot) && ot != PTR)) {
			return -1;
		}
#ifdef notdef
		/*
		 * consider:
		 *	struct foo {
		 *		unsigned char a;
		 *	} f = {
		 *		(u_char)(u_long)(&(((struct foo *)0)->a))
		 *	};
		 * since psize(u_long) != psize(u_char) this fails.
		 */
		else if (psize(t) != psize(ot))
			return -1;
#endif
		if (conaddr(tn->tn_left, symp, offsp) == -1)
			return -1;
		break;
	default:
		return -1;
	}
	return 0;
}

/*
 * Concatenate two string constants.
 */
strg_t *
cat_strings(strg_t *strg1, strg_t *strg2)
{
	size_t	len1, len2, len;

	if (strg1->st_tspec != strg2->st_tspec) {
		/* cannot concatenate wide and regular string literals */
		error(292);
		return strg1;
	}

	len1 = strg1->st_len;
	len2 = strg2->st_len + 1;	/* + NUL */
	len = len1 + len2;

#define COPY(F) \
    do { \
	strg1->F = xrealloc(strg1->F, len * sizeof(*strg1->F)); \
	(void)memcpy(strg1->F + len1, strg2->F, len2 * sizeof(*strg1->F)); \
	free(strg2->F); \
    } while (/*CONSTCOND*/0)

	if (strg1->st_tspec == CHAR)
		COPY(st_cp);
	else
		COPY(st_wcp);

	strg1->st_len = len - 1; /* - NUL */
	free(strg2);

	return strg1;
}

/*
 * Print a warning if the given node has operands which should be
 * parenthesized.
 *
 * XXX Does not work if an operand is a constant expression. Constant
 * expressions are already folded.
 */
static void
check_precedence_confusion(tnode_t *tn)
{
	tnode_t	*ln, *rn;
	op_t	lop, rop = NOOP;
	int	lparn, rparn = 0;
	mod_t	*mp;
	int	dowarn;

	if (!hflag)
		return;

	mp = &modtab[tn->tn_op];

	lparn = 0;
	for (ln = tn->tn_left; ln->tn_op == CVT; ln = ln->tn_left)
		lparn |= ln->tn_parenthesized;
	lparn |= ln->tn_parenthesized;
	lop = ln->tn_op;

	if (mp->m_binary) {
		rparn = 0;
		for (rn = tn->tn_right; tn->tn_op == CVT; rn = rn->tn_left)
			rparn |= rn->tn_parenthesized;
		rparn |= rn->tn_parenthesized;
		rop = rn->tn_op;
	}

	dowarn = 0;

	switch (tn->tn_op) {
	case SHL:
	case SHR:
		if (!lparn && (lop == PLUS || lop == MINUS)) {
			dowarn = 1;
		} else if (!rparn && (rop == PLUS || rop == MINUS)) {
			dowarn = 1;
		}
		break;
	case LOGOR:
		if (!lparn && lop == LOGAND) {
			dowarn = 1;
		} else if (!rparn && rop == LOGAND) {
			dowarn = 1;
		}
		break;
	case AND:
	case XOR:
	case OR:
		if (!lparn && lop != tn->tn_op) {
			if (lop == PLUS || lop == MINUS) {
				dowarn = 1;
			} else if (lop == AND || lop == XOR) {
				dowarn = 1;
			}
		}
		if (!dowarn && !rparn && rop != tn->tn_op) {
			if (rop == PLUS || rop == MINUS) {
				dowarn = 1;
			} else if (rop == AND || rop == XOR) {
				dowarn = 1;
			}
		}
		break;
		/* LINTED206: (enumeration values not handled in switch) */
	case DECAFT:
	case XORASS:
	case SHLASS:
	case NOOP:
	case ARROW:
	case ORASS:
	case POINT:
	case NAME:
	case NOT:
	case COMPL:
	case CON:
	case INC:
	case STRING:
	case DEC:
	case INCBEF:
	case DECBEF:
	case INCAFT:
	case FSEL:
	case CALL:
	case COMMA:
	case CVT:
	case ICALL:
	case LOAD:
	case PUSH:
	case RETURN:
	case INIT:
	case CASE:
	case FARG:
	case SUBASS:
	case ADDASS:
	case MODASS:
	case DIVASS:
	case MULASS:
	case ASSIGN:
	case COLON:
	case QUEST:
	case LOGAND:
	case NE:
	case EQ:
	case GE:
	case GT:
	case LE:
	case LT:
	case MINUS:
	case PLUS:
	case MOD:
	case DIV:
	case MULT:
	case AMPER:
	case STAR:
	case UMINUS:
	case SHRASS:
	case UPLUS:
	case ANDASS:
	case REAL:
	case IMAG:
		break;
	}

	if (dowarn) {
		/* precedence confusion possible: parenthesize! */
		warning(169);
	}

}