c-family: Fix up -W*conversion on bitwise &/|/^ [PR101537]

The following testcases emit a bogus -Wconversion warning.  This is because
conversion_warning function doesn't handle BIT_*_EXPR (only unsafe_conversion_p
that is called during the default: case, and that one doesn't handle
SAVE_EXPRs added because the unsigned char & or | operands promoted to int
have side-effects and =| or =& is used.

The patch handles BIT_IOR_EXPR/BIT_XOR_EXPR like the last 2 operands of
COND_EXPR by recursing on the two operands, if either of them doesn't fit
into the narrower type, complain.  BIT_AND_EXPR too, but first it needs to
handle some special cases that unsafe_conversion_p does, namely when one
of the two operands is a constant.

This fixes completely the pr101537.c test and for C also pr103881.c
and doesn't regress anything in the testsuite, for C++ pr103881.c still
emits the bogus warnings.
This is because while the C FE emits in that case a SAVE_EXPR that
conversion_warning can handle already, C++ FE emits
TARGET_EXPR <D.whatever, ...>, something | D.whatever
etc. and conversion_warning handles COMPOUND_EXPR by "recursing" on the
rhs.  To handle that case, we'd need for TARGET_EXPR on the lhs remember
in some hash map the mapping from D.whatever to the TARGET_EXPR and when
we see D.whatever, use corresponding TARGET_EXPR initializer instead.

2022-01-11  Jakub Jelinek  <jakub@redhat.com>

	PR c/101537
	PR c/103881
gcc/c-family/
	* c-warn.c (conversion_warning): Handle BIT_AND_EXPR, BIT_IOR_EXPR
	and BIT_XOR_EXPR.
gcc/testsuite/
	* c-c++-common/pr101537.c: New test.
	* c-c++-common/pr103881.c: New test.
This commit is contained in:
Jakub Jelinek 2022-01-11 19:11:51 +01:00
parent 0378f563b0
commit 20e4a5e573
3 changed files with 74 additions and 0 deletions

View File

@ -1304,6 +1304,34 @@ conversion_warning (location_t loc, tree type, tree expr, tree result)
|| conversion_warning (loc, type, op2, result));
}
case BIT_AND_EXPR:
if (TREE_CODE (expr_type) == INTEGER_TYPE
&& TREE_CODE (type) == INTEGER_TYPE)
for (int i = 0; i < 2; ++i)
{
tree op = TREE_OPERAND (expr, i);
if (TREE_CODE (op) != INTEGER_CST)
continue;
/* If one of the operands is a non-negative constant
that fits in the target type, then the type of the
other operand does not matter. */
if (int_fits_type_p (op, c_common_signed_type (type))
&& int_fits_type_p (op, c_common_unsigned_type (type)))
return false;
/* If constant is unsigned and fits in the target
type, then the result will also fit. */
if (TYPE_UNSIGNED (TREE_TYPE (op)) && int_fits_type_p (op, type))
return false;
}
/* FALLTHRU */
case BIT_IOR_EXPR:
case BIT_XOR_EXPR:
return (conversion_warning (loc, type, TREE_OPERAND (expr, 0), result)
|| conversion_warning (loc, type, TREE_OPERAND (expr, 1),
result));
default_:
default:
conversion_kind = unsafe_conversion_p (type, expr, result, true);

View File

@ -0,0 +1,26 @@
/* PR c/101537 */
/* { dg-do compile } */
/* { dg-options "-Wconversion" } */
int
foo ()
{
int aaa = 1;
unsigned char bbb = 0;
bbb |= aaa ? 1 : 0;
return bbb;
}
int
bar (unsigned char x, int f)
{
x |= f ? 1 : 0;
return x;
}
int
baz (unsigned char x, int f)
{
x = x | f ? 1 : 0;
return x;
}

View File

@ -0,0 +1,20 @@
/* PR c/103881 */
/* { dg-do compile } */
/* { dg-options "-Wconversion" } */
unsigned char bar (void);
void
foo (void)
{
unsigned char t = 0;
t |= bar ();
t |= bar () & bar (); /* { dg-bogus "conversion from 'int' to 'unsigned char' may change value" "" { xfail c++ } } */
t &= bar () & bar (); /* { dg-bogus "conversion from 'int' to 'unsigned char' may change value" "" { xfail c++ } } */
t = bar () & bar ();
unsigned char a = bar ();
t |= a & a;
t |= bar () & a; /* { dg-bogus "conversion from 'int' to 'unsigned char' may change value" "" { xfail c++ } } */
t |= a & bar (); /* { dg-bogus "conversion from 'int' to 'unsigned char' may change value" "" { xfail c++ } } */
}