gccrs icon indicating copy to clipboard operation
gccrs copied to clipboard

Error when using signed integer minimum value

Open lukyxu opened this issue 3 years ago • 1 comments

I tried this code:

fn main() {
    let x: i8 = -128_i8;
}

I expected to see this happen: Code should compile as the i8 has the range [-128..127]

Instead, this happened: gcc-test.rs:2:18: error: integer overflows the respective type ‘i8’ 2 | let x: i8 = -128_i8;

Meta

  • What version of Rust GCC were you using, git sha if possible. c0f11672d760513256997f325da678016d13f677

lukyxu avatar Jun 18 '22 09:06 lukyxu

Note that this is technically an overflow in Rust, that much is right: -128_i8 is the negation of the integer literal 128_i8, and 128_i8 is out of range for type i8. -128_i8 is not a literal in its own right. However, Rust has special handling to deal with this and not regard it as an error. The way this works in Rust is that:

  • Out of range literals are at the core level valid. Literals are parsed as u128 constants and then converted as if by a numeric cast to the requested type. The cast is specified to truncate. This means that the value of 128_i8 is 128, converted to i8, which results in -128. (Ref: https://doc.rust-lang.org/stable/reference/expressions/literal-expr.html#integer-literal-expressions)
  • Negation of literals is treated as a special case. Negation is one of the operations that can result in an overflow error in debug mode, but negation of literals is a special case where overflow is always allowed. The negation of an i8 value of -128 would ordinarily overflow and result in an error in debug builds, but the negation of a literal with a value of -128 leaves the value unchanged, it produces a constant that still has the same value -128. (Ref: https://doc.rust-lang.org/stable/reference/expressions/operator-expr.html#overflow)
  • There is a lint rule overflowing_literals, optional but enabled by default, to flag out-of-range literals. However, despite the name, it does not actually check for literals. This lint rule too has a special case to deal with this: if a literal is negated, rather than checking that the literal's value is in range, it checks that the negation of the literal's value is in range (or, in other words, it checks that value <= type::MAX + 1 rather than value <= type::MAX).

You can see this if you look closely: with the rustc compiler, if you change the code to

fn main() {
    let x: i8 = -200_i8;
}

the resulting note points out that 200 is out of range, not that -200 is out of range.

hvdijk avatar Jul 20 '22 17:07 hvdijk