> Java-style wrapping integers should never be the default, this is arguably even worse than C and C++’s UB-on-overflow which at least permits an implementation to trap.
> Java-style wrapping integers should never be the default, this is arguably even worse than C and C++’s UB-on-overflow which at least permits an implementation to trap.
Either way there are explicit methods for doing wrapped, checked or saturating operations in every mode.
I imagine throw-on-overflow is slower than wrap-on-overflow.
C# can be configured to throw an exception when an int is overflowed, [0] but this behaviour isn't the default and is rarely used (typically it uses wrap-on-overflow). I imagine it might have a significant performance impact, but I'm not sure.
In a language like SPARK Ada, intended for formal verification, you can insist upon a rigorous proof that unintended overflow can never occur. That isn't an option for Safe Rust, at least not without significant breakthroughs in tooling.
[0] https://docs.microsoft.com/en-us/dotnet/csharp/language-refe...
The push for safe languages is motivated by pragmatism, not theoretical purity.
It depends on the CPU: MIPS has(had if you consider MIPS dead) some integer operations which trapped in case of integer overflow AFAIK the 'trapping operations' were as fast as the non-trapping operations (when no overflow occurred of course).
Unfortunately even though RISC-V is MIPS successor, it doesn't have those trapping operations :-(
I must admit I don't understand why no modern CPU ISA has these trapping instructions: explicit checks have an 'instruction cache cost', so users won't enable them which reduces security..
An obvious example: does this code result in a divide-by-zero? (I'll use C syntax.)
int myInt = INT_MAX;
++myInt;
int myOtherInt = 1000 / myInt;
If signed overflow is permitted to result in myInt holding zero, then we have a divide-by-zero. Not the kind of thing that should be left up to the particular platform.The behaviour of your Java code does not change when you move it from a 32-bit x86 machine to a 64-bit ARM machine. That's part of the appeal of Java. The same should be true of Safe Rust.
To put that another way: Safe Rust is remarkable because of its ambition: to be a truly safe language, while also having excellent real-world performance. It seems to be succeeding in doing both, without trading off on performance (Java, Go, C#) or safety (C++, and even Ada). If it starts compromising on either dimension, it becomes 'just another language'.
Integer overflow is a "program error." This case is handled by either "default" or "enabled" overflow checking:
* If checks are "enabled", then overflow must panic
* If checks are "default", then you'll get two's compliment wrapping
For implementations, if debug_assertions are enabled, then so must overflow checking be, unless the user specifically requests otherwise.
According to these rules, rustc today has "enabled" checking when debug_assertions is on, or when the user requests it via a flag. Otherwise, it leaves it to "default." If these checks ever become cheap enough, rustc may move to "enabled" in all cases by default. We'll see if that ever happens.
Integer division in Java never results in undefined behaviour. Divide-by-zero results in an exception, as does (Integer.MIN_VALUE / -1). In C/C++, both of those operations result in undefined behaviour. Modern JVMs have to generate some additional instructions to implement this [0], but I wonder if the real-world performance penalty is that substantial.
Another example is reading an uninitialized variable, which is undefined behaviour in C/C++. Does this footgun really improve performance, with modern compilers? I don't have a solid answer here but I suspect not.
> IMHO 'integer overflow is UB' should be scrapped
The C committee is opposed to radical change, they never want to step on the toes of exotic compilers and exotic hardware architectures, so I doubt they'll ever change it. I think it would be more realistic to ask the major compiler vendors to commit to never doing anything unsafe on signed integer overflow. I believe GCC has an 'opt-in' flag for this. I wonder what the performance cost is, if any. Perhaps it breaks some optimisations, but as you say, other fast languages like Rust seem to get by fine.
Still, I agree, I prefer consistent semantics between dev/prod as much as possible. Especially since there's methods to have checked/wrapping/etc arithmetic, so I can always go to those if I want the other behavior
Thankfully, this behavior is a flag which you can personally configure to be consistent across dev/prod: https://doc.rust-lang.org/cargo/reference/profiles.html
It does make sense when interpreting wrapping checks in dev as debug assertions
More like:
> "integer overflow checks are a painful/unacceptable performance degradation for some use-cases, but we still want to cough over-/under-flow bugs during testing"
Anyway luckily you can just enable integer overflow checks in release builds, which is not a uncommon setup in use-cases like server code.