Const assertion fails to infer correct type

Just popping in to share a somewhat surprising behavior I encountered.

The anonymous const assertion in the ranged macro, produces a compile error by wrongly interpreting $min and $max literals as i32s at build time.

I find it surprising that the compiler can't infer the correct type in this very unambiguous context (value, min and max fields of the Error type are i64s, value's type is explicitly annotated etc.).

And it does the same if the whole macro is invoked in a const context.

Also, the error message is confusing: assertion failed: 0 <= 2_147_483_648 (which is obviously wrong). A more insightful message would be to something like literal '2_147_483_648' does not fit into the type 'i32'.

Do you think this is worth reporting? In which case where and how should I report it?

#[derive(Debug)]
pub struct Error {
    value: i64,
    min: i64,
    max: i64
}

macro_rules! ranged {
    ($value:ident in $min:literal..=$max:literal) => {{
    
        // wrong interpretation of $min and $max as i32 at build time
        const _: () = assert!($min <= $max);
        
        // workaround
        const _: () = assert!($min as i64 <= $max as i64); 
        
        // correct interpretation of $min and $max as i64 at run time
        if $value < $min || $max < $value {
            return Err(Error {
                value: $value,
                min: $min,
                max: $max,
            });
        }
    }}
}

fn main() -> Result<(), Error> {

    let value: i64 = 42;
    
    // works
    ranged!(value in 0..=2_147_483_647); // i32::MAX
    ranged!(value in 0i64..=2_147_483_648); // i32::MAX + 1
    ranged!(value in 0..=2_147_483_648i64);

    // compile error
    ranged!(value in 0..=2_147_483_648);

    Ok(())
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0080]: evaluation of constant value failed
  --> src/main.rs:13:23
   |
13 |         const _: () = assert!($min <= $max);
   |                       ^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: 0 <= 2_147_483_648', src/main.rs:39:5
...
39 |     ranged!(value in 0..=2_147_483_648);
   |     ----------------------------------- in this macro invocation
   |
   = note: this error originates in the macro `assert` which comes from the expansion of the macro `ranged` (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 1 previous error

1 Like

Well, in the resulting code they are literals, so it's not really as unambiguous as it looks in the macro declaration. $min and $max aren't variables with a single type, so it doesn't infer their types from the Error { .. } construction below. This fails too:

fn example() -> Result<(), Error> {
    assert!(0 <= 2_147_483_648);
    if true {
        return Err(Error {
            value: 0,
            min: 0,
            max: 2_147_483_648,
        })
    }
    Ok(())
}

Whereas this compiles:

macro_rules! use_literal {
    ($max:literal) => {{
        let _i: i64 = $max;
        let _j: u64 = $max;
    }}
}

// ...
    use_literal!(2_147_483_648);

Yep thanks, but it does error with a sensible message:

fn main() {
    assert!(0 <= 2_147_483_648);
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error: literal out of range for `i32`
 --> src/main.rs:4:18
  |
4 |     assert!(0 <= 2_147_483_648);
  |                  ^^^^^^^^^^^^^
  |
  = note: the literal `2_147_483_648` does not fit into the type `i32` whose range is `-2147483648..=2147483647`
  = help: consider using the type `u32` instead
  = note: `#[deny(overflowing_literals)]` on by default

error: could not compile `playground` (bin "playground") due to 1 previous error

Anyways, I thought it would be reasonable to expect type inference to work a usual..

pub struct InferType {
    value: i64,
}


fn main() {
    let value = 2_147_483_648;
    
    assert!(0 <= value);
    
    let _ = InferType { value };
}

(Playground)

That's true, the error message is much better. You could file a diagnostic issue about the OP.

2 Likes

Thanks! Will do :slight_smile:

1 Like