Comment by Panzerschrek
1 day ago
Any change in basic arithmetic should be backwards-compatible. The easiest way to eliminate UB is to define signed overflow for existing types as 2-complement. But new types with different semantics (or maybe library wrappers) can be created - with saturation, panic on overflow, or even with arithmetic operations producing extended types (like u16 + u16 = u17, u8 * u8 = u16 ).
I'm looking through the UB paper https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2026/p31... and I haven't gotten to the good parts yet (how to handle the UB) but it looks like they're taking what I'd call a better approach. Runtime UB results from violation of an implicit contract (e.g. int arithmetic has an implicit contract that the result should not overflow), so they want to let you specify what should happen in those situations (panic, give some wrong result and keep going, call some kind of exception handler, etc.)
Defining int arithmetic as 2's complement would be a big mistake imho. It's fine if there's additional types for 2's complement, saturation, etc. But the only sane well-defined behaviour for int overflow is trap or panic (like in Ada), so in principle there should always be runtime checks. Otherwise you lose obvious invariants like n+1>n for all integers n. It's unfortunate that today's hardware makes runtime checks a big pain for integers even though it's done with no discernible overhead by the floating point hardware (IEEE-754 overflow trap etc). Oh well.