Bug 38341 - Wrong warning comparison of promoted ~unsigned with unsigned
Summary: Wrong warning comparison of promoted ~unsigned with unsigned
Status: RESOLVED DUPLICATE of bug 107465
Alias: None
Product: gcc
Classification: Unclassified
Component: c (show other bugs)
Version: 4.8.2
: P3 minor
Target Milestone: ---
Assignee: Not yet assigned to anyone
Keywords: diagnostic
: 38370 (view as bug list)
Depends on:
Reported: 2008-12-01 08:35 UTC by Fredrik Hederstierna
Modified: 2023-06-29 14:52 UTC (History)
6 users (show)

See Also:
Host: i686-pc-linux-gnu
Target: arm-elf-gcc,x86
Known to work: 12.3.0, 13.1.0, 3.4.5
Known to fail: 10.1.0, 12.1.0, 4.8.2, 6.1.0
Last reconfirmed: 2014-01-29 00:00:00


Note You need to log in before you can comment on or make changes to this bug.
Description Fredrik Hederstierna 2008-12-01 08:35:08 UTC
Compiler gives wrong warning for "comparison of promoted ~unsigned with unsigned" when compiling with ARM-ELF.

Submit script for building arm-elf toolchain and testcode.

Compilation using;

arm-elf-gcc -c cast.c -W
cast.c: In function 'test_cast':
cast.c:13: warning: comparison of promoted ~unsigned with unsigned

Best Regards
Fredrik Hederstierna

FILE: cast.c

typedef unsigned char u8_t;
void test_cast(void) {
  unsigned char c1 = 0;
  unsigned char c2 = 0;  
  u8_t u1 = 0;
  u8_t u2 = 0;  
  if (c1 == (unsigned char)(~c2)) {
  if (u1 == (u8_t)(~u2)) {  // THIS WILL GIVE WARNING

FILE: build_toolchain.sh

set -e -x 





# set rwx access

umask 022 

# unpack tar-balls

tar xvjf "binutils-$BINUTILS_VERSION.tar.bz2" 
tar xvjf "gcc-$GCC_VERSION.tar.bz2" 
tar xvzf "newlib-$NEWLIB_VERSION.tar.gz" 
tar xvjf "gdb-$GDB_VERSION.tar.bz2" 
tar xvjf "insight-$INSIGHT_VERSION.tar.bz2" 

cd "$GCC_DIR"
ln -s "../$NEWLIB_DIR/newlib" newlib 
ln -s "../$NEWLIB_DIR/libgloss" libgloss 
cd ..

rm -fr build 
mkdir -p build/binutils build/gcc build/gdb build/insight build/newlib

# Build binutils

cd build/binutils 
"../../$BINUTILS_DIR/configure" --target="$TARGET" --prefix="$DEST" --disable-nls
make LDFLAGS=-s all install 

# Build GCC

cd ../gcc 
"../../$GCC_DIR/configure" --enable-languages=c,c++ --target="$TARGET" --prefix="$DEST" --with-gnu-as --with-gnu-ld --disable-nls --with-newlib --disable-__cxa_atexit --with-ecos
make LDFLAGS=-s all all-gcc all-target-libstdc++-v3 install install-gcc install-target-libstdc++-v3

# Build GDB and Insight

cd ../gdb
# Without insight
"../../$GDB_DIR/configure" --target="$TARGET" --prefix="$DEST"
make -w all install

cd ../insight
# With insight
"../../$INSIGHT_DIR/configure" --target="$TARGET" --prefix="$DEST"
make -w all install

# Remove uncompressed sources

cd ../.. 
Comment 1 Richard Biener 2008-12-01 10:01:23 UTC
  /* Warn if two unsigned values are being compared in a size larger
     than their original size, and one (and only one) is the result of
     a `~' operator.  This comparison will always fail.

     Also warn if one operand is a constant, and the constant does not
     have all bits set that are set in the ~ operand when it is
     extended.  */

note that integer promotion is done on the operand(!) of ~.  So u1 == (u8_t)(~u2)
is equal to

   (int)u1 == (int)(u8_t)(~(int)u2)

that we do not warn for the first case is because it is optimized to
u1 == ~u2 before.

Why do you think the warning is incorrect?
Comment 2 Fredrik Hederstierna 2008-12-01 12:40:29 UTC
Then why dont we get warning on the first if-statement?
Shouldnt these lines be equal?

  if (c1 == (unsigned char)(~c2)) {
  if (u1 == (u8_t)(~u2)) {  // THIS WILL GIVE WARNING

The first if-statement does not give warnings, should this be evaluated the same way?

if ((int)c1 == (int)(unsigned char)(~(int)c2)) {

My idea was that either of the if-statements are wrong. Either both or none should give warnings, or am I wrong? The typedef to "unsigned char" should be the same as using primitive types regarding this warning, or?
Comment 3 Richard Biener 2008-12-01 12:49:36 UTC
As I said, for the first case we optimize away the promotions before the warning
code comes along.
Comment 4 Fredrik Hederstierna 2008-12-01 12:55:13 UTC
Heres another example, then I do not think the warnings are due to optimization.
I have same warnings with both -O0 and -O3.

#include <stdio.h>

typedef unsigned char u8_t;

void test_cast(unsigned char c1, unsigned char c2, u8_t u1, u8_t u2)
  if (c1 == (unsigned char)(~c2)) {
    printf("No warning");
  if (c1 == ~c2) {
    printf("This gives warning");
  if (u1 == (u8_t)(~u2)) {
    printf("This gives warning");
  if ((unsigned char)u1 == (unsigned char)(~u2)) {
    printf("This gives warning");

The original code that caused this warnings are the TCP/IP stack lwIP, then I constructed this minimal example.

Original code from lwIP TCP/IP stack:

static u8_t ip_reassbitmap[IP_REASS_BUFSIZE / (8 * 8) + 1];
static const u8_t bitmap_bits[8] = { 0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01 };

      if (ip_reassbitmap[ip_reasslen / (8 * 8)] !=
        (u8_t) ~ bitmap_bits[ip_reasslen / 8 & 7]) {
Comment 5 Fredrik Hederstierna 2008-12-01 13:35:12 UTC
On Intel i386-GCC (4.2.3) we just get warning only for the line

  if (c1 == ~c2)

The other lines does not give warnings, so maybe its just the ARM-backend that catch this warning.

I guess you mean that for ARM target the optimization tree does things that silence the warning. Is it good that optimizations can silence possible warnings/errors? And that it differs depending on which backend I'm running?
Comment 6 Richard Biener 2008-12-02 12:12:03 UTC
*** Bug 38370 has been marked as a duplicate of this bug. ***
Comment 7 john.carter 2009-02-23 23:53:17 UTC
R Guenther said...
>  (int)u1 == (int)(u8_t)(~(int)u2)
>  that we do not warn for the first case is because it is optimized to
>    u1 == ~u2 before.
>   Why do you think the warning is incorrect?

I would expect 

  u2 to be promoted to a 4 byte int

  ~ to do the ones complement on a 4 byte int

  (unsigned char)~u2 to truncate to one byte discarding the most significant 3 bytes

  and then the result get promoted to an int to evaluate the ==

ie. The cast cannot get optimized away, it's not a null op, it discards the most significant bytes. 

ie. (int)(unsigned char)~(int)u2 is not equivalent to

ie. This is a bug
Comment 8 Michael Malone 2009-02-24 00:40:28 UTC
#ifdef UINT
  #include <stdint.h>
  #define TYPE uint16_t
  #define TYPE unsigned short int

#define VALUE 0xFF

int main(void);

int main() {
   TYPE variable_a = ~VALUE;
   TYPE variable_b = VALUE;
   TYPE result;

   #ifdef ASSIGN
   TYPE tmp = ~variable_a;
   result = (variable_b == tmp);
   result = (variable_b == (TYPE) ~variable_a);

   return 0;

Further to John's input, here is a sample program which shows up why this bug is interesting.  When one uses a typedef'd type, the promoted comparison warning is displayed.  When one does not, it isn't!

This is not the case for gcc-4.2.3 -both variants compile without warnings.

The compile commands I used were:
gcc gcc_bug.c -W -Wall -o bug
gcc gcc_bug.c -W -Wall -DUINT -o bug

Comment 9 Michael Malone 2009-02-24 00:43:39 UTC
I forgot to mention, if you assign to an intermediate variable, the warning also disappears which is the behaviour I would expect from an explicit cast.
Comment 10 Georg-Johann Lay 2014-01-29 16:00:58 UTC
This is still present for 4.8.2 and current trunk (future 4.9.0).

C++ works fine, i.e. just the warnings that I'd expect.

As this works as expected in good old 3.4.x, shouldn't this be marked as regression?
Comment 11 Daniele Alessandrelli 2016-03-10 10:47:43 UTC
Still present in 5.3.1
Comment 12 Manuel López-Ibáñez 2016-03-10 21:57:52 UTC
At this stage, this will not get fixed before GCC 7 is released in March 2017, if at all.

If somebody wants to see progress on this, they way forward is:

1) Put a breakpoint at warn_for_sign_compare () at /home/manuel/test1/src/gcc/c-family/c-common.c
2) Figure out exactly why one case is warned and the other is not.
3) Figure out exactly when the difference is introduced (very likely in shorten_compare in the same file). The C FE does "folding" (optimizing) while parsing even at -O0. It is a goal to stop doing this (without breaking anything else of course, so abundant testing that the generated code does not change is required).
4) Figure out how to fix the warning or avoid introducing a difference.

Even improving the warning text could help someone to figure out the problem (printing the types involved, explaining that the comparison is always false, etc.)

Any of the steps above would make it a bit easier for the next person to come along and continue the work until a fix is committed.

If you never debugged/modified GCC before, see https://gcc.gnu.org/wiki/GettingStarted#Basics:_Contributing_to_GCC_in_10_easy_steps

(In reply to Fredrik Hederstierna from comment #5)
> On Intel i386-GCC (4.2.3) we just get warning only for the line
>   if (c1 == ~c2)
> The other lines does not give warnings, so maybe its just the ARM-backend
> that catch this warning.

I don't see these differences. It works the same in both cases.
Comment 13 Andrew Pinski 2023-05-20 04:26:11 UTC
So this has been fixed on all of the active branches. Since PR 107465 was the one recorded in the changelog, closing as a dup of that one.

*** This bug has been marked as a duplicate of bug 107465 ***
Comment 14 wolter.hellmundvega 2023-06-29 14:52:13 UTC
I'm not sure this is fixed, please correct me if wrong, but

    #include <stdio.h>
    #include <stdint.h>

    int main(void)
        const uint8_t u = 0U;
        const uint8_t y = (uint8_t) ~u;

        return ((uint8_t) u != (uint8_t) ~y);

gives warning

    test.c: In function ‘main’:
    test.c:9:25: warning: comparison of promoted bitwise complement of an unsigned value with unsigned [-Wsign-compare]
        9 |     return ((uint8_t) u != (uint8_t) ~y);
          |                         ^~

Does this not mean that this issue is still present?