int new_capacity = vec->capacity * 2;
assert(new_capacity > vec->capacity); int new_capacity = vec->capacity * 2;
assert(new_capacity > vec->capacity);But if you are a C programmer and speaking honestly, you need to have a better understanding of undefined behavior in C before you write any more code. Checking for signed overflow in C is extremely difficult to do correctly. This is a good starting point: https://blog.regehr.org/archives/213.
Not sure I understand why you think the assert() gets removed. Is this documented somewhere?
Hint: vec->capacity could be 0
Signed overflow (in int * 2) is an UB. If it happens, compiler is free to do anything. Eg. assume your assert can never happen and remove it.
Let me explain: Because signed overflow is an UB, compiler assumes that you would never write a code that overflows like that (because you write C, the assupmtion is that you are omnipotent), and knowing that, remove the assert that could possibly never ever happen, since it's a dead code anyway.
I've spend more than 6 years writting embedded software in C. God I love Rust.
I do not think its plausible that the compiler is emitting code which checks for an overflow and then in a deterministic fashion skips all dependent logic there after. Im sorry, but I think this line of argument has been taken to such an extreme it has detached from reality.
But all this is beside the point. In C, you may not invoke undefined behavior and then expect anything at all about the result. With malicious input, UB can and will result in RCE.
vec.capacity = 0;
Next, line 26: int new_capacity = vec->capacity * 2;
So we get new_capacity = 0 * 2, so new_capacity is 0. The assert: assert(new_capacity > vec->capacity);
Which translates to 0 > 0, it triggers a fail and we are done. If the compiler were to 'optimize' that out, then the compiler just introduced a very serious and fatal bug in our program, and we didn't even overflow or UB.So that assert works as intended, it forces new_capacity to grow until it hits INT_MAX or UB, at which point it will assert.
No, you don't see the point. Writing code in C that invokes undefined behavior is wrong, full stop. You seem to think that it's only wrong if you, the programmer, can imagine how it would go wrong.
> If the compiler were to 'optimize' that out, then the compiler just introduced a very serious and fatal bug in our program, and we didn't even overflow or UB.
Sure, in this case, the compiler can't optimize the assertion out. But it certainly can change the condition in the assertion so that it won't fire on overflow.
Once again, why would a compiler do that? To satisfy the Rust community's desire to spread FUD throughout the C community? (sorry to be so blunt, but im really scratching my head here)
A more logical thought process here is that signed operations aren't uniform across architectures and are sometimes emulated, leading to UB for certain edge cases, like overflow. Its more practical to believe that a future C standard will fix this loophole rather than think compilers will exploit this and punish their own community.
Very not impressed with this thread.
The theory is that constraint propagation enables optimizations that aren't possible otherwise. In particular, it lets the compiler avoid emitting code for conditions it judges are impossible to occur. This usually doesn't help much with C, but can be useful with C++, and many of the optimization decisions are driven by C++ performance.
To satisfy the Rust community's desire to spread FUD throughout the C community?
I'm a big fan of C, and consider it my primary language. I have barely used Rust and am not part of the Rust community.
A more logical thought process here is that signed operations aren't uniform across architectures and are sometimes emulated, leading to UB for certain edge cases, like overflow.
Personally, I would love if this was the vision for C that compiler writers held. But it is not, and has not been for at least a decade.
Its more practical to believe that a future C standard will fix this loophole rather than think compilers will exploit this and punish their own community.
Unfortunately there is no chance that this will happen. That ship has sailed. Compilers are taking ever greater advantage of undefined behavior for purposes of optimization. Overflowing a signed integer is undefined behavior, therefor real-world compilers can and do strip out checks that they determine would require undefined behavior to be occur.
I realize this is hard to believe, but try this article as an intro: https://blogs.msdn.microsoft.com/oldnewthing/20140627-00/?p=...
Or even better, just look at the assembly that is generated here:
#include <assert.h>
int add_one(int num) {
int increased = num + 1;
assert(increased > num);
return increased;
}
Both GCC and Clang completely remove the assert from the generated code: https://godbolt.org/g/5k43pV. To confirm, try adding "unsigned" to the "int" declarations and notice that it then keeps the check.I don't think either removes the check yet in the exact multiply by two case that you offer, but it is not safe to depend on this behavior.
Very not impressed with this thread.
I can see why you feel that way, but I think if you research this further you'll come to appreciate the stern warnings that people are giving you in this thread. C does not work the you want it to. Your choices are to adapt and avoid undefined behavior, or switch to a better language. I'm sticking with C because I think it's the best choice for what I do, but you need to be aware of the dangers if you are going to use it.
Sadly, a lot of people choose the third choice: write code in what they imagine C to be, fiddle with it until it compiles without warnings and, if they're trying extra hard, passes limited testing with Valgrind. Then they ship it.
If you wanted to check overflow condition, this assert doesn't help. If the capacity was almost MAX_INT, and you multiply it be 2, then compiler has freedom to do whatever, and not catch it because it must never be happening in the first place.
I found out about it through https://github.com/rohe/pysaml2/issues/451