/*	$NetBSD: msg_132.c,v 1.53 2025/01/03 01:27:35 rillig Exp $	*/
# 3 "msg_132.c"

// Test for message: conversion from '%s' to '%s' may lose accuracy [132]

/*
 * NetBSD's default lint flags only include a single -a, which only flags
 * narrowing conversions from long.  To get warnings for all narrowing
 * conversions, -a needs to be given more than once.
 *
 * https://gnats.netbsd.org/14531
 */

/* lint1-extra-flags: -aa -X 351 */

typedef unsigned char u8_t;
typedef unsigned short u16_t;
typedef unsigned int u32_t;
typedef unsigned long long u64_t;
typedef signed char s8_t;
typedef signed short s16_t;
typedef signed int s32_t;
typedef signed long long s64_t;

_Bool cond;
char ch;

u8_t u8;
u16_t u16;
u32_t u32;
u64_t u64;

s8_t s8;
s16_t s16;
s32_t s32;
s64_t s64;

const char *ptr;

struct bit_fields {
	unsigned u1:1;
	unsigned u2:2;
	unsigned u3:3;
	unsigned u4:4;
	unsigned u5:5;
	unsigned u6:6;
	unsigned u7:7;
	unsigned u8:8;
	unsigned u9:9;
	unsigned u10:10;
	unsigned u11:11;
	unsigned u12:12;
	unsigned u32:32;
} bits;


void
unsigned_to_unsigned(void)
{
	/* expect+1: warning: conversion from 'unsigned short' to 'unsigned char' may lose accuracy [132] */
	u8 = u16;
	/* expect+1: warning: conversion from 'unsigned int' to 'unsigned char' may lose accuracy [132] */
	u8 = u32;
	/* expect+1: warning: conversion from 'unsigned long long' to 'unsigned char' may lose accuracy [132] */
	u8 = u64;

	u16 = u8;
	/* expect+1: warning: conversion from 'unsigned int' to 'unsigned short' may lose accuracy [132] */
	u16 = u32;
	/* expect+1: warning: conversion from 'unsigned long long' to 'unsigned short' may lose accuracy [132] */
	u16 = u64;

	u32 = u8;
	u32 = u16;
	/* expect+1: warning: conversion from 'unsigned long long' to 'unsigned int' may lose accuracy [132] */
	u32 = u64;

	u64 = u8;
	u64 = u16;
	u64 = u32;
}

void
unsigned_to_signed(void)
{
	/* expect+1: warning: conversion from 'unsigned short' to 'signed char' may lose accuracy [132] */
	s8 = u16;
	/* expect+1: warning: conversion from 'unsigned int' to 'signed char' may lose accuracy [132] */
	s8 = u32;
	/* expect+1: warning: conversion from 'unsigned long long' to 'signed char' may lose accuracy [132] */
	s8 = u64;

	s16 = u8;
	/* expect+1: warning: conversion from 'unsigned int' to 'short' may lose accuracy [132] */
	s16 = u32;
	/* expect+1: warning: conversion from 'unsigned long long' to 'short' may lose accuracy [132] */
	s16 = u64;

	s32 = u8;
	s32 = u16;
	/* expect+1: warning: conversion from 'unsigned long long' to 'int' may lose accuracy [132] */
	s32 = u64;

	s64 = u8;
	s64 = u16;
	s64 = u32;
}

void
signed_to_unsigned(void)
{
	/* expect+1: warning: conversion from 'short' to 'unsigned char' may lose accuracy [132] */
	u8 = s16;
	/* expect+1: warning: conversion from 'int' to 'unsigned char' may lose accuracy [132] */
	u8 = s32;
	/* expect+1: warning: conversion from 'long long' to 'unsigned char' may lose accuracy [132] */
	u8 = s64;

	u16 = s8;
	/* expect+1: warning: conversion from 'int' to 'unsigned short' may lose accuracy [132] */
	u16 = s32;
	/* expect+1: warning: conversion from 'long long' to 'unsigned short' may lose accuracy [132] */
	u16 = s64;

	u32 = s8;
	u32 = s16;
	/* expect+1: warning: conversion from 'long long' to 'unsigned int' may lose accuracy [132] */
	u32 = s64;

	u64 = s8;
	u64 = s16;
	u64 = s32;
}

void
signed_to_signed(void)
{
	/* expect+1: warning: conversion from 'short' to 'signed char' may lose accuracy [132] */
	s8 = s16;
	/* expect+1: warning: conversion from 'int' to 'signed char' may lose accuracy [132] */
	s8 = s32;
	/* expect+1: warning: conversion from 'long long' to 'signed char' may lose accuracy [132] */
	s8 = s64;

	s16 = s8;
	/* expect+1: warning: conversion from 'int' to 'short' may lose accuracy [132] */
	s16 = s32;
	/* expect+1: warning: conversion from 'long long' to 'short' may lose accuracy [132] */
	s16 = s64;

	s32 = s8;
	s32 = s16;
	/* expect+1: warning: conversion from 'long long' to 'int' may lose accuracy [132] */
	s32 = s64;

	s64 = s8;
	s64 = s16;
	s64 = s32;
}

/*
 * Before tree.c 1.268 from 2021-04-06, lint wrongly warned that conversion
 * to _Bool might lose accuracy.  C99 6.3.1.2 defines a special conversion
 * rule from scalar to _Bool though by comparing the value to 0.
 */
_Bool
to_bool(long a, long b)
{
	/* seen in fp_lib.h, function wideRightShiftWithSticky */
	return a | b;
}

/* ARGSUSED */
const char *
cover_build_plus_minus(const char *arr, double idx)
{
	if (idx > 0.0)
		/* expect+2: error: operands of '+' have incompatible types 'pointer to const char' and 'double' [107] */
		/* expect+1: error: function 'cover_build_plus_minus' expects to return value [214] */
		return arr + idx;
	return arr + (unsigned int)idx;
}

int
non_constant_expression(void)
{
	/*
	 * Even though this variable definition looks like a constant, it
	 * does not fall within C's definition of an integer constant
	 * expression.  Due to that, lint does not perform constant folding
	 * on the expression built from this variable and thus doesn't know
	 * that the conversion will always succeed.
	 */
	const int not_a_constant = 8;
	/* expect+1: warning: conversion from 'unsigned long long' to 'int' may lose accuracy [132] */
	return not_a_constant * 8ULL;
}

/*
 * PR 36668 notices that lint wrongly complains about the possible loss.
 *
 * 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.
 */
void
be16dec(void)
{
	/*
	 * Before tree.c 1.444 from 2022-05-26, lint complained that the
	 * conversion from 'int' to 'unsigned short' may lose accuracy.
	 */
	u16 = (u16_t)u8 << 8 | u8;
}

/*
 * Since tree.c 1.434 from 2022-04-19, lint infers the possible values of
 * expressions of the form 'integer & constant', see can_represent.
 */
void
be32enc(void)
{
	u8 = u32 >> 24 & 0xff;
	u8 = u32 >> 16 & 0xff;
	u8 = u32 >> 8 & 0xff;
	u8 = u32 & 0xff;
}

void
test_ic_mult(void)
{
	/* expect+1: warning: conversion from 'int' to 'unsigned char' may lose accuracy [132] */
	u8 = u8 * u8;
	u16 = u8 * u8;
	/* expect+1: warning: conversion from 'int' to 'unsigned short' may lose accuracy [132] */
	u16 = u16 * u8;
	u32 = u16 * u16;

	u32 = u16 * 65537ULL;
	/* expect+1: warning: conversion from 'unsigned long long' to 'unsigned int' may lose accuracy [132] */
	u32 = u16 * 65538ULL;

	u16 = 0 * u16;
	u16 = 1 * u16;
	/* expect+1: warning: conversion from 'int' to 'unsigned short' may lose accuracy [132] */
	u16 = 2 * u16;

	// from __BITS, __SHIFTIN, __SHIFTOUT
	u32 = (u16 & 1023ULL) / 1ULL * 1024ULL | (u16 & 1023ULL) / 1ULL * 1ULL;

	s8 = 1 * s8;
	s16 = 1 * s16;
	s32 = 1 * s32;
	s64 = 1 * s64;

	/* expect+1: warning: conversion from 'int' to 'signed char' may lose accuracy [132] */
	s8 = 2 * s8;
	/* expect+1: warning: conversion from 'int' to 'short' may lose accuracy [132] */
	s16 = 2 * s16;
	// No warning, as there is no narrowing conversion.
	s32 = 2 * s32;
	// No warning, as there is no narrowing conversion.
	s64 = 2 * s64;

	/* expect+1: warning: conversion from 'int' to 'signed char' may lose accuracy [132] */
	s8 = -1 * s8;
	/* expect+1: warning: conversion from 'int' to 'short' may lose accuracy [132] */
	s16 = -1 * s16;
	// No warning, as there is no narrowing conversion.
	s32 = -1 * s32;
	// No warning, as there is no narrowing conversion.
	s64 = -1 * s64;
}

void
test_ic_div(void)
{
	u8 = u8 / u8;
	/* expect+1: warning: conversion from 'int' to 'unsigned char' may lose accuracy [132] */
	u8 = u16 / u8;
	u16 = u8 / u8;
	u16 = u32 / 65536;
	/* expect+1: warning: conversion from 'unsigned int' to 'unsigned short' may lose accuracy [132] */
	u16 = u32 / 65535;

	s8 = s8 / 1;
	s16 = s16 / 1;
	s32 = s32 / 1;
	s64 = s64 / 1;

	/* expect+1: warning: conversion from 'int' to 'signed char' may lose accuracy [132] */
	s8 = s8 / -1;
	/* expect+1: warning: conversion from 'int' to 'short' may lose accuracy [132] */
	s16 = s16 / -1;
	// No warning, as there is no narrowing conversion.
	s32 = s32 / -1;
	// No warning, as there is no narrowing conversion.
	s64 = s64 / -1;
}

void
test_ic_mod(void)
{
	/* The result is between 0 and 254. */
	u8 = u64 % u8;

	/* The result is between 0 and 255. */
	u8 = u64 % 256;

	/* The result is between 0 and 256. */
	/* expect+1: warning: conversion from 'unsigned long long' to 'unsigned char' may lose accuracy [132] */
	u8 = u64 % 257;

	/* The result is between 0 and 1000. */
	/* expect+1: warning: conversion from 'unsigned long long' to 'unsigned char' may lose accuracy [132] */
	u8 = u64 % 1000;
	/* expect+1: warning: conversion from 'unsigned long long' to 'unsigned int:9' may lose accuracy [132] */
	bits.u9 = u64 % 1000;
	bits.u10 = u64 % 1000;
	u16 = u64 % 1000;

	s8 = s16 % s8;
	/* expect+1: warning: conversion from 'int' to 'signed char' may lose accuracy [132] */
	s8 = s16 % s16;
	s8 = s64 % 1;
	s8 = s64 % (s16 & 1);
	/* expect+1: warning: conversion from 'long long' to 'signed char' may lose accuracy [132] */
	s8 = s64 % (s16 & 0);
	s8 = (s64 & 0x7f) % s64;
	/* expect+1: warning: conversion from 'long long' to 'signed char' may lose accuracy [132] */
	s8 = (s64 & 0xff) % s64;
}

void
test_ic_plus(void)
{
	/* expect+1: warning: conversion from 'long long' to 'signed char' may lose accuracy [132] */
	s8 = -129 + s64 % 1;
	s8 = -128 + s64 % 1;
	s8 = 127 + s64 % 1;
	/* expect+1: warning: conversion from 'long long' to 'signed char' may lose accuracy [132] */
	s8 = 128 + s64 % 1;

	/* expect+2: warning: conversion of negative constant -129 to unsigned type 'unsigned long long' [222] */
	/* expect+1: warning: conversion from 'unsigned long long' to 'signed char' may lose accuracy [132] */
	s8 = -129 + u64 % 1;
	/* expect+2: warning: conversion of negative constant -128 to unsigned type 'unsigned long long' [222] */
	/* expect+1: warning: conversion from 'unsigned long long' to 'signed char' may lose accuracy [132] */
	s8 = -128 + u64 % 1;
	s8 = 127 + u64 % 1;
	/* expect+1: warning: conversion from 'unsigned long long' to 'signed char' may lose accuracy [132] */
	s8 = 128 + u64 % 1;

	u8 = 0 + u64 % 1;
	u8 = 255 + u64 % 1;
	/* expect+1: warning: conversion from 'unsigned long long' to 'unsigned char' may lose accuracy [132] */
	u8 = 256 + u64 % 1;

	u8 = s8 + 0x80;
	u16 = s16 + 0x8000;
	u32 = s32 + 0x80000000;
	u64 = s64 + 0x8000000000000000;

	// XXX: No warnings since portable_rank_cmp is the same for both sides.
	bits.u11 = bits.u10 + bits.u10 + 1;
	bits.u11 = bits.u10 + bits.u10 + 2;
	bits.u11 = bits.u10 + 1024;
	bits.u11 = bits.u10 + 1025;

	u8 = bits.u7 + bits.u7 + 1;
	/* expect+1: warning: conversion from 'int' to 'unsigned char' may lose accuracy [132] */
	u8 = bits.u7 + bits.u7 + 2;
	u8 = bits.u7 + 128;
	/* expect+1: warning: conversion from 'int' to 'unsigned char' may lose accuracy [132] */
	u8 = bits.u7 + 129;

	// The result of the second '+' wraps around, thus the warning,
	// even though the final result fits in a u16.
	/* expect+1: warning: conversion from 'unsigned int' to 'unsigned short' may lose accuracy [132] */
	u16 = u32 % 0x00010000 + 0x80000000 + 0x80000000;

	/* expect+1: warning: conversion from 'unsigned int' to 'unsigned short' may lose accuracy [132] */
	u16 = u32 % 0x00010000 + 0xffff8000;
	/* expect+1: warning: conversion from 'unsigned int' to 'short' may lose accuracy [132] */
	s16 = u32 % 0x00010000 + 0xffff8000;

	/* expect+1: warning: conversion from 'long long' to 'unsigned short' may lose accuracy [132] */
	u16 = s64 % 0x00010000 + 0xffffffffLL + -0xffffffffLL;
	/* expect+1: warning: conversion from 'int' to 'unsigned short' may lose accuracy [132] */
	u16 = s32 % 0x00010000 + 0x7fff0000 + -0x7fff0000;
	/* expect+1: warning: conversion from 'unsigned int' to 'unsigned short' may lose accuracy [132] */
	u16 = u32 % 0x00010000 + 0xffff0000 + 0x00010000;

	s8 = '0' + s64 % 10;

	ptr = ptr + 3;
}

void
test_ic_minus(void)
{
	// Shift the range [0x00 to 0xff] to [-0x80 to 0x7f].
	s8 = (s64 & 0xff) - 0x80;

	// Sign-extend the lowest bits.
	s8 = ((s64 & 0xff) ^ 0x80) - 0x80;
	s16 = ((s64 & 0xffff) ^ 0x8000) - 0x8000;
	/* expect+1: warning: extra bits set to 0 in conversion of 'unsigned int' to 'long long', op '&' [309] */
	s32 = ((s64 & 0xffffffff) ^ 0x80000000) - 0x80000000;

	// Trying to sign-extend, but with off-by-one errors.
	/* expect+1: warning: conversion from 'long long' to 'signed char' may lose accuracy [132] */
	s8 = ((s64 & 0xff) ^ 0x80) - 0x7f;
	/* expect+1: warning: conversion from 'long long' to 'signed char' may lose accuracy [132] */
	s8 = ((s64 & 0xff) ^ 0x80) - 0x81;

	u8 = s8 - -0x80;
	u16 = s16 - -0x8000;
	u32 = s32 - -0x80000000;
	u64 = s64 - -0x8000000000000000;

	ptr = ptr - 3;
	s64 = ptr + 3 - ptr;
}

void
test_ic_shl(void)
{
	u64 = u64 << u64;
	s64 = s64 << s64;

	u16 = u8 << 8;
	/* expect+1: warning: conversion from 'int' to 'unsigned short' may lose accuracy [132] */
	u16 = u8 << 9;
	u32 = u16 << 16;
	// XXX: missing warning as UINT has the same rank as INT, see portable_rank_cmp.
	u32 = u16 << 17;
	/* expect+1: warning: shift amount 56 is greater than bit-size 32 of 'int' [122] */
	u64 = u8 << 56;
	u64 = (u64_t)u8 << 56;
	// XXX: missing warning, as the operand types of '=' are the same, thus no conversion.
	u64 = (u64_t)u8 << 57;
	/* expect+1: warning: shift amount 48 is greater than bit-size 32 of 'int' [122] */
	u64 = u16 << 48;
	u64 = (u64_t)u16 << 48;
	// XXX: missing warning, as the operand types of '=' are the same, thus no conversion.
	u64 = (u64_t)u16 << 49;
	/* expect+1: warning: shift amount 32 equals bit-size of 'unsigned int' [267] */
	u64 = u32 << 32;
	u64 = (u64_t)u32 << 32;
	// XXX: missing warning, as the operand types of '=' are the same, thus no conversion.
	u64 = (u64_t)u32 << 33;
}

void
test_ic_shr(void)
{
	u64 = u64 >> u64;
	s64 = s64 >> s64;

	u32 = u64 >> 32;
	/* expect+1: warning: conversion from 'unsigned long long' to 'unsigned int' may lose accuracy [132] */
	u32 = u64 >> 31;
	u16 = u64 >> 48;
	u16 = u32 >> 16;
	u8 = u64 >> 56;
	u8 = u32 >> 24;
	u8 = u16 >> 8;

	/*
	 * No matter whether the big integer is signed or unsigned, the
	 * result of '&' is guaranteed to be an unsigned value.
	 */
	u8 = (s64 & 0xf0) >> 4;
	u8 = (s8 & 0xf0) >> 4;
}

void
test_ic_bitand(void)
{
	u8 = u8 & u16;

	/* expect+1: warning: conversion from 'unsigned int' to 'unsigned char' may lose accuracy [132] */
	u8 = u16 & u32;
}

void
test_ic_bitxor(void)
{
	/* expect+1: warning: conversion from 'int' to 'unsigned char' may lose accuracy [132] */
	u8 = u8 ^ u16;
	u16 = u8 ^ u16;

	// Sign-extend.
	s8 = (u8 ^ 0x80) - 0x80;
	s16 = (u16 ^ 0x8000) - 0x8000;
	s32 = (u32 ^ 0x80000000) - 0x80000000;
	s64 = (u64 ^ 0x8000000000000000) - 0x8000000000000000;
}

void
test_ic_bitor(void)
{
	/* expect+1: warning: conversion from 'int' to 'unsigned char' may lose accuracy [132] */
	u8 = u8 | u16;
	u16 = u8 | u16;
	/* expect+1: warning: conversion from 'unsigned int' to 'unsigned short' may lose accuracy [132] */
	u16 = u8 | u32;
	u32 = u8 | u32;
	/* expect+1: warning: conversion from 'unsigned long long' to 'unsigned int' may lose accuracy [132] */
	u32 = u8 | u64;
	u64 = u8 | u64;
}

void
test_ic_quest_colon(char c1, char c2)
{
	/* Both operands are representable as char. */
	ch = cond ? '?' : ':';

	/*
	 * Both operands are representable as char. Clang-Tidy 17 wrongly
	 * warns about a narrowing conversion from 'int' to signed type
	 * 'char'.
	 */
	ch = cond ? c1 : c2;

	/*
	 * Mixing s8 and u8 results in a number from -128 to 255, which neither
	 * fits in s8 nor u8.
	 */
	/* expect+1: warning: conversion from 'int' to 'signed char' may lose accuracy [132] */
	s8 = cond ? s8 : u8;
	/* expect+1: warning: conversion from 'int' to 'unsigned char' may lose accuracy [132] */
	u8 = cond ? s8 : u8;
}

void
test_ic_con(void)
{
	/* expect+1: warning: assignment of negative constant -1 to unsigned type 'unsigned char' [164] */
	u8 = -1;
	u8 = 0;
	u8 = 255;
	/* expect+1: warning: constant truncated by assignment [165] */
	u8 = 256;

	/* expect+1: warning: conversion of 'int' to 'signed char' is out of range [119] */
	s8 = -129;
	s8 = -128;
	s8 = 127;
	/* expect+1: warning: conversion of 'int' to 'signed char' is out of range [119] */
	s8 = 128;
}

void
test_ic_cvt(void)
{
	u16 = (u32 & 0x0000ff00);
	u16 = (u32_t)(u32 & 0x0000ff00);
	u16 = (u16_t)u32;
	u16 = (u8_t)(u32 & 0xffff) << 8;
	u16 = (int)3.0;

	u8 = (u8_t)(u64 & 0x0f);
	u8 = (u8_t)(u64 & 0x0f) << 4;
	/* expect+1: warning: conversion from 'int' to 'unsigned char' may lose accuracy [132] */
	u8 = (u8_t)(u64 & 0x0f) << 5;
}

unsigned char
test_bit_fields(unsigned long long m)
{
	/* expect+1: warning: conversion from 'unsigned long long' to 'unsigned int:3' may lose accuracy [132] */
	bits.u3 = bits.u32 & m;

	bits.u5 = bits.u3 & m;
	bits.u32 = bits.u5 & m;

	/* expect+1: warning: conversion from 'unsigned long long' to 'unsigned char' may lose accuracy [132] */
	return bits.u32 & m;
}

void
compare_bit_field_to_integer_constant(void)
{
	static _Bool b;
	static struct {
		short s16:15;
		unsigned short u16:15;
		int s32:15;
		unsigned u32:15;
		long long s64:15;
		unsigned long long u64:15;
	} s;

	// Since decl.c 1.180 from 2021-05-02 and before tree.c 1.624 from
	// 2024-03-12, lint warned about a possible loss of accuracy [132]
	// when promoting an 'unsigned long long' bit-field to 'int'.
	b = s.s16 == 0;
	b = s.u16 == 0;
	b = s.s32 == 0;
	b = s.u32 == 0;
	b = s.s64 == 0;
	b = s.u64 == 0;
	b = !b;
}

/*
 * Before tree.c 1.626 from 2024-03-26, the usual arithmetic conversions for
 * bit-field types with the same base type but different widths simply took
 * the type of the left operand, leading to wrong warnings about loss of
 * accuracy when the right operand was wider than the left operand.
 */
void
binary_operators_on_bit_fields(void)
{
	struct {
		u64_t u15:15;
		u64_t u48:48;
		u64_t u64;
	} s = { 0, 0, 0 };

	u64 = s.u15 | s.u48;
	u64 = s.u48 | s.u15;
	u64 = s.u15 | s.u48 | s.u64;
	u64 = s.u64 | s.u48 | s.u15;
	cond = (s.u15 | s.u48 | s.u64) != 0;
	cond = (s.u64 | s.u48 | s.u15) != 0;

	// Before tree.c from 1.638 from 2024-05-01, lint wrongly warned:
	// warning: conversion of 'int' to 'int:4' is out of range [119]
	s32 = 8 - bits.u3;
}

unsigned char
combine_arithmetic_and_bit_operations(void)
{
	return 0xc0 | (u32 & 0x07c0) / 64;
}
