Discussion:
warning: conversion from ‘int’ to ‘char’ may change value
Umesh Kalappa
2018-09-17 12:00:51 UTC
Permalink
Hi All,

When we try to compile the below case from trunk gcc we get the below
warning (-Wconversion) i.e

void start(void) {
char n = 1;
char n1 = 0x01;
n &= ~n1;
}

$xgcc -S warn.c -nostdinc -Wconversion
warning: conversion from ‘int’ to ‘char’ may change value [-Wconversion]
n &= ~n1;

typecast the expression like "n& = (char)~n1" and warning goes away .

and when we investigated the gcc source and warning coming from
unsafe_conversion_p@ gcc/c-family/c-common.c:1226

if (TYPE_PRECISION (type) < TYPE_PRECISION (expr_type))
give_warning = UNSAFE_OTHER;

where TYPE_PRECISION (type) is 8 for char and TYPE_PRECISION
(expr_type) is 32 as expected for int .

is that expected behavior of gcc ?

clang compiles with no warnings .

Thank you
~Umesh
David Brown
2018-09-17 14:14:42 UTC
Permalink
Post by Umesh Kalappa
Hi All,
When we try to compile the below case from trunk gcc we get the below
warning (-Wconversion) i.e
void start(void) {
char n = 1;
char n1 = 0x01;
n &= ~n1;
}
$xgcc -S warn.c -nostdinc -Wconversion
warning: conversion from ‘int’ to ‘char’ may change value [-Wconversion]
n &= ~n1;
typecast the expression like "n& = (char)~n1" and warning goes away .
and when we investigated the gcc source and warning coming from
if (TYPE_PRECISION (type) < TYPE_PRECISION (expr_type))
give_warning = UNSAFE_OTHER;
where TYPE_PRECISION (type) is 8 for char and TYPE_PRECISION
(expr_type) is 32 as expected for int .
is that expected behavior of gcc ?
clang compiles with no warnings .
This should really be on the gcc-help mailing list, rather than the
development list.

You could argue that in this particular case, since "n" ends up as 0,
the conversion from int to char will not change its value. But in a
more general case the warning is valid. "~n1" is done as an "int"
operation (that's the way C works), and the compiler is warning you that
you might be dealing with values too big to fit in the type "char".

gcc can help warn you about suspicious code that might be indicate a bug
- such as this case. If you don't want this kind of warning, don't
enable it. But if you /do/ enable the warning, pay attention to it -
your code here is certainly questionable. (For one thing, it is a good
rule that you should never use bitwise operators with signed types, and
you should never use a naked "char" with arithmetic or bitwise operations.)
Martin Sebor
2018-09-17 16:03:48 UTC
Permalink
Post by Umesh Kalappa
Hi All,
When we try to compile the below case from trunk gcc we get the below
warning (-Wconversion) i.e
void start(void) {
char n = 1;
char n1 = 0x01;
n &= ~n1;
}
$xgcc -S warn.c -nostdinc -Wconversion
warning: conversion from ‘int’ to ‘char’ may change value [-Wconversion]
n &= ~n1;
typecast the expression like "n& = (char)~n1" and warning goes away .
and when we investigated the gcc source and warning coming from
if (TYPE_PRECISION (type) < TYPE_PRECISION (expr_type))
give_warning = UNSAFE_OTHER;
where TYPE_PRECISION (type) is 8 for char and TYPE_PRECISION
(expr_type) is 32 as expected for int .
is that expected behavior of gcc ?
It looks like a bug to me.

Declaring n1 const avoids the warning at -O2 but in C but not
at -O0. That doesn't seem quite right -- GCC determines the
type of the bitwise AND expression to be different between
the optimization levels. In C++, declaring n1 const avoids
the warning regardless of optimization levels.

A bug report about these inconsistencies would be useful to
help us determine whether they are the expected result of
constant folding or whether there is, in fact, a subtle bug
(the difference is introduced in shorten_binary_op).

This is all quite interesting to me because is shows how even
fairly simple warnings fully implemented in the front-end and
so theoretically immune from false positives and negatives
can be fragile and prone to such problems, just like warnings
implemented in the middle-end. (I discussed some of these
issues in my talk on Implementing Static Analysis Checkers
In the GCC Middle-End at the last Cauldron.)

Martin
Liu Hao
2018-09-20 14:57:00 UTC
Permalink
In C++, declaring n1 const avoids the warning regardless of
optimization levels.
If the constant propagation is done at -O0, this could explain
the behavior.
Or do you mean that GCC remembers the type the data come from,
i.e. assuming char is signed, if n1 is of type char, then ~n1
is necessarily representable in a char, thus can be regarded
as of being of type char in its analysis?
In C++ adding the `const` qualifier makes `~n1` a constant expression.
In C it never is, regardless of qualifiers.

BTW, I am quite disappointed with such 'false' warnings, because by
performing a compound AND-and-ASSIGN operation on a `char` object I have
no interest in bits that don't fit into a `char`, be they ones or
zeroes. Perhaps there are scenarios where they shouldn't be ignored, but
I can't think of any.
--
Best regards,
L
Jason Merrill
2018-09-20 15:13:59 UTC
Permalink
Post by Martin Sebor
Post by Umesh Kalappa
Hi All,
When we try to compile the below case from trunk gcc we get the below
warning (-Wconversion) i.e
void start(void) {
char n = 1;
char n1 = 0x01;
n &= ~n1;
}
$xgcc -S warn.c -nostdinc -Wconversion
warning: conversion from ‘int’ to ‘char’ may change value [-Wconversion]
n &= ~n1;
typecast the expression like "n& = (char)~n1" and warning goes away .
and when we investigated the gcc source and warning coming from
if (TYPE_PRECISION (type) < TYPE_PRECISION (expr_type))
give_warning = UNSAFE_OTHER;
where TYPE_PRECISION (type) is 8 for char and TYPE_PRECISION
(expr_type) is 32 as expected for int .
is that expected behavior of gcc ?
It looks like a bug to me.
Declaring n1 const avoids the warning at -O2 but in C but not
at -O0. That doesn't seem quite right -- GCC determines the
type of the bitwise AND expression to be different between
the optimization levels. In C++, declaring n1 const avoids
the warning regardless of optimization levels.
A bug report about these inconsistencies would be useful to
help us determine whether they are the expected result of
constant folding or whether there is, in fact, a subtle bug
(the difference is introduced in shorten_binary_op).
This is all quite interesting to me because is shows how even
fairly simple warnings fully implemented in the front-end and
so theoretically immune from false positives and negatives
can be fragile and prone to such problems, just like warnings
implemented in the middle-end. (I discussed some of these
issues in my talk on Implementing Static Analysis Checkers
In the GCC Middle-End at the last Cauldron.)
Indeed, whether this sort of warning is a false positive depends on
values, which the optimizers can always do better with. We could
build some logic into the front end, e.g. recognize that a &
expression will always fit in the smallest of its operand types, but
on the other hand that duplicates things that other parts of the
compiler already deal with, so there's a question of how to share
these rules between front and middle end, like we're doing more of
with constant folding. Perhaps we could use the folder somehow.

Jason
Martin Sebor
2018-09-20 17:38:15 UTC
Permalink
Post by Martin Sebor
Post by Umesh Kalappa
Hi All,
When we try to compile the below case from trunk gcc we get the below
warning (-Wconversion) i.e
void start(void) {
char n = 1;
char n1 = 0x01;
n &= ~n1;
}
$xgcc -S warn.c -nostdinc -Wconversion
warning: conversion from ‘int’ to ‘char’ may change value [-Wconversion]
n &= ~n1;
[...]
Post by Martin Sebor
It looks like a bug to me.
Declaring n1 const avoids the warning at -O2 but in C but not
at -O0.
Perhaps at some optimization level, GCC determines that the
expression is safe (thus no longer emits the warning), i.e.
that n & ~n1 is necessarily representable in a char.
Post by Martin Sebor
That doesn't seem quite right -- GCC determines the
type of the bitwise AND expression to be different between
the optimization levels.
No, the type of this AND expression is always int. The question
is whether this int is necessarily representable in a char.
Post by Martin Sebor
In C++, declaring n1 const avoids the warning regardless of
optimization levels.
If the constant propagation is done at -O0, this could explain
the behavior.
Or do you mean that GCC remembers the type the data come from,
i.e. assuming char is signed, if n1 is of type char, then ~n1
is necessarily representable in a char, thus can be regarded
as of being of type char in its analysis?
What I'm saying is that the type that determines whether or
not to issue a warning in this case is computed in
the shorten_binary_op() function. The function is passed
the operands of the &= expression and returns the expression's
"new" type. When n1's value is known (i.e., when it's const
and with -O2) and fits in char, and when n's type is the char
(or under a bunch of other conditions), the function returns
the type char. Comments in the code indicate it's
an optimization. That may be fine as far as code correctness
goes but it doesn't seem quite right or robust to me because
it makes the warning appear inconsistent, both between
languages, and in C, between optimization levels. Delaying
the warning until a later stage (e.g., until folding as Jason
suggested) would make it more consistent. It wouldn't solve
all problems (e.g., it would still be prone to false positives
in unreachable code), but solving those by delaying it even
further could easily lead to others.

Martin
Eric Gallager
2018-09-20 18:15:20 UTC
Permalink
Post by Martin Sebor
Post by Martin Sebor
Post by Umesh Kalappa
Hi All,
When we try to compile the below case from trunk gcc we get the below
warning (-Wconversion) i.e
void start(void) {
char n = 1;
char n1 = 0x01;
n &= ~n1;
}
$xgcc -S warn.c -nostdinc -Wconversion
warning: conversion from ‘int’ to ‘char’ may change value [-Wconversion]
n &= ~n1;
[...]
Post by Martin Sebor
It looks like a bug to me.
Declaring n1 const avoids the warning at -O2 but in C but not
at -O0.
Perhaps at some optimization level, GCC determines that the
expression is safe (thus no longer emits the warning), i.e.
that n & ~n1 is necessarily representable in a char.
Post by Martin Sebor
That doesn't seem quite right -- GCC determines the
type of the bitwise AND expression to be different between
the optimization levels.
No, the type of this AND expression is always int. The question
is whether this int is necessarily representable in a char.
Post by Martin Sebor
In C++, declaring n1 const avoids the warning regardless of
optimization levels.
If the constant propagation is done at -O0, this could explain
the behavior.
Or do you mean that GCC remembers the type the data come from,
i.e. assuming char is signed, if n1 is of type char, then ~n1
is necessarily representable in a char, thus can be regarded
as of being of type char in its analysis?
What I'm saying is that the type that determines whether or
not to issue a warning in this case is computed in
the shorten_binary_op() function. The function is passed
the operands of the &= expression and returns the expression's
"new" type. When n1's value is known (i.e., when it's const
and with -O2) and fits in char, and when n's type is the char
(or under a bunch of other conditions), the function returns
the type char. Comments in the code indicate it's
an optimization. That may be fine as far as code correctness
goes but it doesn't seem quite right or robust to me because
it makes the warning appear inconsistent, both between
languages, and in C, between optimization levels. Delaying
the warning until a later stage (e.g., until folding as Jason
suggested) would make it more consistent. It wouldn't solve
all problems (e.g., it would still be prone to false positives
in unreachable code), but solving those by delaying it even
further could easily lead to others.
Martin
I think this is bug 40752: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=40752
(or some of the bugs related to it, or marked as duplicates of it)
Jason Merrill
2018-09-20 19:29:09 UTC
Permalink
Post by Martin Sebor
Post by Martin Sebor
Post by Umesh Kalappa
Hi All,
When we try to compile the below case from trunk gcc we get the below
warning (-Wconversion) i.e
void start(void) {
char n = 1;
char n1 = 0x01;
n &= ~n1;
}
$xgcc -S warn.c -nostdinc -Wconversion
warning: conversion from ‘int’ to ‘char’ may change value [-Wconversion]
n &= ~n1;
[...]
Post by Martin Sebor
It looks like a bug to me.
Declaring n1 const avoids the warning at -O2 but in C but not
at -O0.
Perhaps at some optimization level, GCC determines that the
expression is safe (thus no longer emits the warning), i.e.
that n & ~n1 is necessarily representable in a char.
Post by Martin Sebor
That doesn't seem quite right -- GCC determines the
type of the bitwise AND expression to be different between
the optimization levels.
No, the type of this AND expression is always int. The question
is whether this int is necessarily representable in a char.
Post by Martin Sebor
In C++, declaring n1 const avoids the warning regardless of
optimization levels.
If the constant propagation is done at -O0, this could explain
the behavior.
Or do you mean that GCC remembers the type the data come from,
i.e. assuming char is signed, if n1 is of type char, then ~n1
is necessarily representable in a char, thus can be regarded
as of being of type char in its analysis?
What I'm saying is that the type that determines whether or
not to issue a warning in this case is computed in
the shorten_binary_op() function. The function is passed
the operands of the &= expression and returns the expression's
"new" type. When n1's value is known (i.e., when it's const
and with -O2) and fits in char, and when n's type is the char
(or under a bunch of other conditions), the function returns
the type char. Comments in the code indicate it's
an optimization. That may be fine as far as code correctness
goes but it doesn't seem quite right or robust to me because
it makes the warning appear inconsistent, both between
languages, and in C, between optimization levels.
Lots of warnings vary between optimization levels, because
optimizations make more information available. I don't think that's
avoidable in general unless we want to more frequently enable some
optimization passes when certain warnings are enabled.

Jason
Martin Sebor
2018-09-20 19:53:14 UTC
Permalink
Post by Jason Merrill
Post by Martin Sebor
Post by Martin Sebor
Post by Umesh Kalappa
Hi All,
When we try to compile the below case from trunk gcc we get the below
warning (-Wconversion) i.e
void start(void) {
char n = 1;
char n1 = 0x01;
n &= ~n1;
}
$xgcc -S warn.c -nostdinc -Wconversion
warning: conversion from ‘int’ to ‘char’ may change value [-Wconversion]
n &= ~n1;
[...]
Post by Martin Sebor
It looks like a bug to me.
Declaring n1 const avoids the warning at -O2 but in C but not
at -O0.
Perhaps at some optimization level, GCC determines that the
expression is safe (thus no longer emits the warning), i.e.
that n & ~n1 is necessarily representable in a char.
Post by Martin Sebor
That doesn't seem quite right -- GCC determines the
type of the bitwise AND expression to be different between
the optimization levels.
No, the type of this AND expression is always int. The question
is whether this int is necessarily representable in a char.
Post by Martin Sebor
In C++, declaring n1 const avoids the warning regardless of
optimization levels.
If the constant propagation is done at -O0, this could explain
the behavior.
Or do you mean that GCC remembers the type the data come from,
i.e. assuming char is signed, if n1 is of type char, then ~n1
is necessarily representable in a char, thus can be regarded
as of being of type char in its analysis?
What I'm saying is that the type that determines whether or
not to issue a warning in this case is computed in
the shorten_binary_op() function. The function is passed
the operands of the &= expression and returns the expression's
"new" type. When n1's value is known (i.e., when it's const
and with -O2) and fits in char, and when n's type is the char
(or under a bunch of other conditions), the function returns
the type char. Comments in the code indicate it's
an optimization. That may be fine as far as code correctness
goes but it doesn't seem quite right or robust to me because
it makes the warning appear inconsistent, both between
languages, and in C, between optimization levels.
Lots of warnings vary between optimization levels, because
optimizations make more information available. I don't think that's
avoidable in general unless we want to more frequently enable some
optimization passes when certain warnings are enabled.
Sure, it's expected in the middle-end. I just don't remember
having seen it in a purely front-end warning. It also goes
against what I said in my talk (and what has been argued by
some is the main disadvantage of middle-end warnings): that
front-end only warnings are more consistent because they
aren't subject to the effects of optimization.

Martin
Jeff Law
2018-09-20 19:54:41 UTC
Permalink
Post by Martin Sebor
Post by Martin Sebor
Post by Umesh Kalappa
Hi All,
When we try to compile the below case from trunk gcc we get the below
warning (-Wconversion) i.e
void start(void) {
 char n = 1;
 char n1 = 0x01;
 n &=  ~n1;
}
$xgcc -S  warn.c -nostdinc -Wconversion
 warning: conversion from ‘int’ to ‘char’ may change value
[-Wconversion]
  n &=  ~n1;
[...]
Post by Martin Sebor
It looks like a bug to me.
Declaring n1 const avoids the warning at -O2 but in C but not
at -O0.
Perhaps at some optimization level, GCC determines that the
expression is safe (thus no longer emits the warning), i.e.
that n & ~n1 is necessarily representable in a char.
Post by Martin Sebor
That doesn't seem quite right -- GCC determines the
type of the bitwise AND expression to be different between
the optimization levels.
No, the type of this AND expression is always int. The question
is whether this int is necessarily representable in a char.
Post by Martin Sebor
In C++, declaring n1 const avoids the warning regardless of
optimization levels.
If the constant propagation is done at -O0, this could explain
the behavior.
Or do you mean that GCC remembers the type the data come from,
i.e. assuming char is signed, if n1 is of type char, then ~n1
is necessarily representable in a char, thus can be regarded
as of being of type char in its analysis?
What I'm saying is that the type that determines whether or
not to issue a warning in this case is computed in
the shorten_binary_op() function.  The function is passed
the operands of the &= expression and returns the expression's
"new" type.  When n1's value is known (i.e., when it's const
and with -O2) and fits in char, and when n's type is the  char
(or under a bunch of other conditions), the function returns
the type char.  Comments in the code indicate it's
an optimization.  That may be fine as far as code correctness
goes but it doesn't seem quite right or robust to me because
it makes the warning appear inconsistent, both between
languages, and in C, between optimization levels.  Delaying
the warning until a later stage (e.g., until folding as Jason
suggested) would make it more consistent.  It wouldn't solve
all problems (e.g., it would still be prone to false positives
in unreachable code), but solving those by delaying it even
further could easily lead to others.
We have long wanted to defer all the transformations in
shorten_binary_op and its close cousins. They really should be match.pd
patterns.

Kai and I did some poking around in this space a while back but nothing
that was mature enough to incorporate into the tree.
jeff
Post by Martin Sebor
Martin
Continue reading on narkive:
Loading...