….when I have warnings that will tell me about it.
So quite a few Rants have gone by about the behaviour of modern compilers around undefined behaviour.
The C standard says what happens when signed integers overflow is undefined…. so some compiler writers conclude that you have written the program so signed integer overflow cannot happen.
This is additional information that the optimizer may use.
Now I have been playing with gcc-7.2’s -Wstrict-overflow=N
This warning is not enabled by default or by -Wall or -Wextra.
I hit a largish code base with this and this is the sort of thing it finds, in practice. ie. This isn’t a theoretical debate about the merits and expectations of UB, this is a collection of practical examples in an industrial code base.
Examples have been extracted from body of code and simplified for readability reasons..
Message: assuming signed overflow does not occur when distributing negation across division
unsigned u = -i / 10;
Actually that is sort of cool. Especially on this tiny embedded target that doesn’t have hardware implemented multiply and divide. So the compilers intent of translating that to
unsigned u = i / -10;
…is actually a fairly sane.
But wait? What is it whinging about?
Aha! The signed int gotcha.
-INTN_MIN is an overflow. It’s undefined. So that simple obvious transformation is potentially behaviour changing.
Message: assuming signed overflow does not occur when simplifying division
if(remainder < 0)
{
remainder = -(remainder);
if(remainder*10 > denominator*10/2)
{
Again: If it ignores the possibility that remainder is INT_MIN to start with, it can assume remainder is positive in the next if.
But honestly, what do you as a programmer expect to happen in a case like that?
Message: assuming signed overflow does not occur when changing X +- C1 cmp C2 to X cmp C2 -+ C1
if( i +1 > 10)
ie. It’s transforming that to if( i > 9)
Again, that only makes sense if i+1 doesn’t overflow.
Message: assuming signed overflow does not occur when simplifying ‘/’ or ‘%’ to ‘>>’ or …
(i+1) / 2
Message: assuming signed overflow does not occur when simplifying conditional
These tend to be harder to spot and understand, so I haven’t a clear simple example of them. If anyone cares enough I might do the work to extract and prove an example.
So how have I been cleaning up the warnings?
In some cases doing the transformation by hand, as it actually is better and more reliable code.
In some cases say, oh bugger, that’s really a bug, the api of the function said it handled an int16_t input parameter, but we don’t actually handle the INT16_MIN case correctly at all. So let me fix that.
The notion of a UB by itself sucks, UB optimizations with static warnings is actually surprisingly useful.
-WliterallyAll would be very appreciated.
The historical reason why -Wall doesn’t enable all warnings is that warnings have been gradually added to the compiler over time. Adding new warnings to existing options could cause builds to fail after upgrading gcc.
Moreover, some pairs of warnings are incompatible (in the sense that any code accepted by one would be rejected by the other). An example of this is -Wstrict-prototypes and -Wtraditional.
I’m aware of that, though I still find it wrong that -Wall doesn’t actually include the new warnings, a build breaking on upgrade with -Wall is in my opinion the more logical outcome. I would rather have flags like -Wall4.9 that would remain constant on upgrades so no one who’s just using that subset of the warnings breaks their build. -Wall can then remain true to its meaning. Seeing that the ship has sailed on that a long time ago, I still would like to have a -WliterallyAll (can be called something else) that would include -Wall -Wextra and others like -Wstrict-overflow.
These ones can’t be and don’t have to be included.
I really like the idea of
-Wall-from=$VERSION, and you could even support-Wall-from=latestfor people who truly are okay with their builds breaking whenever they upgrade their compiler.clangsupports-Weverythingwhich I’ve tried, and it happily spews out contradictory warnings (“Padding bytes added to this structure”, “No padding bytes have been added to this packed structure!”) along with (in my opinion) useless warnings (“converting char to int without cast”).Yep, -Weverything can be amusing, but it really does throw everything at the code.
So your -WliterallyAll would not enable literally all warnings either? I’m not sure how that solves the problem.
Now this is a neat idea that I can get behind.
Correction: I have been informed that new warnings actually have been added to -Wall on multiple occasions.
The better explanation for why -Wall leaves many warnings disabled is that many of them are just not useful most of the time. The manual states:
In other words, it might be better to think of -Wall not as “all warnings”, but as “all generally useful warnings”.
Except the -Weffc++ warnings, those are really annoying and are not really about actual problems in your code.