diff options
| author | bors <bors@rust-lang.org> | 2024-07-08 16:10:58 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2024-07-08 16:10:58 +0000 |
| commit | 5445aef843f1a921ba146ace01a2cbbfc33ee38f (patch) | |
| tree | d91ec197c2db970a26831c865f100461902740fd | |
| parent | fa29aa3a4f17def436da5c980550c55bce8eec49 (diff) | |
| parent | da41ae7b3acd899654a532fd56f17d79fbfec321 (diff) | |
| download | rust-5445aef843f1a921ba146ace01a2cbbfc33ee38f.tar.gz rust-5445aef843f1a921ba146ace01a2cbbfc33ee38f.zip | |
Auto merge of #17558 - beetrees:fix-double-rounding, r=Veykril
fix: Fix double rounding of `f32` literals Fixes #17556 by delaying parsing until the type is known. Also adds a test to check the issue is fixed.
4 files changed, 47 insertions, 33 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index fd6f4a3d089..378ce1df672 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -56,29 +56,29 @@ pub struct Label { } pub type LabelId = Idx<Label>; -// We convert float values into bits and that's how we don't need to deal with f32 and f64. -// For PartialEq, bits comparison should work, as ordering is not important +// We leave float values as a string to avoid double rounding. +// For PartialEq, string comparison should work, as ordering is not important // https://github.com/rust-lang/rust-analyzer/issues/12380#issuecomment-1137284360 -#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)] -pub struct FloatTypeWrapper(u64); +#[derive(Default, Debug, Clone, Eq, PartialEq)] +pub struct FloatTypeWrapper(Box<str>); impl FloatTypeWrapper { - pub fn new(value: f64) -> Self { - Self(value.to_bits()) + pub fn new(value: String) -> Self { + Self(value.into()) } - pub fn into_f64(self) -> f64 { - f64::from_bits(self.0) + pub fn to_f64(&self) -> f64 { + self.0.parse().unwrap_or_default() } - pub fn into_f32(self) -> f32 { - f64::from_bits(self.0) as f32 + pub fn to_f32(&self) -> f32 { + self.0.parse().unwrap_or_default() } } impl fmt::Display for FloatTypeWrapper { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", f64::from_bits(self.0)) + f.write_str(&self.0) } } @@ -120,10 +120,7 @@ impl From<ast::LiteralKind> for Literal { match ast_lit_kind { LiteralKind::IntNumber(lit) => { if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) { - Literal::Float( - FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())), - builtin, - ) + Literal::Float(FloatTypeWrapper::new(lit.value_string()), builtin) } else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinUint::from_suffix) { Literal::Uint(lit.value().unwrap_or(0), builtin) } else { @@ -133,7 +130,7 @@ impl From<ast::LiteralKind> for Literal { } LiteralKind::FloatNumber(lit) => { let ty = lit.suffix().and_then(BuiltinFloat::from_suffix); - Literal::Float(FloatTypeWrapper::new(lit.value().unwrap_or(Default::default())), ty) + Literal::Float(FloatTypeWrapper::new(lit.value_string()), ty) } LiteralKind::ByteString(bs) => { let text = bs.value().map_or_else(|_| Default::default(), Box::from); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 3402b761d32..3ceb94ad7af 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -1432,8 +1432,8 @@ impl<'ctx> MirLowerCtx<'ctx> { hir_def::hir::Literal::Int(it, _) => Box::from(&it.to_le_bytes()[0..size()?]), hir_def::hir::Literal::Uint(it, _) => Box::from(&it.to_le_bytes()[0..size()?]), hir_def::hir::Literal::Float(f, _) => match size()? { - 8 => Box::new(f.into_f64().to_le_bytes()), - 4 => Box::new(f.into_f32().to_le_bytes()), + 8 => Box::new(f.to_f64().to_le_bytes()), + 4 => Box::new(f.to_f32().to_le_bytes()), _ => { return Err(MirLowerError::TypeError("float with size other than 4 or 8 bytes")) } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 84793746475..51b60df7cc0 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -5310,6 +5310,28 @@ const FOO$0: f64 = expf64(1.2); ``` "#]], ); + // check `f32` isn't double rounded via `f64` + check( + r#" +/// This is a doc +const FOO$0: f32 = 1.9999999403953552_f32; +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: f32 = 1.9999999 + ``` + + --- + + This is a doc + "#]], + ); } #[test] diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs index 1ce548f8fc7..f00e5c6d0e1 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs @@ -1,9 +1,6 @@ //! There are many AstNodes, but only a few tokens, so we hand-write them here. -use std::{ - borrow::Cow, - num::{ParseFloatError, ParseIntError}, -}; +use std::{borrow::Cow, num::ParseIntError}; use rustc_lexer::unescape::{ unescape_byte, unescape_char, unescape_mixed, unescape_unicode, EscapeError, MixedUnit, Mode, @@ -393,9 +390,9 @@ impl ast::IntNumber { } } - pub fn float_value(&self) -> Option<f64> { + pub fn value_string(&self) -> String { let (_, text, _) = self.split_into_parts(); - text.replace('_', "").parse::<f64>().ok() + text.replace('_', "") } } @@ -432,14 +429,9 @@ impl ast::FloatNumber { } } - pub fn value(&self) -> Result<f64, ParseFloatError> { - let (text, _) = self.split_into_parts(); - text.replace('_', "").parse::<f64>() - } - - pub fn value_f32(&self) -> Result<f32, ParseFloatError> { + pub fn value_string(&self) -> String { let (text, _) = self.split_into_parts(); - text.replace('_', "").parse::<f32>() + text.replace('_', "") } } @@ -509,10 +501,13 @@ mod tests { fn check_float_value(lit: &str, expected: impl Into<Option<f64>> + Copy) { assert_eq!( - FloatNumber { syntax: make::tokens::literal(lit) }.value().ok(), + FloatNumber { syntax: make::tokens::literal(lit) }.value_string().parse::<f64>().ok(), + expected.into() + ); + assert_eq!( + IntNumber { syntax: make::tokens::literal(lit) }.value_string().parse::<f64>().ok(), expected.into() ); - assert_eq!(IntNumber { syntax: make::tokens::literal(lit) }.float_value(), expected.into()); } fn check_int_value(lit: &str, expected: impl Into<Option<u128>>) { |
