Skip to content

fix(hir-ty): saturate float-to-uint cast in const eval#22430

Merged
lnicola merged 1 commit into
rust-lang:masterfrom
shulaoda:05-23-fix_hir-ty_saturate_float-to-uint_cast_in_const_eval
May 23, 2026
Merged

fix(hir-ty): saturate float-to-uint cast in const eval#22430
lnicola merged 1 commit into
rust-lang:masterfrom
shulaoda:05-23-fix_hir-ty_saturate_float-to-uint_cast_in_const_eval

Conversation

@shulaoda
Copy link
Copy Markdown
Contributor

@shulaoda shulaoda commented May 23, 2026

Closes #22429.

Summary

  • Fix an off-by-one bug in crates/hir-ty/src/mir/eval.rs FloatToInt cast path: the unsigned upper bound was 1i128 << dest_bits instead of (1i128 << dest_bits) - 1, breaking symmetry with the signed branch directly above it.
  • As a result, any float-to-unsigned-int cast whose float value is ≥ 2^N (e.g. 256.0f32 as u8, (1./0.) as u8, 1e10f32 as u16) was evaluated as 0 instead of saturating to u_N::MAX.
  • This affects hover, inlay hints, and any feature surfacing const-eval results in rust-analyzer. Real rustc is unaffected (saturating float casts are stable since Rust 1.45).

How to reproduce

const X: u8 = 256.0_f32 as u8;
Before this PR After this PR cargo run (real rustc)
Hover on X const X: u8 = 0 const X: u8 = 255
Inlay hint 0 255
Compiled binary output 255

Signed casts (e.g. 256.0f32 as i8) are unaffected — the signed branch was already correct.

Root cause

let (max, min) = if dest_bits == 128 {
    (i128::MAX, i128::MIN)
} else if is_signed {
    let max = 1i128 << (dest_bits - 1);
    (max - 1, -max)                    // signed: correctly subtracts 1
} else {
    (1i128 << dest_bits, 0)            // unsigned: missing the -1!
};
let value = (value as i128).min(max).max(min);
let result = value.to_le_bytes();
Owned(result[0..dest_size].to_vec())

For u8 (dest_bits = 8), the buggy upper bound is 256. The clamp .min(256) keeps the value as 256 (or anything ≥ 256). Then 256.to_le_bytes()[0..1] = [0] because 256 = 0x100 and the low byte is 0. The function therefore returns 0 instead of 255.


🤖 Spotted via AI-assisted code review.

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label May 23, 2026
@lnicola lnicola added this pull request to the merge queue May 23, 2026
Merged via the queue into rust-lang:master with commit 2cf14f4 May 23, 2026
32 of 34 checks passed
@rustbot rustbot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label May 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

const eval: float-to-uint saturating cast incorrectly returns 0 for out-of-range values

3 participants