Module Name: src Committed By: rillig Date: Thu May 26 09:26:00 UTC 2022
Modified Files: src/tests/usr.bin/xlint/lint1: msg_132.c msg_132.exp src/usr.bin/xlint/lint1: tree.c Log Message: lint: do not warn about loss in accuracy if the actual value fits The expression 'any & 0xff' can always be assigned to 'uint8_t' without loss of any value bits. In the same way, '(any & 0xff) << 8' can always be assigned to 'uint16_t'. Previously, lint warned about these cases. Fix these wrong warnings by tracking the possible values of integer expressions across a single expression. Fixes PR 36668, so that <sys/endian.h> does not need to be cluttered with useless casts anymore. To generate a diff of this commit: cvs rdiff -u -r1.10 -r1.11 src/tests/usr.bin/xlint/lint1/msg_132.c cvs rdiff -u -r1.9 -r1.10 src/tests/usr.bin/xlint/lint1/msg_132.exp cvs rdiff -u -r1.443 -r1.444 src/usr.bin/xlint/lint1/tree.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/tests/usr.bin/xlint/lint1/msg_132.c diff -u src/tests/usr.bin/xlint/lint1/msg_132.c:1.10 src/tests/usr.bin/xlint/lint1/msg_132.c:1.11 --- src/tests/usr.bin/xlint/lint1/msg_132.c:1.10 Thu May 26 07:03:03 2022 +++ src/tests/usr.bin/xlint/lint1/msg_132.c Thu May 26 09:26:00 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: msg_132.c,v 1.10 2022/05/26 07:03:03 rillig Exp $ */ +/* $NetBSD: msg_132.c,v 1.11 2022/05/26 09:26:00 rillig Exp $ */ # 3 "msg_132.c" // Test for message: conversion from '%s' to '%s' may lose accuracy [132] @@ -148,15 +148,23 @@ typedef unsigned long long u64_t; /* * PR 36668 notices that lint wrongly complains about the possible loss. - * The expression 'uint8_t << 8' is guaranteed to fit into an 'unsigned short'. - * 'unsigned short | unsigned char' is guaranteed to fit into 'unsigned short' + * + * The expression 'u8_t << 8' is guaranteed to fit into an 'u16_t', and its + * lower 8 bits are guaranteed to be clear. 'u16_t | u8_t' is guaranteed to + * fit into 'u16_t'. + * + * Since tree.c 1.444 from 2022-05-26, lint tracks simple bitwise and + * arithmetic constraints across a single expression. */ static inline u16_t be16dec(const void *buf) { const u8_t *p = buf; - /* expect+1: warning: conversion from 'int' to 'unsigned short' may lose accuracy [132] */ + /* + * Before tree.c 1.444 from 2022-05-26, lint complained that the + * conversion from 'int' to 'unsigned short' may lose accuracy. + */ return ((u16_t)p[0]) << 8 | p[1]; } Index: src/tests/usr.bin/xlint/lint1/msg_132.exp diff -u src/tests/usr.bin/xlint/lint1/msg_132.exp:1.9 src/tests/usr.bin/xlint/lint1/msg_132.exp:1.10 --- src/tests/usr.bin/xlint/lint1/msg_132.exp:1.9 Thu May 26 07:03:03 2022 +++ src/tests/usr.bin/xlint/lint1/msg_132.exp Thu May 26 09:26:00 2022 @@ -25,4 +25,3 @@ msg_132.c(99): warning: conversion from msg_132.c(125): error: operands of '+' have incompatible types (pointer != double) [107] msg_132.c(125): warning: function 'cover_build_plus_minus' expects to return value [214] msg_132.c(141): warning: conversion from 'unsigned long long' to 'int' may lose accuracy [132] -msg_132.c(160): warning: conversion from 'int' to 'unsigned short' may lose accuracy [132] Index: src/usr.bin/xlint/lint1/tree.c diff -u src/usr.bin/xlint/lint1/tree.c:1.443 src/usr.bin/xlint/lint1/tree.c:1.444 --- src/usr.bin/xlint/lint1/tree.c:1.443 Thu May 26 06:43:58 2022 +++ src/usr.bin/xlint/lint1/tree.c Thu May 26 09:26:00 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: tree.c,v 1.443 2022/05/26 06:43:58 rillig Exp $ */ +/* $NetBSD: tree.c,v 1.444 2022/05/26 09:26:00 rillig Exp $ */ /* * Copyright (c) 1994, 1995 Jochen Pohl @@ -37,7 +37,7 @@ #include <sys/cdefs.h> #if defined(__RCSID) -__RCSID("$NetBSD: tree.c,v 1.443 2022/05/26 06:43:58 rillig Exp $"); +__RCSID("$NetBSD: tree.c,v 1.444 2022/05/26 09:26:00 rillig Exp $"); #endif #include <float.h> @@ -50,6 +50,15 @@ __RCSID("$NetBSD: tree.c,v 1.443 2022/05 #include "lint1.h" #include "cgram.h" +typedef struct integer_constraints { + int64_t smin; /* signed minimum */ + int64_t smax; /* signed maximum */ + uint64_t umin; /* unsigned minimum */ + uint64_t umax; /* unsigned maximum */ + uint64_t bset; /* bits that are definitely set */ + uint64_t bclr; /* bits that are definitely clear */ +} integer_constraints; + static tnode_t *build_integer_constant(tspec_t, int64_t); static void check_pointer_comparison(op_t, const tnode_t *, const tnode_t *); @@ -96,6 +105,137 @@ static void check_precedence_confusion(t extern sig_atomic_t fpe; +static integer_constraints +ic_any(const type_t *tp) +{ + integer_constraints c; + + lint_assert(is_integer(tp->t_tspec)); + unsigned int sz = type_size_in_bits(tp); + uint64_t vbits = value_bits(sz); + if (is_uinteger(tp->t_tspec)) { + c.smin = INT64_MIN; + c.smax = INT64_MAX; + c.umin = 0; + c.umax = vbits; + c.bset = 0; + c.bclr = ~c.umax; + } else { + c.smin = (int64_t)-1 - (int64_t)(vbits >> 1); + c.smax = (int64_t)(vbits >> 1); + c.umin = 0; + c.umax = UINT64_MAX; + c.bset = 0; + c.bclr = 0; + } + return c; +} + +static integer_constraints +ic_con(const type_t *tp, const val_t *v) +{ + integer_constraints c; + + lint_assert(is_integer(tp->t_tspec)); + int64_t s = v->v_quad; + uint64_t u = (uint64_t)s; + c.smin = s; + c.smax = s; + c.umin = u; + c.umax = u; + c.bset = u; + c.bclr = ~u; + return c; +} + +static integer_constraints +ic_cvt(const type_t *ntp, const type_t *otp, integer_constraints a) +{ + + if (type_size_in_bits(ntp) > type_size_in_bits(otp) && + is_uinteger(otp->t_tspec)) + return a; + return ic_any(ntp); +} + +static integer_constraints +ic_bitand(integer_constraints a, integer_constraints b) +{ + integer_constraints c; + + c.smin = INT64_MIN; + c.smax = INT64_MAX; + c.umin = 0; + c.umax = UINT64_MAX; + c.bset = a.bset & b.bset; + c.bclr = a.bclr | b.bclr; + return c; +} + +static integer_constraints +ic_bitor(integer_constraints a, integer_constraints b) +{ + integer_constraints c; + + c.smin = INT64_MIN; + c.smax = INT64_MAX; + c.umin = 0; + c.umax = UINT64_MAX; + c.bset = a.bset | b.bset; + c.bclr = a.bclr & b.bclr; + return c; +} + +static integer_constraints +ic_shl(const type_t *tp, integer_constraints a, integer_constraints b) +{ + integer_constraints c; + unsigned int amount; + + if (b.smin == b.smax && b.smin >= 0 && b.smin < 64) + amount = (unsigned int)b.smin; + else if (b.umin == b.umax && b.umin < 64) + amount = (unsigned int)b.umin; + else + return ic_any(tp); + + c.smin = INT64_MIN; + c.smax = INT64_MAX; + c.umin = 0; + c.umax = UINT64_MAX; + c.bset = a.bset << amount; + c.bclr = a.bclr << amount | (((uint64_t)1 << amount) - 1); + return c; +} + +static integer_constraints +ic_expr(const tnode_t *tn) +{ + integer_constraints lc, rc; + + switch (tn->tn_op) { + case CON: + return ic_con(tn->tn_type, tn->tn_val); + case CVT: + lc = ic_expr(tn->tn_left); + return ic_cvt(tn->tn_type, tn->tn_left->tn_type, lc); + case SHL: + lc = ic_expr(tn->tn_left); + rc = ic_expr(tn->tn_right); + return ic_shl(tn->tn_type, lc, rc); + case BITAND: + lc = ic_expr(tn->tn_left); + rc = ic_expr(tn->tn_right); + return ic_bitand(lc, rc); + case BITOR: + lc = ic_expr(tn->tn_left); + rc = ic_expr(tn->tn_right); + return ic_bitor(lc, rc); + default: + return ic_any(tn->tn_type); + } +} + static const char * op_name(op_t op) { @@ -2211,22 +2351,16 @@ static bool can_represent(const type_t *tp, const tnode_t *tn) { - if (tn->tn_op == BITAND) { - const tnode_t *rn = tn->tn_right; - tspec_t nt; - - if (!(rn != NULL && rn->tn_op == CON && - is_integer(rn->tn_type->t_tspec))) - return false; + debug_step("%s: type '%s'", __func__, type_name(tp)); + debug_node(tn); - uint64_t nmask = value_bits(size_in_bits(nt = tp->t_tspec)); - if (!is_uinteger(nt)) - nmask >>= 1; + uint64_t nmask = value_bits(type_size_in_bits(tp)); + if (!is_uinteger(tp->t_tspec)) + nmask >>= 1; - uint64_t omask = (uint64_t)rn->tn_val->v_quad; - if ((omask & ~nmask) == 0) - return true; - } + integer_constraints c = ic_expr(tn); + if ((~c.bclr & ~nmask) == 0) + return true; return false; }