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;
 }

Reply via email to