How to enable overflow checks in const evaluation

I've recently noticed, that overflow checks are disabled in release mode even in const evaluation. For example:

pub const fn add(a: u8, b: u8) -> u8 {
    a + b
}

pub const FOO: u8 = add(200, 200);
pub const BAR: u8 = const { add(200, 200) };

fn main() {
    println!("{FOO}");
    println!("{BAR}");
}

This code will fail to compile in debug build with following errors:

compiler's output
   Compiling playground v0.0.1 (/playground)
error[E0080]: evaluation of constant value failed
 --> src/main.rs:2:5
  |
2 |     a + b
  |     ^^^^^ attempt to compute `200_u8 + 200_u8`, which would overflow
  |
note: inside `add`
 --> src/main.rs:2:5
  |
2 |     a + b
  |     ^^^^^
note: inside `FOO`
 --> src/main.rs:5:21
  |
5 | pub const FOO: u8 = add(200, 200);
  |                     ^^^^^^^^^^^^^

error[E0080]: evaluation of `BAR::{constant#0}` failed
 --> src/main.rs:2:5
  |
2 |     a + b
  |     ^^^^^ attempt to compute `200_u8 + 200_u8`, which would overflow
  |
note: inside `add`
 --> src/main.rs:2:5
  |
2 |     a + b
  |     ^^^^^
note: inside `BAR::{constant#0}`
 --> src/main.rs:6:29
  |
6 | pub const BAR: u8 = const { add(200, 200) };
  |                             ^^^^^^^^^^^^^

note: erroneous constant encountered
 --> src/main.rs:6:21
  |
6 | pub const BAR: u8 = const { add(200, 200) };
  |                     ^^^^^^^^^^^^^^^^^^^^^^^

note: erroneous constant encountered
  --> src/main.rs:10:16
   |
10 |     println!("{BAR}");
   |                ^^^

note: erroneous constant encountered
 --> src/main.rs:9:16
  |
9 |     println!("{FOO}");
  |                ^^^

note: erroneous constant encountered
 --> src/main.rs:9:15
  |
9 |     println!("{FOO}");
  |               ^^^^^
  |
  = note: this note originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)

note: erroneous constant encountered
  --> src/main.rs:10:15
   |
10 |     println!("{BAR}");
   |               ^^^^^
   |
   = note: this note originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0080`.
error: could not compile `playground` (bin "playground") due to 2 previous errors

But in release mode it builds and runs:

144
144

This worries me somewhat. I cannot say that this breaks any rules, because technically overflows are well defined in Rust, but this just feels wrong. To me overflow => bug. I understand compromise Rust made and I quite like it. But I don't understand why constant evaluation doesn't have stricter rules (it already does for example stricter unsafe code rules).

Is there some option to enable overflow checks in const eval regardless of release mode? Has this been discussed before? I couldn't find any answers myself. And if it really is impossible to to this now, do you think it should stay this way? It probably is possible for the compiler to do this, right?

2 Likes

checked_add is const:

pub const fn add(a: u8, b: u8) -> u8 {
    if let Some(x) = a.checked_add(b) {
        x
    } else {
        panic!("oh oh");
    }
}

pub const FOO: u8 = add(200, 200);
pub const BAR: u8 = add(200, 55);

fn main() {
    println!("{FOO}");
    println!("{BAR}");
}

Playground.

1 Like

const fn is a function that may be executed in both compile time and runtime. And its behavior should be identical regardless of its execution timing.

1 Like

I am aware of that. What I want is to be sure that even if I don't use explicitly checked methods, that still overflow bugs will be caught.

Interesting. This sounds right, but is there any rule/guarantee that would force that? Not all functions are deterministic (though it is probably impossible to write nondeterministic const function). It could be also argued that they didn't produce different results, since in const evaluation one panicked (and thus whole build failed).

1 Like

I'm not aware of a way (like a compiler flag) to change the current behaviour other than using explicitly checked operations, potentially guided by clippy.

Edit: overflow checking can be enabled with the overflow-checks option, which can be set in the Cargo profile config to enable it when building in release mode.

3 Likes

But that would force those checks to also appear in normal, non-const code in --release builds, which would make me even sadder. As I've stated before I quite like the compromise to disable those checks in release mode. I guess I want to have a cake and eat it too…

2 Likes

That must be true for functions which don't stop the compilation.
However, if overflow was a compile error, I think it'd be fine.

The compiler already catches some simple cases (not const fn) at compile time which would have a different runtime behavior:

let x:u8 = 999:
1 Like

I am surprised this doesn't trigger E0080:

This error indicates that the compiler was unable to sensibly evaluate a constant expression that had to be evaluated. Attempting to divide by 0 or causing an integer overflow are two ways to induce this error.

This fails with that error even in release mode:

pub const FOO: u8 = 200 + 200;
2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.