The bug in the above code has absolutely nothing to do with implicit widening. Here’s the same code written in Rust, but using explicit widening for the result of the expression since Rust demands that:
fn foo(x: i32) -> i64 {
(x * x) as i64
}
fn main() {
println!("{}", foo(2147483640)) // prints 64
}
And this has the same bug, even with explicit widening.
You are complaining about overflow of intermediate results, not the lossless expansion of a 32bit signed integer to a 64bit signed integer.
You might say that the compiler error about the missing cast in Rust would make you write the expression as x as i64 * x as i64 instead of what I wrote. If so, then that’s a complete coincidence and doesn’t mean that the explicit widening is somehow safer. Here’s the same code with a much lower chance of the user correctly changing it:
fn foo(x: i32) -> i64 {
let a = x * x;
... lots of code ...
a
}
Upon encountering the type error pointing at the final statement, the user would rewrite it as a as i64 and still have the bug.
Note that even with casting x to i64 the code still has an overflow bug, only for larger numbers (which probably makes the bug worse since it’s harder to encounter it). If you don’t know what domain your numbers take, use a BigInt type. No amount of explicit casting will help you if you don’t.
And again, the problem is overflow and not implicit widening. All of your examples amount to “guns kill people so we should ban straw hats.”
I have heard many people complain about lossless, implicit widening and every time they are actually complaining about something else that’s completely unrelated, usually overflow.