Unnamed repository; edit this file 'description' to name the repository.
Auto merge of #17942 - HKalbasi:fp-const-eval, r=HKalbasi
Implement floating point casts in const eval fix #17926
bors 2024-08-22
parent 011e3bb · parent 850a83c · commit 0fe4653
-rw-r--r--crates/hir-ty/src/consteval/tests.rs11
-rw-r--r--crates/hir-ty/src/mir/eval.rs94
2 files changed, 102 insertions, 3 deletions
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
@@ -248,6 +248,17 @@ fn casts() {
}
#[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(
r#"
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"),
},
})