Summary: | Wrong warning comparison of promoted ~unsigned with unsigned | ||
---|---|---|---|
Product: | gcc | Reporter: | Fredrik Hederstierna <fredrik.hederstierna> |
Component: | c | Assignee: | Not yet assigned to anyone <unassigned> |
Status: | RESOLVED DUPLICATE | ||
Severity: | minor | CC: | doko, gcc-bugs, gjl, manu, michael.malone, wolter.hellmundvega |
Priority: | P3 | Keywords: | diagnostic |
Version: | 4.8.2 | ||
Target Milestone: | --- | ||
See Also: | https://gcc.gnu.org/bugzilla/show_bug.cgi?id=107465 | ||
Host: | i686-pc-linux-gnu | Target: | arm-elf-gcc,x86 |
Build: | 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 |
Description
Fredrik Hederstierna
2008-12-01 08:35:08 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? 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? As I said, for the first case we optimize away the promotions before the warning code comes along. 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]) { /.../ 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? *** Bug 38370 has been marked as a duplicate of this bug. *** 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 ~(int)u2 ie. This is a bug #ifdef UINT #include <stdint.h> #define TYPE uint16_t #else #define TYPE unsigned short int #endif #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); #else result = (variable_b == (TYPE) ~variable_a); #endif 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 and gcc gcc_bug.c -W -Wall -DUINT -o bug 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. 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? Still present in 5.3.1 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. 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 *** 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? |