-
Notifications
You must be signed in to change notification settings - Fork 1.2k
fix: compiler warning wconversion #222
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
e19268f
to
7df1796
Compare
Oh boy, another consequence of C/C++ language stupidity. (I complained a lot in https://www.nayuki.io/page/summary-of-c-cpp-integer-rules ) While your patch addresses the most visible aspect of the problem - which is the compiler warning - I do not believe there is an adequate solution to the underlying issue. Let's just simplify that bit of code to: vector<uint8_t> dataCodewords;
int val = condition ? 1 : 0
dataCodewords[i] |= val; Conceptually speaking, the dataCodewords[i] = dataCodewords[i] | val; The patch you wrote essentially does this: dataCodewords[i] = dataCodewords[i] | static_cast<uint8_t>(val); But the problem is that So while your patch fixes the very visible compiler warning, it doesn't do anything about the second, implicit, silent conversion to And yes, I am bitter about C/C++'s integer conversion rules and how they apply implicitly and how they're correct 99.9% of the time but have subtle edge cases 0.1% of the time where things blow up in your face (e.g. |
Thanks for this complete answer and yes cpp conversion rules are complex. Even if you are correct for the expanded/standalone part of the code, I'm not sure that this applies for the full line. So I did a try with compiler explorer code example the compiler effectively make a difference and properly use the correct assembly code instruction with the proposed cast. And hence reflect this by showing the compiler warning only without the cast. What would be your suggestion here to fix this compiler warning ? Should we cast the result of the Or operation (before assignation) ? |
I usually read x86 assembly code, and this would be one of the few times I read ARM assembly. I see the point that you're making, where the first C++ statement turns into a cluster of instructions that uses sign extension from 8 bits to whatever the register width is (presumably 64 bits), and the second statement turns into a cluster that uses But I think the assembly output you showed is a red herring. I'm pretty sure it's compiled at -O0 optimization level, because I know that good compiled code has way fewer instructions to accomplish simple tasks like this. There is a good chance that at higher optimization, both lines of C++ code would generate the same machine code, especially as the compiler knows that both sides of the bitwise-OR operation fit in The reasoning that I gave in my previous message is based on the C++ abstract machine, which is upstream of the machine-dependent code generation process. I am reasoning about how the arithmetic would work on all possible machines. I probably have compiled the codebase with So I think the only truly correct fix is the rather ugly expansion: |
Okay, I did a bit more investigation and it confirms what I suspected. Pasting my qrcodegen.hpp and qrcodegen.cpp into the Godbolt Compiler Explorer, I tried a few things. I am using the flags When compiling with essentially any version of Clang (from 5.0.0 up to the current 20.1.0), I get no warnings whatsoever. (Guess I optimized my workflow for Clang, eh?) When compiling with GCC, from version 4.7.1 (first supporting C++11) through to 9.5, I get 3 conversion warnings: From GCC 10.1 to the current 14.2, I lose the bottom two warnings and only still have the top warning - which is the one you reported. So GCC has gotten smarter over time at analyzing value ranges and how operations affect ranges, and it suppresses unnecessary warnings even though implicit conversions are still happening behind our backs. In the past, GCC was unable to prove that I am almost 100% sure that your cast is technically incorrect because like I said, the Another interesting tidbit is that Intel ICC 2021.10.0 even complains about a particular conversion when assigning to a scalar variable: I think the moral of the story here is that there are tons of implicit conversions happening in the codebase. I was aware of this in the back of my mind as I was writing the code, and I could humanly prove that the value ranges were correct and there would be no loss of information (or unwanted changes in values). But getting the compiler to prove these assertions is a different story, and as we see, different compilers have different capabilities at analyzing and proving things. In the end, adding casts just says "I know what I'm doing; shut up compiler", but does not change the result of the calculations - because I already designed them to be correct in the first place. |
Let me emphasize one more time that when performing an operation Consider this very simple program: #include <stdint.h>
int main() {
uint16_t x = 0xFFFF;
uint16_t y = 0xFFFF;
x *= y;
} This code produces no warnings on GCC or Clang, even with the flags On most platforms, Now, Even if we tweak the values to avoid overflow, say with |
Tthis fixes the wconversion warning
Note: With this warning, the "-Werror" compiler option, can't be enabled