Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rustc only warns when value that overflows is assigned

Tags:

rust

I am finding what I think is a very strange behaviour. Rustc panics when when a variable overflows at runtime; this makes sense to me. However, it only raises a warning when value that overflows is assigned at compile time. Shouldn't that be a compile time error? Otherwise, two behaviours seem inconsistent.

I expect a compile time error:

fn main() {
    let b: i32 = 3_000_000_000;
    println!("{}", b);
}

Produces:

<anon>:2:18: 2:31 warning: literal out of range for i32, #[warn(overflowing_literals)] on by default
<anon>:2     let b: i32 = 3_000_000_000;

Playground 1

This makes sense to me:

fn main() {
    let b: i32 = 30_000;
    let c: i32 = 100_000;
    let d = b * c;
    println!("{}", d);
}

Produces:

thread '<main>' panicked at 'arithmetic operation overflowed', <anon>:4
playpen: application terminated with error code 101

Playground 2

Edit:

Given the comment by FrancisGagné, and me discovering that Rust implements operators that check for overflow during the operation, for example checked_mul, I see that one needs to implement overflow checks themselves. Which makes sense, because release version should be optimized, and constantly checking for overflows could get expensive. So I no longer see the "inconsistency". However, I am still surprised, that assigning a value that would overflow does not lead to compile time error. In golang it would: Go Playground

like image 652
Akavall Avatar asked Oct 19 '22 16:10

Akavall


1 Answers

Actually, your comments are not consistent with the behavior you observe:

  • in your first example: you get a compile-time warning, which you ignore, and thus the compiler deduces that you want wrapping behavior
  • in your second example: you get a run-time error

The Go example is similar to the first Rust example (except that Go, by design, does not have warnings).


In Rust, an underflow or overflow results in an unspecified value which can be ! or bottom in computer science, a special value indicating that the control flow diverge which in general means either abortion or exception.

This specification allows:

  • instrumenting the Debug mode to catch all overflows at the very point at which they occur
  • not instrumenting1 the Release mode (and using wrapping arithmetic there)

and yet have both modes be consistent with the specification.

1Not instrumenting by default, you can if you choose and for a relatively modest performance cost outside of heavy numeric code activate the overflow checks in Release with a simple flag.


On the cost of overflow checks: the current Rust/LLVM situation is helpful for debugging but has not really been optimized. Thus, in this framework, overflow checks cost. If the situation improves, then rustc might decide, one day, to activate overflow checking by default even in Release.

In Midori (a Microsoft experimental OS developed in a language similar to C#), overflow check was turned on even in Release builds:

In Midori, we compiled with overflow checking on by default. This is different from stock C#, where you must explicitly pass the /checked flag for this behavior. In our experience, the number of surprising overflows that were caught, and unintended, was well worth the inconvenience and cost. But it did mean that our compiler needed to get really good at understanding how to eliminate unnecessary ones.

Apparently, they improved their compiler so that:

  • it would reason about the ranges of variables, and statically eliminate bounds checks and overflow checks when possible
  • it would aggregate checks as much as possible (a single check for multiple potentially overflowing operations)

The latter is only to be done in Release (you lose precision) but reduces the number of branches.

So, what cost remain?

Potentially different arithmetic rules that get in the way of optimizations:

  • in regular arithmetic, 64 + x - 128 can be optimized to x - 64; with overflow checks activated the compiler might not be able to perform this optimization
  • vectorization can be hampered too, if the compiler does not have overflow checking vector built-ins
  • ...

Still, unless the code is heavily numeric (scientific simulations or graphics, for example), then it might impact it indeed.

like image 140
Matthieu M. Avatar answered Oct 21 '22 06:10

Matthieu M.