File: [cvs.NetBSD.org] / src / usr.bin / xlint / lint1 / tree.c (download)
Revision 1.271, Tue Apr 6 21:59:58 2021 UTC (2 years, 11 months ago) by rillig
Branch: MAIN
Changes since 1.270: +5 -5
lines
lint: for shift in C99 mode, do not warn about difference to pre-C90
C99 is too far away from traditional C to make this warning useful.
There are 3 different situations in which this warning is generated:
For '1 << (unsigned char)1', the result type is 'unsigned int' in
traditional C. The result type is unsigned because at least 1 of the
operators is unsigned, and it is 'unsigned int' because the usual
arithmetic promotions are applied.
For '1 >> (long)1', as well as for '1 << (long)1', the result type is
'long' in traditional C since the usual arithmetic promotions are
applied.
Omitting this warning in C99 mode reduces the amount of lint warnings in
a typical NetBSD release build by approximately 6800 of 107000 total.
|
/* $NetBSD: tree.c,v 1.271 2021/04/06 21:59:58 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.271 2021/04/06 21:59:58 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"
static tnode_t *expr_new_integer_constant(tspec_t, int64_t);
static void check_pointer_comparison(op_t,
const tnode_t *, const tnode_t *);
static bool check_assign_types_compatible(op_t, int,
const tnode_t *, const tnode_t *);
static void check_bad_enum_operation(op_t,
const tnode_t *, const tnode_t *);
static void check_enum_type_mismatch(op_t, int,
const tnode_t *, const tnode_t *);
static void check_enum_int_mismatch(op_t, int,
const tnode_t *, const 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, const type_t *, tspec_t,
const type_t *, tspec_t);
static void warn_incompatible_pointers(const mod_t *,
const type_t *, const type_t *);
static bool has_constant_member(const 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_address(tnode_t *, bool);
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(const tnode_t *);
static void display_expression(const tnode_t *, int);
static void check_array_index(tnode_t *, bool);
static void check_integer_comparison(op_t, tnode_t *, tnode_t *);
static void check_precedence_confusion(tnode_t *);
extern sig_atomic_t fpe;
static const char *
op_name(op_t op)
{
return modtab[op].m_name;
}
#ifdef DEBUG
void
debug_node(const tnode_t *tn, int indent)
{
op_t op;
if (tn == NULL) {
printf("%*s" "null\n", indent, "");
return;
}
op = tn->tn_op;
printf("%*s%s with type '%s'%s%s",
2 * indent, "",
op == CVT && !tn->tn_cast ? "convert" : op_name(op),
type_name(tn->tn_type), tn->tn_lvalue ? ", lvalue" : "",
tn->tn_parenthesized ? ", parenthesized" : "");
if (op == NAME)
printf(" %s\n", tn->tn_sym->s_name);
else if (op == CON && is_floating(tn->tn_type->t_tspec))
printf(", value %Lg", tn->tn_val->v_ldbl);
else if (op == CON && is_uinteger(tn->tn_type->t_tspec))
printf(", value %llu\n", (unsigned long long)tn->tn_val->v_quad);
else if (op == CON && is_integer(tn->tn_type->t_tspec))
printf(", value %lld\n", (long long)tn->tn_val->v_quad);
else if (op == CON)
printf(", unknown value\n");
else if (op == STRING)
printf(", length %zu\n", tn->tn_string->st_len);
else {
printf("\n");
debug_node(tn->tn_left, indent + 1);
if (modtab[op].m_binary || tn->tn_right != NULL)
debug_node(tn->tn_right, indent + 1);
}
}
#endif
/* Build 'pointer to tp', 'array of tp' or 'function returning tp'. */
type_t *
derive_type(type_t *tp, tspec_t t)
{
type_t *tp2;
tp2 = getblk(sizeof(*tp2));
tp2->t_tspec = t;
tp2->t_subt = tp;
return tp2;
}
/*
* Build 'pointer to tp', 'array of tp' or 'function returning tp'. The
* memory is freed at the end of the current expression.
*/
type_t *
expr_derive_type(type_t *tp, tspec_t t)
{
type_t *tp2;
tp2 = expr_zalloc(sizeof(*tp2));
tp2->t_tspec = t;
tp2->t_subt = tp;
return tp2;
}
/*
* Create a node for a constant.
*/
tnode_t *
expr_new_constant(type_t *tp, val_t *v)
{
tnode_t *n;
n = expr_zalloc_tnode();
n->tn_op = CON;
n->tn_type = tp;
n->tn_val = expr_zalloc(sizeof(*n->tn_val));
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;
}
static tnode_t *
expr_new_integer_constant(tspec_t t, int64_t q)
{
tnode_t *n;
n = expr_zalloc_tnode();
n->tn_op = CON;
n->tn_type = gettyp(t);
n->tn_val = expr_zalloc(sizeof(*n->tn_val));
n->tn_val->v_tspec = t;
n->tn_val->v_quad = q;
return n;
}
static void
fallback_symbol(sym_t *sym)
{
if (fallback_symbol_strict_bool(sym))
return;
if (block_level > 0 && (strcmp(sym->s_name, "__FUNCTION__") == 0 ||
strcmp(sym->s_name, "__PRETTY_FUNCTION__") == 0)) {
/* __FUNCTION__/__PRETTY_FUNCTION__ is a GCC extension */
gnuism(316);
sym->s_type = derive_type(gettyp(CHAR), PTR);
sym->s_type->t_const = true;
return;
}
if (block_level > 0 && strcmp(sym->s_name, "__func__") == 0) {
if (!Sflag)
/* __func__ is a C9X feature */
warning(317);
sym->s_type = derive_type(gettyp(CHAR), PTR);
sym->s_type->t_const = true;
return;
}
/* '%s' undefined */
error(99, sym->s_name);
}
/*
* Create a node for a name (symbol table entry).
* follow_token is the token which follows the name.
*/
tnode_t *
new_name_node(sym_t *sym, int follow_token)
{
tnode_t *n;
if (sym->s_scl == NOSCL) {
sym->s_scl = EXTERN;
sym->s_def = DECL;
if (follow_token == 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 = derive_type(sym->s_type, FUNC);
} else {
fallback_symbol(sym);
}
}
lint_assert(sym->s_kind == FVFT || sym->s_kind == FMEMBER);
n = expr_zalloc_tnode();
n->tn_type = sym->s_type;
if (sym->s_scl != CTCONST) {
n->tn_op = NAME;
n->tn_sym = sym;
if (sym->s_kind == FVFT && sym->s_type->t_tspec != FUNC)
n->tn_lvalue = true;
} else {
n->tn_op = CON;
n->tn_val = expr_zalloc(sizeof(*n->tn_val));
*n->tn_val = sym->s_value;
}
return n;
}
tnode_t *
new_string_node(strg_t *strg)
{
size_t len;
tnode_t *n;
len = strg->st_len;
n = expr_zalloc_tnode();
n->tn_op = STRING;
n->tn_type = expr_derive_type(gettyp(strg->st_tspec), ARRAY);
n->tn_type->t_dim = len + 1;
n->tn_lvalue = true;
n->tn_string = expr_zalloc(sizeof(*n->tn_string));
n->tn_string->st_tspec = strg->st_tspec;
n->tn_string->st_len = len;
if (strg->st_tspec == CHAR) {
n->tn_string->st_cp = expr_zalloc(len + 1);
(void)memcpy(n->tn_string->st_cp, strg->st_cp, len + 1);
free(strg->st_cp);
} else {
size_t size = (len + 1) * sizeof(*n->tn_string->st_wcp);
n->tn_string->st_wcp = expr_zalloc(size);
(void)memcpy(n->tn_string->st_wcp, strg->st_wcp, size);
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)
{
struct_or_union *str;
type_t *tp;
sym_t *sym, *csym;
bool 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) {
/* type '%s' does not have member '%s' */
error(101, type_name(msym->s_type), msym->s_name);
rmsym(msym);
msym->s_kind = FMEMBER;
msym->s_scl = MOS;
msym->s_styp = expr_zalloc(sizeof(*msym->s_styp));
msym->s_styp->sou_tag = expr_zalloc(
sizeof(*msym->s_styp->sou_tag));
msym->s_styp->sou_tag->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 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 false if there are struct/union members with the same
* name and different types and/or offsets.
*/
eq = true;
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) {
bool 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 = false;
break;
}
w = false;
eq = eqtype(csym->s_type, sym->s_type,
false, false, &w) && !w;
if (!eq)
break;
if (csym->s_bitfield != sym->s_bitfield) {
eq = false;
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 = false;
break;
}
if (tp1->t_foffs != tp2->t_foffs) {
eq = false;
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 {
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, op_name(op));
}
}
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)
{
const mod_t *mp;
tnode_t *ntn;
type_t *rettp;
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_left_value_context || mp->m_left_test_context)
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_comparison)
check_integer_comparison(op, ln, rn);
/*
* Promote the left operand if it is in a test or value context
*/
if (mp->m_left_value_context || mp->m_left_test_context)
ln = promote(op, false, 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, false, 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_warn_if_left_unsigned_in_c90 &&
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 = false;
}
if (mp->m_warn_if_right_unsigned_in_c90 &&
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 = false;
}
/* Make sure both operands are of the same type */
if (mp->m_balance_operands || (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 ADDR:
ntn = build_address(ln, false);
break;
case INDIR:
ntn = new_tnode(INDIR, 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:
rettp = mp->m_returns_bool
? gettyp(Tflag ? BOOL : INT) : ln->tn_type;
lint_assert(mp->m_binary || rn == NULL);
ntn = new_tnode(op, rettp, ln, rn);
break;
}
/* Return if an error occurred. */
if (ntn == NULL)
return NULL;
/* Print a warning if precedence confusion is possible */
if (mp->m_possible_precedence_confusion)
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_left_test_context) {
if (ln->tn_op == CON ||
((mp->m_binary && op != QUEST) && rn->tn_op == CON)) {
if (hflag && !constcond_flag &&
!ln->tn_system_dependent)
/* constant in conditional context */
warning(161);
}
}
/* Fold if the operator requires it */
if (mp->m_fold_constant_operands) {
if (ln->tn_op == CON && (!mp->m_binary || rn->tn_op == CON)) {
if (mp->m_left_test_context) {
ntn = fold_test(ntn);
} else if (is_floating(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 != 0
? 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.
*
* C99 6.3 "Conversions"
* C99 6.3.2 "Other operands"
* C99 6.3.2.1 "Lvalues, arrays, and function designators"
*/
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) {
/* XXX print correct operator */
/* %soperand of '%s' must be lvalue */
gnuism(114, "", op_name(ADDR));
}
tn = new_tnode(ADDR,
expr_derive_type(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_address(tn, true);
/* lvalue to rvalue */
if (tn->tn_lvalue) {
tp = expr_dup_type(tn->tn_type);
tp->t_const = tp->t_volatile = false;
tn = new_tnode(LOAD, tp, tn, NULL);
}
return tn;
}
const tnode_t *
before_conversion(const tnode_t *tn)
{
while (tn->tn_op == CVT && !tn->tn_cast)
tn = tn->tn_left;
return tn;
}
static bool
is_null_pointer(const tnode_t *tn)
{
tspec_t t = tn->tn_type->t_tspec;
return ((t == PTR && tn->tn_type->t_subt->t_tspec == VOID) ||
is_integer(t))
&& (tn->tn_op == CON && tn->tn_val->v_quad == 0);
}
static bool
typeok_incdec(op_t op, const tnode_t *tn, const type_t *tp)
{
/* operand has scalar type (checked in typeok) */
if (!tn->tn_lvalue) {
if (tn->tn_op == CVT && tn->tn_cast &&
tn->tn_left->tn_op == LOAD) {
if (tn->tn_type->t_tspec == PTR)
return true;
/* a cast does not yield an lvalue */
error(163);
}
/* %soperand of '%s' must be lvalue */
error(114, "", op_name(op));
return false;
} else if (tp->t_const) {
if (!tflag)
/* %soperand of '%s' must be modifiable lvalue */
warning(115, "", op_name(op));
}
return true;
}
static bool
typeok_address(const mod_t *mp,
const tnode_t *tn, const type_t *tp, tspec_t t)
{
if (t == ARRAY || t == FUNC) {
/* ok, a warning comes later (in build_address()) */
} else if (!tn->tn_lvalue) {
if (tn->tn_op == CVT && tn->tn_cast &&
tn->tn_left->tn_op == LOAD) {
if (tn->tn_type->t_tspec == PTR)
return true;
/* a cast does not yield an lvalue */
error(163);
}
/* %soperand of '%s' must be lvalue */
error(114, "", mp->m_name);
return false;
} else if (is_scalar(t)) {
if (tp->t_bitfield) {
/* cannot take address of bit-field */
error(112);
return false;
}
} else if (t != STRUCT && t != UNION) {
/* unacceptable operand of '%s' */
error(111, mp->m_name);
return false;
}
if (tn->tn_op == NAME && tn->tn_sym->s_reg) {
/* cannot take address of register %s */
error(113, tn->tn_sym->s_name);
return false;
}
return true;
}
static bool
typeok_star(tspec_t t)
{
/* until now there were no type checks for this operator */
if (t != PTR) {
/* cannot dereference non-pointer type */
error(96);
return false;
}
return true;
}
static bool
typeok_plus(op_t op,
const type_t *ltp, tspec_t lt,
const type_t *rtp, tspec_t rt)
{
/* operands have scalar types (checked above) */
if ((lt == PTR && !is_integer(rt)) || (rt == PTR && !is_integer(lt))) {
warn_incompatible_types(op, ltp, lt, rtp, rt);
return false;
}
return true;
}
static bool
typeok_minus(op_t op,
const type_t *ltp, tspec_t lt,
const type_t *rtp, tspec_t rt)
{
/* operands have scalar types (checked above) */
if (lt == PTR && (!is_integer(rt) && rt != PTR)) {
warn_incompatible_types(op, ltp, lt, rtp, rt);
return false;
} else if (rt == PTR && lt != PTR) {
warn_incompatible_types(op, ltp, lt, rtp, rt);
return false;
}
if (lt == PTR && rt == PTR) {
if (!eqtype(ltp->t_subt, rtp->t_subt, true, false, NULL)) {
/* illegal pointer subtraction */
error(116);
}
}
return true;
}
static void
typeok_shr(const mod_t *mp,
const tnode_t *ln, tspec_t lt,
const tnode_t *rn, tspec_t rt)
{
tspec_t olt, ort;
olt = before_conversion(ln)->tn_type->t_tspec;
ort = before_conversion(rn)->tn_type->t_tspec;
/* operands have integer types (checked above) */
if (pflag && !is_uinteger(lt)) {
/*
* The left operand is signed. This means that
* the operation is (possibly) nonportable.
*/
if (ln->tn_op != CON) {
/* bitwise '%s' on signed value possibly nonportable */
warning(117, mp->m_name);
} else if (ln->tn_val->v_quad < 0) {
/* bitwise '%s' on signed value nonportable */
warning(120, mp->m_name);
}
} else if (!tflag && !sflag && !is_uinteger(olt) && is_uinteger(ort)) {
/*
* The left operand would become unsigned in
* traditional C.
*/
if (hflag && !Sflag &&
(ln->tn_op != CON || ln->tn_val->v_quad < 0)) {
/* semantics of '%s' change in ANSI C; use ... */
warning(118, mp->m_name);
}
} else if (!tflag && !sflag && !is_uinteger(olt) && !is_uinteger(ort) &&
portable_size_in_bits(lt) < portable_size_in_bits(rt)) {
/*
* In traditional C the left operand would be extended,
* possibly with 1, and then shifted.
*/
if (hflag && !Sflag &&
(ln->tn_op != CON || ln->tn_val->v_quad < 0)) {
/* semantics of '%s' change in ANSI C; use ... */
warning(118, mp->m_name);
}
}
}
static void
typeok_shl(const mod_t *mp, tspec_t lt, tspec_t rt)
{
/*
* C90 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, then 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 (portable_size_in_bits(lt) < portable_size_in_bits(rt)) {
/*
* XXX If both operands are constant, make sure
* that there is really a difference between
* ANSI C and traditional C.
*/
if (hflag && !Sflag)
/* semantics of '%s' change in ANSI C; use ... */
warning(118, mp->m_name);
}
}
static void
typeok_shift(tspec_t lt, const tnode_t *rn, tspec_t rt)
{
if (rn->tn_op != CON)
return;
if (!is_uinteger(rt) && rn->tn_val->v_quad < 0) {
/* negative shift */
warning(121);
} else if ((uint64_t)rn->tn_val->v_quad ==
(uint64_t)size_in_bits(lt)) {
/* shift equal to size of object */
warning(267);
} else if ((uint64_t)rn->tn_val->v_quad > (uint64_t)size_in_bits(lt)) {
/* shift amount %llu is greater than bit-size %llu of '%s' */
warning(122, (unsigned long long)rn->tn_val->v_quad,
(unsigned long long)size_in_bits(lt),
tspec_name(lt));
}
}
static bool
is_typeok_eq(const tnode_t *ln, tspec_t lt, const tnode_t *rn, tspec_t rt)
{
if (lt == PTR && is_null_pointer(rn))
return true;
if (rt == PTR && is_null_pointer(ln))
return true;
return false;
}
static bool
typeok_ordered_comparison(op_t op,
const tnode_t *ln, const type_t *ltp, tspec_t lt,
const tnode_t *rn, const type_t *rtp, tspec_t rt)
{
if (lt == PTR && rt == PTR) {
check_pointer_comparison(op, ln, rn);
return true;
}
if (lt != PTR && rt != PTR)
return true;
if (!is_integer(lt) && !is_integer(rt)) {
warn_incompatible_types(op, ltp, lt, rtp, rt);
return false;
}
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), op_name(op));
return true;
}
static bool
typeok_quest(tspec_t lt, const tnode_t **rn)
{
if (!is_scalar(lt)) {
/* first operand must have scalar type, op ? : */
error(170);
return false;
}
while ((*rn)->tn_op == CVT)
*rn = (*rn)->tn_left;
lint_assert((*rn)->tn_op == COLON);
return true;
}
static void
typeok_colon_pointer(const mod_t *mp, const type_t *ltp, const type_t *rtp)
{
type_t *lstp = ltp->t_subt;
type_t *rstp = rtp->t_subt;
tspec_t lst = lstp->t_tspec;
tspec_t rst = rstp->t_tspec;
if ((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);
return;
}
if (eqptrtype(lstp, rstp, true))
return;
if (!eqtype(lstp, rstp, true, false, NULL))
warn_incompatible_pointers(mp, ltp, rtp);
}
static bool
typeok_colon(const mod_t *mp,
const tnode_t *ln, const type_t *ltp, tspec_t lt,
const tnode_t *rn, const type_t *rtp, tspec_t rt)
{
if (is_arithmetic(lt) && is_arithmetic(rt))
return true;
if (lt == BOOL && rt == BOOL)
return true;
if (lt == STRUCT && rt == STRUCT && ltp->t_str == rtp->t_str)
return true;
if (lt == UNION && rt == UNION && ltp->t_str == rtp->t_str)
return true;
if (lt == PTR && is_null_pointer(rn))
return true;
if (rt == PTR && is_null_pointer(ln))
return true;
if ((lt == PTR && is_integer(rt)) || (is_integer(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);
return true;
}
if (lt == VOID || rt == VOID) {
if (lt != VOID || rt != VOID)
/* incompatible types '%s' and '%s' in conditional */
warning(126, type_name(ltp), type_name(rtp));
return true;
}
if (lt == PTR && rt == PTR) {
typeok_colon_pointer(mp, ltp, rtp);
return true;
}
/* incompatible types '%s' and '%s' in conditional */
error(126, type_name(ltp), type_name(rtp));
return false;
}
static bool
typeok_assign(const mod_t *mp, const tnode_t *ln, const type_t *ltp, tspec_t lt)
{
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)
return true;
/* a cast does not yield an lvalue */
error(163);
}
/* %soperand of '%s' must be lvalue */
error(114, "left ", mp->m_name);
return false;
} 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);
}
return true;
}
/* Check the types using the information from modtab[]. */
static bool
typeok_scalar(op_t op, const mod_t *mp,
const type_t *ltp, tspec_t lt,
const type_t *rtp, tspec_t rt)
{
if (mp->m_takes_bool && lt == BOOL && rt == BOOL)
return true;
if (mp->m_requires_integer) {
if (!is_integer(lt) || (mp->m_binary && !is_integer(rt))) {
warn_incompatible_types(op, ltp, lt, rtp, rt);
return false;
}
} else if (mp->m_requires_integer_or_complex) {
if ((!is_integer(lt) && !is_complex(lt)) ||
(mp->m_binary && (!is_integer(rt) && !is_complex(rt)))) {
warn_incompatible_types(op, ltp, lt, rtp, rt);
return false;
}
} else if (mp->m_requires_scalar) {
if (!is_scalar(lt) || (mp->m_binary && !is_scalar(rt))) {
warn_incompatible_types(op, ltp, lt, rtp, rt);
return false;
}
} else if (mp->m_requires_arith) {
if (!is_arithmetic(lt) ||
(mp->m_binary && !is_arithmetic(rt))) {
warn_incompatible_types(op, ltp, lt, rtp, rt);
return false;
}
}
return true;
}
/* Check the types for specific operators and type combinations. */
static bool
typeok_op(op_t op, const mod_t *mp, int arg,
const tnode_t *ln, const type_t *ltp, tspec_t lt,
const tnode_t *rn, const type_t *rtp, tspec_t rt)
{
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_bitfield ||
((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 false;
}
/* Now we have an object we can create a pointer to */
break;
case ARROW:
if (lt != PTR && !(tflag && is_integer(lt))) {
/* Without tflag we got already an error */
if (tflag)
/* unacceptable operand of '%s' */
error(111, mp->m_name);
return false;
}
break;
case INCAFT:
case DECAFT:
case INCBEF:
case DECBEF:
if (!typeok_incdec(op, ln, ltp))
return false;
break;
case ADDR:
if (!typeok_address(mp, ln, ltp, lt))
return false;
break;
case INDIR:
if (!typeok_star(lt))
return false;
break;
case PLUS:
if (!typeok_plus(op, ltp, lt, rtp, rt))
return false;
break;
case MINUS:
if (!typeok_minus(op, ltp, lt, rtp, rt))
return false;
break;
case SHR:
typeok_shr(mp, ln, lt, rn, rt);
goto shift;
case SHL:
typeok_shl(mp, lt, rt);
shift:
typeok_shift(lt, rn, rt);
break;
case EQ:
case NE:
/*
* Accept some things which are allowed with EQ and NE,
* but not with ordered comparisons.
*/
if (is_typeok_eq(ln, lt, rn, rt))
break;
/* FALLTHROUGH */
case LT:
case GT:
case LE:
case GE:
if (!typeok_ordered_comparison(op, ln, ltp, lt, rn, rtp, rt))
return false;
break;
case QUEST:
if (!typeok_quest(lt, &rn))
return false;
break;
case COLON:
if (!typeok_colon(mp, ln, ltp, lt, rn, rtp, rt))
return false;
break;
case ASSIGN:
case INIT:
case FARG:
case RETURN:
if (!check_assign_types_compatible(op, arg, ln, rn))
return false;
goto assign;
case MULASS:
case DIVASS:
case MODASS:
goto assign;
case ADDASS:
case SUBASS:
/* operands have scalar types (checked above) */
if ((lt == PTR && !is_integer(rt)) || rt == PTR) {
warn_incompatible_types(op, ltp, lt, rtp, rt);
return false;
}
goto assign;
case SHLASS:
goto assign;
case SHRASS:
if (pflag && !is_uinteger(lt) && !(tflag && is_uinteger(rt))) {
/* bitwise '%s' on signed value possibly nonportable */
warning(117, mp->m_name);
}
goto assign;
case ANDASS:
case XORASS:
case ORASS:
goto assign;
assign:
if (!typeok_assign(mp, ln, ltp, lt))
return false;
break;
case COMMA:
if (!modtab[ln->tn_op].m_has_side_effect)
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 BITOR:
case BITXOR:
case BITAND:
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;
}
return true;
}
static void
typeok_enum(op_t op, const mod_t *mp, int arg,
const tnode_t *ln, const type_t *ltp,
const tnode_t *rn, const type_t *rtp)
{
if (mp->m_bad_on_enum &&
(ltp->t_is_enum || (mp->m_binary && rtp->t_is_enum))) {
check_bad_enum_operation(op, ln, rn);
} else if (mp->m_valid_on_enum &&
(ltp->t_is_enum && rtp != NULL && rtp->t_is_enum)) {
check_enum_type_mismatch(op, arg, ln, rn);
} else if (mp->m_valid_on_enum &&
(ltp->t_is_enum || (rtp != NULL && rtp->t_is_enum))) {
check_enum_int_mismatch(op, arg, ln, rn);
}
}
/* Perform most type checks. Return whether the types are ok. */
bool
typeok(op_t op, int arg, const tnode_t *ln, const tnode_t *rn)
{
const mod_t *mp;
tspec_t lt, rt;
type_t *ltp, *rtp;
mp = &modtab[op];
lint_assert((ltp = ln->tn_type) != NULL);
lt = ltp->t_tspec;
if (mp->m_binary) {
lint_assert((rtp = rn->tn_type) != NULL);
rt = rtp->t_tspec;
} else {
rtp = NULL;
rt = NOTSPEC;
}
if (Tflag && !typeok_scalar_strict_bool(op, mp, arg, ln, rn))
return false;
if (!typeok_scalar(op, mp, ltp, lt, rtp, rt))
return false;
if (!typeok_op(op, mp, arg, ln, ltp, lt, rn, rtp, rt))
return false;
typeok_enum(op, mp, arg, ln, ltp, rn, rtp);
return true;
}
static void
check_pointer_comparison(op_t op, const tnode_t *ln, const tnode_t *rn)
{
type_t *ltp, *rtp;
tspec_t lst, rst;
const char *lsts, *rsts;
lst = (ltp = ln->tn_type)->t_subt->t_tspec;
rst = (rtp = rn->tn_type)->t_subt->t_tspec;
if (lst == VOID || rst == VOID) {
if (sflag && (lst == FUNC || rst == FUNC)) {
/* (void *)0 already handled in typeok() */
*(lst == FUNC ? &lsts : &rsts) = "function pointer";
*(lst == VOID ? &lsts : &rsts) = "'void *'";
/* ANSI C forbids comparison of %s with %s */
warning(274, lsts, rsts);
}
return;
}
if (!eqtype(ltp->t_subt, rtp->t_subt, true, false, NULL)) {
warn_incompatible_pointers(&modtab[op], ltp, rtp);
return;
}
if (lst == FUNC && rst == 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 bool
check_assign_types_compatible(op_t op, int arg,
const tnode_t *ln, const tnode_t *rn)
{
tspec_t lt, rt, lst = NOTSPEC, rst = NOTSPEC;
type_t *ltp, *rtp, *lstp = NULL, *rstp = NULL;
const 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 (lt == BOOL && is_scalar(rt)) /* C99 6.3.1.2 */
return true;
if (is_arithmetic(lt) && (is_arithmetic(rt) || rt == BOOL))
return true;
if ((lt == STRUCT || lt == UNION) && (rt == STRUCT || rt == UNION))
/* both are struct or union */
return ltp->t_str == rtp->t_str;
/* a null pointer may be assigned to any pointer */
if (lt == PTR && is_null_pointer(rn))
return true;
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 ? <s : &rts) = "function pointer";
*(lst == VOID ? <s : &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, true, false, 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:
/* converting '%s' to incompatible '%s' ... */
warning(153,
type_name(rtp), type_name(ltp), arg);
break;
default:
/* operands have incompatible pointer type... */
warning(128, mp->m_name,
type_name(lstp), type_name(rstp));
break;
}
}
return true;
}
if ((lt == PTR && is_integer(rt)) || (is_integer(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 true;
}
if (lt == PTR && rt == PTR) {
switch (op) {
case INIT:
case RETURN:
warn_incompatible_pointers(NULL, ltp, rtp);
break;
case FARG:
/* converting '%s' to incompatible '%s' for ... */
warning(153, type_name(rtp), type_name(ltp), arg);
break;
default:
warn_incompatible_pointers(mp, ltp, rtp);
break;
}
return true;
}
switch (op) {
case INIT:
/* cannot initialize '%s' from '%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, ltp, lt, rtp, rt);
break;
}
return false;
}
/* Prints a warning if a strange operator is used on an enum type. */
static void
check_bad_enum_operation(op_t op, const tnode_t *ln, const tnode_t *rn)
{
if (!eflag)
return;
if (!(ln->tn_type->t_is_enum ||
(modtab[op].m_binary && rn->tn_type->t_is_enum))) {
return;
}
/*
* Enum as offset to a pointer is an exception (otherwise enums
* could not be used as array indices).
*/
if (op == PLUS &&
((ln->tn_type->t_is_enum && rn->tn_type->t_tspec == PTR) ||
(rn->tn_type->t_is_enum && ln->tn_type->t_tspec == PTR))) {
return;
}
/* dubious operation on enum, op %s */
warning(241, op_name(op));
}
/*
* Prints a warning if an operator is applied to two different enum types.
*/
static void
check_enum_type_mismatch(op_t op, int arg, const tnode_t *ln, const tnode_t *rn)
{
const mod_t *mp;
mp = &modtab[op];
if (ln->tn_type->t_enum != rn->tn_type->t_enum) {
switch (op) {
case INIT:
/* enum type mismatch between '%s' and '%s' in ... */
warning(210,
type_name(ln->tn_type), type_name(rn->tn_type));
break;
case FARG:
/* enum type mismatch, arg #%d (%s != %s) */
warning(156, arg,
type_name(ln->tn_type), type_name(rn->tn_type));
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: '%s' '%s' '%s' */
warning(130, type_name(ln->tn_type), mp->m_name,
type_name(rn->tn_type));
break;
}
} else if (Pflag && mp->m_comparison && op != EQ && op != NE) {
if (eflag)
/* dubious comparison of enums, op %s */
warning(243, mp->m_name);
}
}
/* Prints a warning if the operands mix between enum and integer. */
static void
check_enum_int_mismatch(op_t op, int arg, const tnode_t *ln, const tnode_t *rn)
{
if (!eflag)
return;
switch (op) {
case INIT:
/*
* Initialization with 0 is allowed. Otherwise, all implicit
* initializations would need to be warned upon as well.
*/
if (!rn->tn_type->t_is_enum && rn->tn_op == CON &&
is_integer(rn->tn_type->t_tspec) &&
rn->tn_val->v_quad == 0) {
return;
}
/* initialization 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),
op_name(op));
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 = expr_zalloc_tnode();
ntn->tn_op = op;
ntn->tn_type = type;
if (ln->tn_from_system_header)
ntn->tn_from_system_header = true;
if (rn != NULL && rn->tn_from_system_header)
ntn->tn_from_system_header = true;
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 = type_size_in_bits(ln->tn_type) / CHAR_SIZE;
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 INDIR:
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 = true;
break;
default:
break;
}
return ntn;
}
/*
* Performs the "integer promotions" (C99 6.3.1.1p2), which convert small
* integer types to either int or unsigned int.
*
* If tflag is set or the operand is a function argument with no type
* information (no prototype or variable # of args), converts float to double.
*/
tnode_t *
promote(op_t op, bool farg, tnode_t *tn)
{
tspec_t t;
type_t *ntp;
u_int len;
t = tn->tn_type->t_tspec;
if (!is_arithmetic(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_bitfield) {
len = tn->tn_type->t_flen;
if (size_in_bits(INT) > len) {
t = INT;
} else {
lint_assert(len == size_in_bits(INT));
if (is_uinteger(t)) {
t = UINT;
} else {
t = INT;
}
}
} else if (t == CHAR || t == UCHAR || t == SCHAR) {
t = (size_in_bits(CHAR) < size_in_bits(INT)
|| t != UCHAR) ? INT : UINT;
} else if (t == SHORT || t == USHORT) {
t = (size_in_bits(SHORT) < size_in_bits(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 = expr_dup_type(tn->tn_type);
ntp->t_tspec = t;
/*
* Keep t_is_enum so we are later able to check compatibility
* of enum types.
*/
tn = convert(op, 0, ntp, tn);
}
return tn;
}
/*
* Apply the "usual arithmetic conversions" (C99 6.3.1.8).
*
* This gives both operands the same type.
* This is done in different ways for traditional C and C90.
*/
static void
balance(op_t op, tnode_t **lnp, tnode_t **rnp)
{
tspec_t lt, rt, t;
int i;
bool u;
type_t *ntp;
static const 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 (!is_arithmetic(lt) || !is_arithmetic(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_in_bits(lt) > size_in_bits(rt)) {
t = lt;
} else if (size_in_bits(lt) < size_in_bits(rt)) {
t = rt;
} else {
for (i = 3; tl[i] != INT; i++) {
if (tl[i] == lt || tl[i] == rt)
break;
}
if ((is_uinteger(lt) || is_uinteger(rt)) &&
!is_uinteger(tl[i])) {
i--;
}
t = tl[i];
}
}
} else {
/* Keep unsigned in traditional C */
u = is_uinteger(lt) || is_uinteger(rt);
for (i = 0; tl[i] != INT; i++) {
if (lt == tl[i] || rt == tl[i])
break;
}
t = tl[i];
if (u && is_integer(t) && !is_uinteger(t))
t = unsigned_type(t);
}
if (t != lt) {
ntp = expr_dup_type((*lnp)->tn_type);
ntp->t_tspec = t;
*lnp = convert(op, 0, ntp, *lnp);
}
if (t != rt) {
ntp = expr_dup_type((*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;
nt = tp->t_tspec;
ot = tn->tn_type->t_tspec;
if (!tflag && !sflag && op == FARG)
check_prototype_conversion(arg, nt, ot, tp, tn);
if (is_integer(nt) && is_integer(ot)) {
check_integer_conversion(op, arg, nt, ot, tp, tn);
} else if (nt == PTR && is_null_pointer(tn)) {
/* a null pointer may be assigned to any pointer. */
} else if (is_integer(nt) && nt != BOOL && ot == PTR) {
check_pointer_integer_conversion(op, nt, tp, tn);
} else if (nt == PTR && ot == PTR) {
check_pointer_conversion(op, tn, tp);
}
ntn = expr_zalloc_tnode();
ntn->tn_op = CVT;
ntn->tn_type = tp;
ntn->tn_cast = op == CVT;
ntn->tn_from_system_header |= tn->tn_from_system_header;
ntn->tn_right = NULL;
if (tn->tn_op != CON || nt == VOID) {
ntn->tn_left = tn;
} else {
ntn->tn_op = CON;
ntn->tn_val = expr_zalloc(sizeof(*ntn->tn_val));
convert_constant(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 (!is_arithmetic(nt) || !is_arithmetic(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.
*/
/* XXX: what about SCHAR? */
if (nt == CHAR || nt == UCHAR || nt == SHORT || nt == USHORT)
return;
/* get default promotion */
ptn = promote(NOOP, true, 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 (is_floating(nt) != is_floating(ot) ||
portable_size_in_bits(nt) != portable_size_in_bits(ot)) {
/* representation and/or width change */
if (!is_integer(ot) ||
portable_size_in_bits(ot) > portable_size_in_bits(INT)) {
/* argument #%d is converted from '%s' to '%s' ... */
warning(259,
arg, type_name(tn->tn_type), type_name(tp));
}
} 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 && is_integer(nt) &&
signed_type(nt) == signed_type(ot) &&
msb(ptn->tn_val->v_quad, ot, -1) == 0) {
/* ok */
} else {
/* argument #%d is converted from '%s' to '%s' ... */
warning(259,
arg, type_name(tn->tn_type), type_name(tp));
}
}
}
/*
* 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 (Sflag && nt == BOOL)
return; /* See C99 6.3.1.2 */
if (Pflag && portable_size_in_bits(nt) > portable_size_in_bits(ot) &&
is_uinteger(nt) != is_uinteger(ot)) {
if (aflag > 0 && 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 && portable_size_in_bits(nt) > portable_size_in_bits(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 (portable_size_in_bits(nt) < portable_size_in_bits(ot) &&
(ot == LONG || ot == ULONG || ot == QUAD || ot == UQUAD ||
aflag > 1)) {
/* conversion from '%s' may lose accuracy */
if (aflag > 0) {
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)
return; /* We got already an error. */
if (portable_size_in_bits(nt) >= portable_size_in_bits(PTR))
return;
if (pflag && size_in_bits(nt) >= size_in_bits(PTR)) {
/* conversion of pointer to '%s' may lose bits */
warning(134, type_name(tp));
} else {
/* conversion of pointer to '%s' loses bits */
warning(133, type_name(tp));
}
}
static bool
should_warn_about_pointer_cast(const type_t *tp, tspec_t nst,
const tnode_t *tn, tspec_t ost)
{
if (nst == STRUCT || nst == UNION)
if (tp->t_subt->t_str != tn->tn_type->t_subt->t_str)
return true;
if (nst == CHAR || nst == UCHAR)
return false; /* for the sake of traditional C code */
return portable_size_in_bits(nst) != portable_size_in_bits(ost);
}
/*
* Warn about questionable pointer conversions.
*/
static void
check_pointer_conversion(op_t op, tnode_t *tn, type_t *tp)
{
tspec_t nst, ost;
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;
nst = tp->t_subt->t_tspec;
ost = tn->tn_type->t_subt->t_tspec;
if (nst == VOID || ost == VOID) {
if (sflag && (nst == FUNC || ost == FUNC)) {
/* null pointers are already handled in convert() */
*(nst == FUNC ? &nts : &ots) = "function pointer";
*(nst == VOID ? &nts : &ots) = "'void *'";
/* ANSI C forbids conversion of %s to %s */
warning(303, ots, nts);
}
return;
} else if (nst == FUNC && ost == FUNC) {
return;
} else if (nst == FUNC || ost == FUNC) {
/* converting '%s' to '%s' is questionable */
warning(229, type_name(tn->tn_type), type_name(tp));
return;
}
if (hflag && alignment_in_bits(tp->t_subt) >
alignment_in_bits(tn->tn_type->t_subt)) {
/* converting '%s' to '%s' may cause alignment problem */
warning(135, type_name(tn->tn_type), type_name(tp));
}
if (cflag && should_warn_about_pointer_cast(tp, nst, tn, ost)) {
/* pointer cast from '%s' to '%s' may be troublesome */
warning(247, type_name(tn->tn_type), type_name(tp));
}
}
/*
* Converts a typed constant to 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
convert_constant(op_t op, int arg, const type_t *tp, val_t *nv, val_t *v)
{
tspec_t ot, nt;
ldbl_t max = 0.0, min = 0.0;
int sz;
bool rchk;
int64_t xmask, xmsk1;
int osz, nsz;
ot = v->v_tspec;
nt = nv->v_tspec = tp->t_tspec;
rchk = false;
if (nt == BOOL) { /* C99 6.3.1.2 */
nv->v_ansiu = false;
nv->v_quad = is_nonzero_val(v) ? 1 : 0;
return;
}
if (ot == FLOAT || ot == DOUBLE || ot == LDOUBLE) {
switch (nt) {
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(/*CONSTCOND*/false);
}
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 || is_uinteger(nt)) ?
(int64_t)v->v_ldbl : (int64_t)v->v_ldbl;
}
} else {
if (nt == FLOAT) {
nv->v_ldbl = (ot == PTR || is_uinteger(ot)) ?
(float)(uint64_t)v->v_quad : (float)v->v_quad;
} else if (nt == DOUBLE) {
nv->v_ldbl = (ot == PTR || is_uinteger(ot)) ?
(double)(uint64_t)v->v_quad : (double)v->v_quad;
} else if (nt == LDOUBLE) {
nv->v_ldbl = (ot == PTR || is_uinteger(ot)) ?
(ldbl_t)(uint64_t)v->v_quad : (ldbl_t)v->v_quad;
} else {
rchk = true; /* Check for lost precision. */
nv->v_quad = v->v_quad;
}
}
if (v->v_ansiu && is_floating(nt)) {
/* ANSI C treats constant as unsigned */
warning(157);
v->v_ansiu = false;
} else if (v->v_ansiu && (is_integer(nt) && !is_uinteger(nt) &&
portable_size_in_bits(nt) >
portable_size_in_bits(ot))) {
/* ANSI C treats constant as unsigned */
warning(157);
v->v_ansiu = false;
}
switch (nt) {
case FLOAT:
case FCOMPLEX:
case DOUBLE:
case DCOMPLEX:
case LDOUBLE:
case LCOMPLEX:
break;
default:
sz = tp->t_bitfield ? tp->t_flen : size_in_bits(nt);
nv->v_quad = xsign(nv->v_quad, nt, sz);
break;
}
if (rchk && op != CVT) {
osz = size_in_bits(ot);
nsz = tp->t_bitfield ? tp->t_flen : size_in_bits(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 == BITOR || op == BITXOR) {
/*
* 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, op_name(op));
}
} else if (op == ANDASS || op == BITAND) {
/*
* 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), op_name(op));
} else if (nsz < osz &&
(v->v_quad & xmask) != xmask &&
(v->v_quad & xmask) != 0) {
/* constant truncated by conv., op %s */
warning(306, op_name(op));
}
} else if ((nt != PTR && is_uinteger(nt)) &&
(ot != PTR && !is_uinteger(ot)) &&
v->v_quad < 0) {
if (op == ASSIGN) {
/* assignment of negative constant to ... */
warning(164);
} else if (op == INIT) {
/* initialization of unsigned with neg... */
warning(221);
} else if (op == FARG) {
/* conversion of negative constant to ... */
warning(296, arg);
} else if (modtab[op].m_comparison) {
/* 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 &&
(is_uinteger(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_bitfield) {
/* precision lost in bit-field assignment */
warning(166);
} else if (op == ASSIGN) {
/* constant truncated by assignment */
warning(165);
} else if (op == INIT && tp->t_bitfield) {
/* 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_bitfield) {
/* precision lost in bit-field assignment */
warning(166);
} else if (op == INIT && tp->t_bitfield) {
/* 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,
const type_t *ltp, tspec_t lt,
const type_t *rtp, tspec_t rt)
{
const 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 {
/* cannot assign to '%s' from '%s' */
error(171, type_name(ltp), type_name(rtp));
}
} 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 {
lint_assert(rt == NOTSPEC);
/* operand of '%s' has invalid type (%s) */
error(108, mp->m_name, tspec_name(lt));
}
}
/*
* Called if incompatible pointer types are detected.
* Print an appropriate warning.
*/
static void
warn_incompatible_pointers(const mod_t *mp,
const type_t *ltp, const 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 {
/* incompatible structure pointers: '%s' '%s' '%s' */
warning(245, type_name(ltp), mp->m_name, type_name(rtp));
}
} else {
if (mp == NULL) {
/* illegal pointer combination */
warning(184);
} else {
/* illegal pointer combination (%s) and (%s), op %s */
warning(124,
type_name(ltp), type_name(rtp), mp->m_name);
}
}
}
/* Return a type based on tp1, with added qualifiers from tp2. */
static type_t *
merge_qualifiers(type_t *tp1, const type_t *tp2)
{
type_t *ntp, *nstp;
lint_assert(tp1->t_tspec == PTR);
lint_assert(tp2->t_tspec == PTR);
bool c1 = tp1->t_subt->t_const;
bool c2 = tp2->t_subt->t_const;
bool v1 = tp1->t_subt->t_volatile;
bool v2 = tp2->t_subt->t_volatile;
if (c1 == (c1 | c2) && v1 == (v1 | v2))
return tp1;
nstp = expr_dup_type(tp1->t_subt);
nstp->t_const |= c2;
nstp->t_volatile |= v2;
ntp = expr_dup_type(tp1);
ntp->t_subt = nstp;
return ntp;
}
/*
* Returns true if the given structure or union has a constant member
* (maybe recursively).
*/
static bool
has_constant_member(const type_t *tp)
{
sym_t *m;
tspec_t t;
lint_assert((t = tp->t_tspec) == STRUCT || t == UNION);
for (m = tp->t_str->sou_first_member; m != NULL; m = m->s_next) {
tp = m->s_type;
if (tp->t_const)
return true;
if ((t = tp->t_tspec) == STRUCT || t == UNION) {
if (has_constant_member(m->s_type))
return true;
}
}
return false;
}
/*
* 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;
bool 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_address(ln, true);
} else if (ln->tn_type->t_tspec != PTR) {
lint_assert(tflag);
lint_assert(is_integer(ln->tn_type->t_tspec));
ln = convert(NOOP, 0, expr_derive_type(gettyp(VOID), PTR), ln);
}
ctn = expr_new_integer_constant(PTRDIFF_TSPEC,
rn->tn_sym->s_value.v_quad / CHAR_SIZE);
ntn = new_tnode(PLUS, expr_derive_type(rn->tn_type, PTR), ln, ctn);
if (ln->tn_op == CON)
ntn = fold(ntn);
if (rn->tn_type->t_bitfield) {
ntn = new_tnode(FSEL, ntn->tn_type->t_subt, ntn, NULL);
} else {
ntn = new_tnode(INDIR, ntn->tn_type->t_subt, ntn, NULL);
}
if (nolval)
ntn->tn_lvalue = false;
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 = expr_new_integer_constant(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:
/* XXX: integer and LDOUBLE don't match. */
cn = expr_new_integer_constant(LDOUBLE, (int64_t)1);
break;
case DCOMPLEX:
/* XXX: integer and DOUBLE don't match. */
cn = expr_new_integer_constant(DOUBLE, (int64_t)1);
break;
case FCOMPLEX:
/* XXX: integer and FLOAT don't match. */
cn = expr_new_integer_constant(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 = true;
return ntn;
}
/*
* Create a tree node for the unary & operator
*/
static tnode_t *
build_address(tnode_t *tn, bool noign)
{
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 == INDIR &&
tn->tn_left->tn_type->t_tspec == PTR &&
tn->tn_left->tn_type->t_subt == tn->tn_type) {
return tn->tn_left;
}
return new_tnode(ADDR, expr_derive_type(tn->tn_type, PTR), tn, NULL);
}
/*
* 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 && is_integer(ln->tn_type->t_tspec)) {
ntn = ln;
ln = rn;
rn = ntn;
}
if (ln->tn_type->t_tspec == PTR && rn->tn_type->t_tspec != PTR) {
/* XXX: this assertion should be easy to trigger */
lint_assert(is_integer(rn->tn_type->t_tspec));
check_ctype_macro_invocation(ln, rn);
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);
tp = gettyp(PTRDIFF_TSPEC);
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 *tp;
tnode_t *ntn;
lt = ln->tn_type->t_tspec;
rt = rn->tn_type->t_tspec;
pdt = PTRDIFF_TSPEC;
/*
* Arithmetic types are balanced, all other type combinations
* still need to be handled.
*/
if (is_arithmetic(lt) && is_arithmetic(rt)) {
tp = ln->tn_type;
} else if (lt == BOOL && rt == BOOL) {
tp = ln->tn_type;
} else if (lt == VOID || rt == VOID) {
tp = 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 (is_incomplete(ln->tn_type)) {
/* unknown operand size, op %s */
error(138, op_name(COLON));
return NULL;
}
tp = ln->tn_type;
} else if (lt == PTR && is_integer(rt)) {
if (rt != pdt) {
rn = convert(NOOP, 0, gettyp(pdt), rn);
rt = pdt;
}
tp = ln->tn_type;
} else if (rt == PTR && is_integer(lt)) {
if (lt != pdt) {
ln = convert(NOOP, 0, gettyp(pdt), ln);
lt = pdt;
}
tp = rn->tn_type;
} else if (lt == PTR && ln->tn_type->t_subt->t_tspec == VOID) {
tp = merge_qualifiers(rn->tn_type, ln->tn_type);
} else if (rt == PTR && rn->tn_type->t_subt->t_tspec == VOID) {
tp = merge_qualifiers(ln->tn_type, rn->tn_type);
} else {
/*
* 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.
*/
tp = merge_qualifiers(ln->tn_type, rn->tn_type);
}
ntn = new_tnode(COLON, tp, 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(is_integer(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 (is_incomplete(ln->tn_type)) {
if (op == RETURN) {
/* cannot return incomplete type */
error(212);
} else {
/* unknown operand size, op %s */
error(138, op_name(op));
}
return NULL;
}
}
if (op == SHLASS) {
if (portable_size_in_bits(lt) < portable_size_in_bits(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_bitfield && 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;
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 unknown size */
gnuism(136);
break;
case STRUCT:
case UNION:
if ((elsz = tp->t_str->sou_size_in_bits) == 0)
/* cannot do pointer arithmetic on operand of ... */
error(136);
break;
case ENUM:
if (is_incomplete(tp)) {
/* cannot do pointer arithmetic on operand of ... */
warning(136);
}
/* FALLTHROUGH */
default:
if ((elsz = size_in_bits(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 unknown size */
error(136);
}
if (elsz == 0)
elsz = CHAR_SIZE;
return expr_new_integer_constant(PTRDIFF_TSPEC,
(int64_t)(elem * elsz / CHAR_SIZE));
}
/*
* 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 all operands are constants.
*/
static tnode_t *
fold(tnode_t *tn)
{
val_t *v;
tspec_t t;
bool utyp, ovfl;
int64_t sl, sr = 0, q = 0, mask;
uint64_t ul, ur = 0;
tnode_t *cn;
v = xcalloc(1, sizeof(*v));
v->v_tspec = t = tn->tn_type->t_tspec;
utyp = t == PTR || is_uinteger(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_in_bits(t)];
ovfl = false;
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 = true;
break;
case COMPL:
q = ~sl;
break;
case MULT:
if (utyp) {
q = ul * ur;
if (q != (q & mask))
ovfl = true;
else if ((ul != 0) && ((q / ul) != ur))
ovfl = true;
} else {
q = sl * sr;
if (msb(q, t, -1) != (msb(sl, t, -1) ^ msb(sr, t, -1)))
ovfl = true;
}
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 = true;
} else if (msb(sl, t, -1) == 0 && msb(sr, t, -1) == 0) {
if (msb(q, t, -1) != 0)
ovfl = true;
}
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 = true;
} else if (msb(sl, t, -1) == 0 && msb(sr, t, -1) != 0) {
if (msb(q, t, -1) != 0)
ovfl = true;
}
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_in_bits(t) - (int)sr);
break;
case LT:
q = (utyp ? ul < ur : sl < sr) ? 1 : 0;
break;
case LE:
q = (utyp ? ul <= ur : sl <= sr) ? 1 : 0;
break;
case GE:
q = (utyp ? ul >= ur : sl >= sr) ? 1 : 0;
break;
case GT:
q = (utyp ? ul > ur : sl > sr) ? 1 : 0;
break;
case EQ:
q = (utyp ? ul == ur : sl == sr) ? 1 : 0;
break;
case NE:
q = (utyp ? ul != ur : sl != sr) ? 1 : 0;
break;
case BITAND:
q = utyp ? (int64_t)(ul & ur) : sl & sr;
break;
case BITXOR:
q = utyp ? (int64_t)(ul ^ ur) : sl ^ sr;
break;
case BITOR:
q = utyp ? (int64_t)(ul | ur) : sl | sr;
break;
default:
lint_assert(/*CONSTCOND*/false);
}
/* 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, op_name(tn->tn_op));
}
v->v_quad = xsign(q, t, -1);
cn = expr_new_constant(tn->tn_type, v);
if (tn->tn_left->tn_system_dependent)
cn->tn_system_dependent = true;
if (modtab[tn->tn_op].m_binary && tn->tn_right->tn_system_dependent)
cn->tn_system_dependent = true;
return cn;
}
/*
* Fold constant nodes, as much as is needed for comparing the value with 0
* (test context, for controlling expressions).
*/
static tnode_t *
fold_test(tnode_t *tn)
{
bool l, r;
val_t *v;
v = xcalloc(1, sizeof(*v));
v->v_tspec = tn->tn_type->t_tspec;
lint_assert(v->v_tspec == INT || (Tflag && v->v_tspec == BOOL));
l = constant_is_nonzero(tn->tn_left);
r = modtab[tn->tn_op].m_binary && constant_is_nonzero(tn->tn_right);
switch (tn->tn_op) {
case NOT:
if (hflag && !constcond_flag)
/* constant argument to '!' */
warning(239);
v->v_quad = !l ? 1 : 0;
break;
case LOGAND:
v->v_quad = l && r ? 1 : 0;
break;
case LOGOR:
v->v_quad = l || r ? 1 : 0;
break;
default:
lint_assert(/*CONSTCOND*/false);
}
return expr_new_constant(tn->tn_type, v);
}
/*
* Fold constant nodes having 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(*v));
v->v_tspec = t = tn->tn_type->t_tspec;
lint_assert(is_floating(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 ? 1 : 0;
break;
case LE:
v->v_quad = l <= r ? 1 : 0;
break;
case GE:
v->v_quad = l >= r ? 1 : 0;
break;
case GT:
v->v_quad = l > r ? 1 : 0;
break;
case EQ:
v->v_quad = l == r ? 1 : 0;
break;
case NE:
v->v_quad = l != r ? 1 : 0;
break;
default:
lint_assert(/*CONSTCOND*/false);
}
lint_assert(fpe != 0 || isnan((double)v->v_ldbl) == 0);
if (fpe != 0 || finite((double)v->v_ldbl) == 0 ||
(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, op_name(tn->tn_op));
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 expr_new_constant(tn->tn_type, v);
}
/*
* Create a constant node for sizeof.
*/
tnode_t *
build_sizeof(const type_t *tp)
{
int64_t size_in_bytes = type_size_in_bits(tp) / CHAR_SIZE;
tnode_t *tn = expr_new_integer_constant(SIZEOF_TSPEC, size_in_bytes);
tn->tn_system_dependent = true;
return tn;
}
/*
* Create a constant node for offsetof.
*/
tnode_t *
build_offsetof(const type_t *tp, const sym_t *sym)
{
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
int64_t offset_in_bytes = type_size_in_bits(tp) / CHAR_SIZE;
tnode_t *tn = expr_new_integer_constant(SIZEOF_TSPEC, offset_in_bytes);
tn->tn_system_dependent = true;
return tn;
}
int64_t
type_size_in_bits(const type_t *tp)
{
int elem, elsz;
bool flex;
elem = 1;
flex = false;
while (tp->t_tspec == ARRAY) {
flex = true; /* 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 (is_incomplete(tp)) {
/* cannot take size/alignment of incomplete type */
error(143);
elsz = 1;
} else {
elsz = tp->t_str->sou_size_in_bits;
}
break;
case ENUM:
if (is_incomplete(tp)) {
/* cannot take size/alignment of incomplete type */
warning(143);
}
/* FALLTHROUGH */
default:
if (tp->t_bitfield) {
/* 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_in_bits(tp->t_tspec);
lint_assert(elsz > 0);
}
break;
}
return (int64_t)elem * elsz;
}
tnode_t *
build_alignof(const type_t *tp)
{
switch (tp->t_tspec) {
case ARRAY:
break;
case FUNC:
/* cannot take size/alignment of function */
error(144);
return 0;
case STRUCT:
case UNION:
if (is_incomplete(tp)) {
/* cannot take size/alignment of incomplete type */
error(143);
return 0;
}
break;
case ENUM:
break;
default:
if (tp->t_bitfield) {
/* 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;
}
return expr_new_integer_constant(SIZEOF_TSPEC,
(int64_t)alignment_in_bits(tp) / CHAR_SIZE);
}
/*
* Type casts.
*/
tnode_t *
cast(tnode_t *tn, type_t *tp)
{
tspec_t nt, ot;
if (tn == NULL)
return NULL;
/*
* XXX: checking for tp == NULL is only a quick fix for PR 22119.
* The proper fix needs to be investigated properly.
* See d_pr_22119.c for how to get here.
*/
if (tp == 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 seems really questionable.
*/
} else if (nt == UNION) {
sym_t *m;
struct_or_union *str = tp->t_str;
if (!Sflag) {
/* union cast is a C9X feature */
error(328);
return NULL;
}
for (m = str->sou_first_member; m != NULL; m = m->s_next) {
if (sametype(m->s_type, tn->tn_type)) {
tn = expr_zalloc_tnode();
tn->tn_op = CVT;
tn->tn_type = tp;
tn->tn_cast = true;
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 (is_integer(nt) && is_scalar(ot)) {
/* ok */
} else if (is_floating(nt) && is_arithmetic(ot)) {
/* ok */
} else if (nt == PTR && is_integer(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 type '%s' */
warning(275, type_name(tn->tn_type));
}
} else {
/* invalid cast expression */
error(147);
return NULL;
}
tn = convert(CVT, 0, tp, tn);
tn->tn_cast = true;
return tn;
}
/*
* Create the node for a function argument.
* All necessary conversions and type checks are done in
* new_function_call_node because new_function_argument_node has no
* information about expected argument types.
*/
tnode_t *
new_function_argument_node(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 = expr_new_integer_constant(INT, 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 *
new_function_call_node(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;
}
check_ctype_function_call(func, args);
/*
* 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) &&
is_incomplete(arg->tn_left->tn_type)) {
/* argument cannot have unknown size, arg #%d */
error(152, n);
return NULL;
} else if (is_integer(at) &&
arg->tn_left->tn_type->t_is_enum &&
is_incomplete(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, true, 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;
bool dowarn;
ln = xcalloc(1, sizeof(*ln));
ln->tn_type = expr_dup_type(tp);
ln->tn_type->t_const = false;
ln->tn_lvalue = true;
if (typeok(FARG, n, ln, tn)) {
if (!eqtype(tp, tn->tn_type,
true, false, (dowarn = false, &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, bool required)
{
val_t *v;
if (tn != NULL)
tn = cconv(tn);
if (tn != NULL)
tn = promote(NOOP, false, tn);
v = xcalloc(1, sizeof(*v));
if (tn == NULL) {
lint_assert(nerr != 0);
if (dflag)
printf("constant node is null; returning 1 instead\n");
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 (is_integer(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 (!is_integer(v->v_tspec))
v->v_tspec = INT;
return v;
}
static bool
is_constcond_false(const tnode_t *tn, tspec_t t)
{
return (t == BOOL || t == INT) &&
tn->tn_op == CON && tn->tn_val->v_quad == 0;
}
/*
* 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 and dofreeblk is true, expr() frees the
* memory which is used for the expression.
*/
void
expr(tnode_t *tn, bool vctx, bool tctx, bool dofreeblk, bool constcond_false_ok)
{
lint_assert(tn != NULL || nerr != 0);
if (tn == NULL) {
expr_free_all();
return;
}
/* expr() is also called in global initializations */
/* TODO: rename constcond_false_ok */
if (dcs->d_ctx != EXTERN && !constcond_false_ok)
check_statement_reachable();
check_expr_misc(tn, vctx, tctx, !tctx, false, false, false);
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 &&
!tn->tn_system_dependent &&
!(constcond_false_ok &&
is_constcond_false(tn, tn->tn_type->t_tspec)))
/* constant in conditional context */
warning(161);
}
if (!modtab[tn->tn_op].m_has_side_effect) {
/*
* 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)
expr_free_all();
}
static bool
has_side_effect(const tnode_t *tn) // NOLINT(misc-no-recursion)
{
op_t op = tn->tn_op;
if (modtab[op].m_has_side_effect)
return true;
if (op == CVT && tn->tn_type->t_tspec == VOID)
return has_side_effect(tn->tn_left);
/* XXX: Why not has_side_effect(tn->tn_left) as well? */
if (op == LOGAND || op == LOGOR)
return has_side_effect(tn->tn_right);
/* XXX: Why not has_side_effect(tn->tn_left) as well? */
if (op == QUEST)
return has_side_effect(tn->tn_right);
if (op == COLON || op == COMMA) {
return has_side_effect(tn->tn_left) ||
has_side_effect(tn->tn_right);
}
return false;
}
static void
check_null_effect(const tnode_t *tn)
{
if (hflag && !has_side_effect(tn)) {
/* expression has null effect */
warning(129);
}
}
/*
* Dump an expression to stdout
* only used for debugging
*/
static void
display_expression(const tnode_t *tn, int offs)
{
uint64_t uq;
if (tn == NULL) {
(void)printf("%*s%s\n", offs, "", "NULL");
return;
}
(void)printf("%*sop %s ", offs, "", op_name(tn->tn_op));
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 && is_floating(tn->tn_type->t_tspec)) {
(void)printf("%#g ", (double)tn->tn_val->v_ldbl);
} else if (tn->tn_op == CON && is_integer(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(const tnode_t *tn, bool vctx, bool tctx,
bool eqwarn, bool fcall, bool rvdisc, bool szof)
{
tnode_t *ln, *rn;
const mod_t *mp;
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 ADDR:
/* XXX: Taking warn_about_unreachable into account here feels wrong. */
if (ln->tn_op == NAME && (reached || !warn_about_unreachable)) {
if (!szof)
mark_as_set(ln->tn_sym);
mark_as_used(ln->tn_sym, fcall, szof);
}
if (ln->tn_op == INDIR && ln->tn_left->tn_op == PLUS)
/* check the range of array indices */
check_array_index(ln->tn_left, true);
break;
case LOAD:
if (ln->tn_op == INDIR && ln->tn_left->tn_op == PLUS)
/* check the range of array indices */
check_array_index(ln->tn_left, false);
/* 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:
/* XXX: Taking warn_about_unreachable into account here feels wrong. */
if (ln->tn_op == NAME && (reached || !warn_about_unreachable)) {
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, false, false);
}
break;
case ASSIGN:
/* XXX: Taking warn_about_unreachable into account here feels wrong. */
if (ln->tn_op == NAME && !szof && (reached || !warn_about_unreachable)) {
mark_as_set(ln->tn_sym);
if (ln->tn_sym->s_scl == EXTERN)
outusg(ln->tn_sym);
}
if (ln->tn_op == INDIR && ln->tn_left->tn_op == PLUS)
/* check the range of array indices */
check_array_index(ln->tn_left, false);
break;
case CALL:
lint_assert(ln->tn_op == ADDR);
lint_assert(ln->tn_left->tn_op == 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 BITOR:
case BITXOR:
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 INDIR:
case UMINUS:
case UPLUS:
case DEC:
case INC:
case COMPL:
case NOT:
case POINT:
case ARROW:
case NOOP:
case BITAND:
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;
}
bool cvctx = mp->m_left_value_context;
bool ctctx = mp->m_left_test_context;
bool eq = mp->m_warn_if_operand_eq &&
!ln->tn_parenthesized &&
rn != NULL && !rn->tn_parenthesized;
/*
* 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 = false;
bool discard = op == CVT && tn->tn_type->t_tspec == VOID;
check_expr_misc(ln, cvctx, ctctx, eq, op == CALL, discard, szof);
switch (op) {
case PUSH:
if (rn != NULL)
check_expr_misc(rn, false, false, eq, false, false,
szof);
break;
case LOGAND:
case LOGOR:
check_expr_misc(rn, false, true, eq, false, false, szof);
break;
case COLON:
check_expr_misc(rn, cvctx, ctctx, eq, false, false, szof);
break;
case COMMA:
check_expr_misc(rn, vctx, tctx, eq, false, false, szof);
break;
default:
if (mp->m_binary)
check_expr_misc(rn, true, false, eq, false, false,
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, bool 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 != ADDR)
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 (is_incomplete(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_SIZE;
/* Change the unit of the index from bytes to element size. */
if (is_uinteger(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 (!is_uinteger(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;
lt = ln->tn_type->t_tspec;
rt = rn->tn_type->t_tspec;
if (ln->tn_op != CON && rn->tn_op != CON)
return;
if (!is_integer(lt) || !is_integer(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_SIZE - 1)))) {
/* nonportable character comparison, op %s */
warning(230, op_name(op));
return;
}
if ((hflag || pflag) && rt == CHAR && ln->tn_op == CON &&
(ln->tn_val->v_quad < 0 ||
ln->tn_val->v_quad > (int)~(~0U << (CHAR_SIZE - 1)))) {
/* nonportable character comparison, op %s */
warning(230, op_name(op));
return;
}
if (is_uinteger(lt) && !is_uinteger(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", op_name(op));
} else if (op == LT || op == GE || (hflag && op == LE)) {
/* comparison of %s with %s, op %s */
warning(162, type_name(ln->tn_type), "0", op_name(op));
}
return;
}
if (is_uinteger(rt) && !is_uinteger(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), op_name(op));
} else if (op == GT || op == LE || (hflag && op == GE)) {
/* comparison of %s with %s, op %s */
warning(162, "0", type_name(rn->tn_type), op_name(op));
}
return;
}
}
/*
* Return whether the expression can be used for static initialization.
*
* Constant initialization 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, ADDR, NAME, STRING and
* CON. Type conversions are allowed if they do not change binary
* representation (including width).
*/
bool
constant_addr(const tnode_t *tn, const sym_t **symp, ptrdiff_t *offsp)
{
const 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 constant_addr(tn->tn_right, symp, offsp);
else if (tn->tn_right->tn_op != CON)
return false;
/* FALLTHROUGH */
case PLUS:
offs1 = offs2 = 0;
if (tn->tn_left->tn_op == CON) {
offs1 = (ptrdiff_t)tn->tn_left->tn_val->v_quad;
if (!constant_addr(tn->tn_right, &sym, &offs2))
return false;
} 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 (!constant_addr(tn->tn_left, &sym, &offs1))
return false;
} else {
return false;
}
*symp = sym;
*offsp = offs1 + offs2;
return true;
case ADDR:
if (tn->tn_left->tn_op == NAME) {
*symp = tn->tn_left->tn_sym;
*offsp = 0;
return true;
} else {
/*
* If this would be the front end of a compiler we
* would return a label instead of 0, at least if
* 'tn->tn_left->tn_op == STRING'.
*/
*symp = NULL;
*offsp = 0;
return true;
}
case CVT:
t = tn->tn_type->t_tspec;
ot = tn->tn_left->tn_type->t_tspec;
if ((!is_integer(t) && t != PTR) ||
(!is_integer(ot) && ot != PTR)) {
return false;
}
#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
return constant_addr(tn->tn_left, symp, offsp);
default:
return false;
}
}
/*
* 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*/false)
if (strg1->st_tspec == CHAR)
COPY(st_cp);
else
COPY(st_wcp);
strg1->st_len = len - 1; /* - NUL */
free(strg2);
return strg1;
}
static bool
is_confusing_precedence(op_t op, op_t lop, bool lparen, op_t rop, bool rparen)
{
if (op == SHL || op == SHR) {
if (!lparen && (lop == PLUS || lop == MINUS))
return true;
if (!rparen && (rop == PLUS || rop == MINUS))
return true;
return false;
}
if (op == LOGOR) {
if (!lparen && lop == LOGAND)
return true;
if (!rparen && rop == LOGAND)
return true;
return false;
}
lint_assert(op == BITAND || op == BITXOR || op == BITOR);
if (!lparen && lop != op) {
if (lop == PLUS || lop == MINUS)
return true;
if (lop == BITAND || lop == BITXOR)
return true;
}
if (!rparen && rop != op) {
if (rop == PLUS || rop == MINUS)
return true;
if (rop == BITAND || rop == BITXOR)
return true;
}
return false;
}
/*
* 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;
if (!hflag)
return;
debug_node(tn, 0);
lint_assert(modtab[tn->tn_op].m_binary);
for (ln = tn->tn_left; ln->tn_op == CVT; ln = ln->tn_left)
continue;
for (rn = tn->tn_right; rn->tn_op == CVT; rn = rn->tn_left)
continue;
if (is_confusing_precedence(tn->tn_op,
ln->tn_op, ln->tn_parenthesized,
rn->tn_op, rn->tn_parenthesized)) {
/* precedence confusion possible: parenthesize! */
warning(169);
}
}