Repository: rust-lang/rust-analyzer (rust)

Issue:
Int-to-Float casts break const evaluation
<!--
Troubleshooting guide: https://rust-analyzer.github.io/manual.html#troubleshooting
Forum for questions: https://users.rust-lang.org/c/ide/14

Before submitting, please make sure that you're not running into one of these known issues:

 1. on-the-fly diagnostics are mostly unimplemented (`cargo check` diagnostics will be shown when saving a file): #3107

Otherwise please try to provide information which will help us to fix the issue faster. Minimal reproducible examples with few dependencies are especially lovely <3.
-->

Int to float casts do not evaluate in the hover window thing, including casts inside a const fn. All other floating point operations work.

**rust-analyzer version**: `rust-analyzer version: 0.4.2071-standalone (32a86cb1d 2024-08-12) [c:\Users\ds841\.vscode\extensions\rust-lang.rust-analyzer-0.4.2071-win32-x64\server\rust-analyzer.exe]`

**rustc version**: `rustc 1.81.0-nightly (d9284afea 2024-07-14)`

**editor or extension**: VSCode

**relevant settings**: N/A

**code snippet to reproduce**: 
```rust
// Hovering over the identifier yields the constant definition, verbatim.
const BROKEN: f64 = 1usize as f64;
```


Pull request:
Implement floating point casts in const eval
fix #17926

Patch:
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index 590cb3952e..1bb0c18868 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -1518,9 +1518,97 @@ impl Evaluator<'_> {
                         self.size_of_sized(target_ty, locals, "destination of int to int cast")?;
                     Owned(current[0..dest_size].to_vec())
                 }
-                CastKind::FloatToInt => not_supported!("float to int cast"),
-                CastKind::FloatToFloat => not_supported!("float to float cast"),
-                CastKind::IntToFloat => not_supported!("float to int cast"),
+                CastKind::FloatToInt => {
+                    let ty = self.operand_ty(operand, locals)?;
+                    let TyKind::Scalar(chalk_ir::Scalar::Float(ty)) = ty.kind(Interner) else {
+                        not_supported!("invalid float to int cast");
+                    };
+                    let value = self.eval_operand(operand, locals)?.get(self)?;
+                    let value = match ty {
+                        chalk_ir::FloatTy::F32 => {
+                            let value = value.try_into().unwrap();
+                            f32::from_le_bytes(value) as f64
+                        }
+                        chalk_ir::FloatTy::F64 => {
+                            let value = value.try_into().unwrap();
+                            f64::from_le_bytes(value)
+                        }
+                        chalk_ir::FloatTy::F16 | chalk_ir::FloatTy::F128 => {
+                            not_supported!("unstable floating point type f16 and f128");
+                        }
+                    };
+                    let is_signed = matches!(
+                        target_ty.kind(Interner),
+                        TyKind::Scalar(chalk_ir::Scalar::Int(_))
+                    );
+                    let dest_size =
+                        self.size_of_sized(target_ty, locals, "destination of float to int cast")?;
+                    let dest_bits = dest_size * 8;
+                    let (max, min) = if dest_bits == 128 {
+                        (i128::MAX, i128::MIN)
+                    } else if is_signed {
+                        let max = 1i128 << (dest_bits - 1);
+                        (max - 1, -max)
+                    } else {
+                        (1i128 << dest_bits, 0)
+                    };
+                    let value = (value as i128).min(max).max(min);
+                    let result = value.to_le_bytes();
+                    Owned(result[0..dest_size].to_vec())
+                }
+                CastKind::FloatToFloat => {
+                    let ty = self.operand_ty(operand, locals)?;
+                    let TyKind::Scalar(chalk_ir::Scalar::Float(ty)) = ty.kind(Interner) else {
+                        not_supported!("invalid float to int cast");
+                    };
+                    let value = self.eval_operand(operand, locals)?.get(self)?;
+                    let value = match ty {
+                        chalk_ir::FloatTy::F32 => {
+                            let value = value.try_into().unwrap();
+                            f32::from_le_bytes(value) as f64
+                        }
+                        chalk_ir::FloatTy::F64 => {
+                            let value = value.try_into().unwrap();
+                            f64::from_le_bytes(value)
+                        }
+                        chalk_ir::FloatTy::F16 | chalk_ir::FloatTy::F128 => {
+                            not_supported!("unstable floating point type f16 and f128");
+                        }
+                    };
+                    let TyKind::Scalar(chalk_ir::Scalar::Float(target_ty)) =
+                        target_ty.kind(Interner)
+                    else {
+                        not_supported!("invalid float to float cast");
+                    };
+                    match target_ty {
+                        chalk_ir::FloatTy::F32 => Owned((value as f32).to_le_bytes().to_vec()),
+                        chalk_ir::FloatTy::F64 => Owned((value as f64).to_le_bytes().to_vec()),
+                        chalk_ir::FloatTy::F16 | chalk_ir::FloatTy::F128 => {
+                            not_supported!("unstable floating point type f16 and f128");
+                        }
+                    }
+                }
+                CastKind::IntToFloat => {
+                    let current_ty = self.operand_ty(operand, locals)?;
+                    let is_signed = matches!(
+                        current_ty.kind(Interner),
+                        TyKind::Scalar(chalk_ir::Scalar::Int(_))
+                    );
+                    let value = pad16(self.eval_operand(operand, locals)?.get(self)?, is_signed);
+                    let value = i128::from_le_bytes(value);
+                    let TyKind::Scalar(chalk_ir::Scalar::Float(target_ty)) =
+                        target_ty.kind(Interner)
+                    else {
+                        not_supported!("invalid int to float cast");
+                    };
+                    match target_ty {
+                        chalk_ir::FloatTy::F32 => Owned((value as f32).to_le_bytes().to_vec()),
+                        chalk_ir::FloatTy::F64 => Owned((value as f64).to_le_bytes().to_vec()),
+                        chalk_ir::FloatTy::F16 | chalk_ir::FloatTy::F128 => {
+                            not_supported!("unstable floating point type f16 and f128");
+                        }
+                    }
+                }
                 CastKind::FnPtrToPtr => not_supported!("fn ptr to ptr cast"),
             },
         })


Test patch:
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs
index a0399a06f2..86228250c2 100644
--- a/crates/hir-ty/src/consteval/tests.rs
+++ b/crates/hir-ty/src/consteval/tests.rs
@@ -247,6 +247,17 @@ fn casts() {
     check_number(r#"const GOAL: i32 = -12i8 as i32"#, -12);
 }
 
+#[test]
+fn floating_point_casts() {
+    check_number(r#"const GOAL: usize = 12i32 as f32 as usize"#, 12);
+    check_number(r#"const GOAL: i8 = -12i32 as f64 as i8"#, -12);
+    check_number(r#"const GOAL: i32 = (-1ui8 as f32 + 2u64 as f32) as i32"#, 1);
+    check_number(r#"const GOAL: i8 = (0./0.) as i8"#, 0);
+    check_number(r#"const GOAL: i8 = (1./0.) as i8"#, 127);
+    check_number(r#"const GOAL: i8 = (-1./0.) as i8"#, -128);
+    check_number(r#"const GOAL: i64 = 1e18f64 as f32 as i64"#, 999999984306749440);
+}
+
 #[test]
 fn raw_pointer_equality() {
     check_number(