Add f16 and f128 support

This commit is contained in:
beetrees 2024-07-10 09:38:47 +01:00
parent da27b89ca5
commit d5db933f9d
No known key found for this signature in database
GPG key ID: 8791BD754191EBD6
28 changed files with 384 additions and 73 deletions

20
Cargo.lock generated
View file

@ -167,9 +167,9 @@ checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
[[package]]
name = "chalk-derive"
version = "0.97.0"
version = "0.98.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92a0aedc4ac2adc5c0b7dc9ec38c5c816284ad28da6d4ecd01873b9683f54972"
checksum = "9426c8fd0fe61c3da880b801d3b510524df17843a8f9ec1f5b9cec24fb7412df"
dependencies = [
"proc-macro2",
"quote",
@ -179,9 +179,9 @@ dependencies = [
[[package]]
name = "chalk-ir"
version = "0.97.0"
version = "0.98.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db18493569b190f7266a04901e520fc3a5c00564475154287906f8a27302c119"
checksum = "d5f2eb1cd6054da221bd1ac0197fb2fe5e2caf3dcb93619398fc1433f8f09093"
dependencies = [
"bitflags 2.5.0",
"chalk-derive",
@ -189,9 +189,9 @@ dependencies = [
[[package]]
name = "chalk-recursive"
version = "0.97.0"
version = "0.98.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae4ba8ce5bd2e1b59f1f79495bc8704db09a8285e51cc5ddf01d9baee1bf447d"
checksum = "129dc03458f71cfb9c3cd621c9c68166a94e87b85b16ccd29af015d7ff9a1c61"
dependencies = [
"chalk-derive",
"chalk-ir",
@ -202,9 +202,9 @@ dependencies = [
[[package]]
name = "chalk-solve"
version = "0.97.0"
version = "0.98.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ec1b3b7f7b1ec38f099ef39c2bc3ea29335be1b8316d114baff46d96d131e9"
checksum = "d7e8a8c1e928f98cdf227b868416ef21dcd8cc3c61b347576d783713444d41c8"
dependencies = [
"chalk-derive",
"chalk-ir",
@ -546,6 +546,7 @@ dependencies = [
"ra-ap-rustc_abi",
"ra-ap-rustc_parse_format",
"rustc-hash",
"rustc_apfloat",
"smallvec",
"span",
"stdx",
@ -613,6 +614,7 @@ dependencies = [
"ra-ap-rustc_index",
"ra-ap-rustc_pattern_analysis",
"rustc-hash",
"rustc_apfloat",
"scoped-tls",
"smallvec",
"span",
@ -658,6 +660,7 @@ dependencies = [
"profile",
"pulldown-cmark",
"pulldown-cmark-to-cmark",
"rustc_apfloat",
"smallvec",
"span",
"stdx",
@ -1939,6 +1942,7 @@ dependencies = [
"rayon",
"rowan",
"rustc-hash",
"rustc_apfloat",
"smol_str",
"stdx",
"test-utils",

View file

@ -104,10 +104,10 @@ arrayvec = "0.7.4"
bitflags = "2.4.1"
cargo_metadata = "0.18.1"
camino = "1.1.6"
chalk-solve = { version = "0.97.0", default-features = false }
chalk-ir = "0.97.0"
chalk-recursive = { version = "0.97.0", default-features = false }
chalk-derive = "0.97.0"
chalk-solve = { version = "0.98.0", default-features = false }
chalk-ir = "0.98.0"
chalk-recursive = { version = "0.98.0", default-features = false }
chalk-derive = "0.98.0"
crossbeam-channel = "0.5.8"
dissimilar = "1.0.7"
dot = "0.1.4"

View file

@ -28,6 +28,7 @@ tracing.workspace = true
smallvec.workspace = true
hashbrown.workspace = true
triomphe.workspace = true
rustc_apfloat = "0.2.0"
ra-ap-rustc_parse_format.workspace = true
ra-ap-rustc_abi.workspace = true

View file

@ -30,8 +30,10 @@ pub enum BuiltinUint {
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum BuiltinFloat {
F16,
F32,
F64,
F128,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@ -65,8 +67,10 @@ impl BuiltinType {
(name![u64], BuiltinType::Uint(BuiltinUint::U64)),
(name![u128], BuiltinType::Uint(BuiltinUint::U128)),
(name![f16], BuiltinType::Float(BuiltinFloat::F16)),
(name![f32], BuiltinType::Float(BuiltinFloat::F32)),
(name![f64], BuiltinType::Float(BuiltinFloat::F64)),
(name![f128], BuiltinType::Float(BuiltinFloat::F128)),
];
pub fn by_name(name: &Name) -> Option<Self> {
@ -97,8 +101,10 @@ impl AsName for BuiltinType {
BuiltinUint::U128 => name![u128],
},
BuiltinType::Float(it) => match it {
BuiltinFloat::F16 => name![f16],
BuiltinFloat::F32 => name![f32],
BuiltinFloat::F64 => name![f64],
BuiltinFloat::F128 => name![f128],
},
}
}
@ -155,8 +161,10 @@ impl BuiltinUint {
impl BuiltinFloat {
pub fn from_suffix(suffix: &str) -> Option<BuiltinFloat> {
let res = match suffix {
"f16" => BuiltinFloat::F16,
"f32" => BuiltinFloat::F32,
"f64" => BuiltinFloat::F64,
"f128" => BuiltinFloat::F128,
_ => return None,
};
Some(res)
@ -192,8 +200,10 @@ impl fmt::Display for BuiltinUint {
impl fmt::Display for BuiltinFloat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
BuiltinFloat::F16 => "f16",
BuiltinFloat::F32 => "f32",
BuiltinFloat::F64 => "f64",
BuiltinFloat::F128 => "f128",
})
}
}

View file

@ -20,6 +20,7 @@ use std::fmt;
use hir_expand::name::Name;
use intern::Interned;
use la_arena::{Idx, RawIdx};
use rustc_apfloat::ieee::{Half as f16, Quad as f128};
use smallvec::SmallVec;
use syntax::ast;
@ -62,11 +63,16 @@ pub type LabelId = Idx<Label>;
#[derive(Default, Debug, Clone, Eq, PartialEq)]
pub struct FloatTypeWrapper(Box<str>);
// FIXME(#17451): Use builtin types once stabilised.
impl FloatTypeWrapper {
pub fn new(value: String) -> Self {
Self(value.into())
}
pub fn to_f128(&self) -> f128 {
self.0.parse().unwrap_or_default()
}
pub fn to_f64(&self) -> f64 {
self.0.parse().unwrap_or_default()
}
@ -74,6 +80,10 @@ impl FloatTypeWrapper {
pub fn to_f32(&self) -> f32 {
self.0.parse().unwrap_or_default()
}
pub fn to_f16(&self) -> f16 {
self.0.parse().unwrap_or_default()
}
}
impl fmt::Display for FloatTypeWrapper {
@ -91,7 +101,7 @@ pub enum Literal {
Bool(bool),
Int(i128, Option<BuiltinInt>),
Uint(u128, Option<BuiltinUint>),
// Here we are using a wrapper around float because f32 and f64 do not implement Eq, so they
// Here we are using a wrapper around float because float primitives do not implement Eq, so they
// could not be used directly here, to understand how the wrapper works go to definition of
// FloatTypeWrapper
Float(FloatTypeWrapper, Option<BuiltinFloat>),

View file

@ -36,6 +36,7 @@ macro_rules! m {
let _ = 'c';
let _ = 1000;
let _ = 12E+99_f64;
let _ = 45E+1234_f128;
let _ = "rust1";
let _ = -92;
}
@ -50,6 +51,7 @@ macro_rules! m {
let _ = 'c';
let _ = 1000;
let _ = 12E+99_f64;
let _ = 45E+1234_f128;
let _ = "rust1";
let _ = -92;
}
@ -58,6 +60,7 @@ fn f() {
let _ = 'c';
let _ = 1000;
let _ = 12E+99_f64;
let _ = 45E+1234_f128;
let _ = "rust1";
let _ = -92;
}

View file

@ -275,8 +275,10 @@ pub mod known {
u32,
u64,
u128,
f16,
f32,
f64,
f128,
bool,
char,
str,

View file

@ -33,6 +33,7 @@ triomphe.workspace = true
nohash-hasher.workspace = true
typed-arena = "2.0.1"
indexmap.workspace = true
rustc_apfloat = "0.2.0"
ra-ap-rustc_abi.workspace = true
ra-ap-rustc_index.workspace = true

View file

@ -119,8 +119,10 @@ impl TyExt for Ty {
TyKind::Scalar(Scalar::Bool) => Some(BuiltinType::Bool),
TyKind::Scalar(Scalar::Char) => Some(BuiltinType::Char),
TyKind::Scalar(Scalar::Float(fty)) => Some(BuiltinType::Float(match fty {
FloatTy::F128 => BuiltinFloat::F128,
FloatTy::F64 => BuiltinFloat::F64,
FloatTy::F32 => BuiltinFloat::F32,
FloatTy::F16 => BuiltinFloat::F16,
})),
TyKind::Scalar(Scalar::Int(ity)) => Some(BuiltinType::Int(match ity {
IntTy::Isize => BuiltinInt::Isize,

View file

@ -1,6 +1,10 @@
use base_db::FileId;
use chalk_ir::Substitution;
use hir_def::db::DefDatabase;
use rustc_apfloat::{
ieee::{Half as f16, Quad as f128},
Float,
};
use test_fixture::WithFixture;
use test_utils::skip_slow_tests;
@ -140,6 +144,14 @@ fn bit_op() {
#[test]
fn floating_point() {
check_number(
r#"const GOAL: f128 = 2.0 + 3.0 * 5.5 - 8.;"#,
"10.5".parse::<f128>().unwrap().to_bits() as i128,
);
check_number(
r#"const GOAL: f128 = -90.0 + 36.0;"#,
"-54.0".parse::<f128>().unwrap().to_bits() as i128,
);
check_number(
r#"const GOAL: f64 = 2.0 + 3.0 * 5.5 - 8.;"#,
i128::from_le_bytes(pad16(&f64::to_le_bytes(10.5), true)),
@ -152,6 +164,20 @@ fn floating_point() {
r#"const GOAL: f32 = -90.0 + 36.0;"#,
i128::from_le_bytes(pad16(&f32::to_le_bytes(-54.0), true)),
);
check_number(
r#"const GOAL: f16 = 2.0 + 3.0 * 5.5 - 8.;"#,
i128::from_le_bytes(pad16(
&u16::try_from("10.5".parse::<f16>().unwrap().to_bits()).unwrap().to_le_bytes(),
true,
)),
);
check_number(
r#"const GOAL: f16 = -90.0 + 36.0;"#,
i128::from_le_bytes(pad16(
&u16::try_from("-54.0".parse::<f16>().unwrap().to_bits()).unwrap().to_le_bytes(),
true,
)),
);
}
#[test]

View file

@ -411,6 +411,7 @@ fn likely() {
#[test]
fn floating_point() {
// FIXME(#17451): Add `f16` and `f128` tests once intrinsics are added.
check_number(
r#"
extern "rust-intrinsic" {

View file

@ -28,6 +28,10 @@ use hir_expand::name::Name;
use intern::{Internable, Interned};
use itertools::Itertools;
use la_arena::ArenaMap;
use rustc_apfloat::{
ieee::{Half as f16, Quad as f128},
Float,
};
use smallvec::SmallVec;
use stdx::{never, IsNoneOr};
use triomphe::Arc;
@ -545,6 +549,17 @@ fn render_const_scalar(
write!(f, "{it}")
}
Scalar::Float(fl) => match fl {
chalk_ir::FloatTy::F16 => {
// FIXME(#17451): Replace with builtins once they are stabilised.
let it = f16::from_bits(u16::from_le_bytes(b.try_into().unwrap()).into());
let s = it.to_string();
if s.strip_prefix('-').unwrap_or(&s).chars().all(|c| c.is_ascii_digit()) {
// Match Rust debug formatting
write!(f, "{s}.0")
} else {
write!(f, "{s}")
}
}
chalk_ir::FloatTy::F32 => {
let it = f32::from_le_bytes(b.try_into().unwrap());
write!(f, "{it:?}")
@ -553,6 +568,17 @@ fn render_const_scalar(
let it = f64::from_le_bytes(b.try_into().unwrap());
write!(f, "{it:?}")
}
chalk_ir::FloatTy::F128 => {
// FIXME(#17451): Replace with builtins once they are stabilised.
let it = f128::from_bits(u128::from_le_bytes(b.try_into().unwrap()));
let s = it.to_string();
if s.strip_prefix('-').unwrap_or(&s).chars().all(|c| c.is_ascii_digit()) {
// Match Rust debug formatting
write!(f, "{s}.0")
} else {
write!(f, "{s}")
}
}
},
},
TyKind::Ref(_, _, t) => match t.kind(Interner) {

View file

@ -265,8 +265,10 @@ pub fn layout_of_ty_query(
chalk_ir::Scalar::Float(f) => scalar(
dl,
Primitive::Float(match f {
FloatTy::F16 => Float::F16,
FloatTy::F32 => Float::F32,
FloatTy::F64 => Float::F64,
FloatTy::F128 => Float::F128,
}),
),
},

View file

@ -426,6 +426,7 @@ fn enums() {
#[test]
fn primitives() {
// FIXME(#17451): Add `f16` and `f128` once they are stabilised.
size_and_align! {
struct Goal(i32, i128, isize, usize, f32, f64, bool, char);
}

View file

@ -127,9 +127,11 @@ pub(crate) const ALL_INT_FPS: [TyFingerprint; 12] = [
TyFingerprint::Scalar(Scalar::Uint(UintTy::Usize)),
];
pub(crate) const ALL_FLOAT_FPS: [TyFingerprint; 2] = [
pub(crate) const ALL_FLOAT_FPS: [TyFingerprint; 4] = [
TyFingerprint::Scalar(Scalar::Float(FloatTy::F16)),
TyFingerprint::Scalar(Scalar::Float(FloatTy::F32)),
TyFingerprint::Scalar(Scalar::Float(FloatTy::F64)),
TyFingerprint::Scalar(Scalar::Float(FloatTy::F128)),
];
type TraitFpMap = FxHashMap<TraitId, FxHashMap<Option<TyFingerprint>, Box<[ImplId]>>>;

View file

@ -18,6 +18,10 @@ use hir_expand::{mod_path::ModPath, HirFileIdExt, InFile};
use intern::Interned;
use la_arena::ArenaMap;
use rustc_abi::TargetDataLayout;
use rustc_apfloat::{
ieee::{Half as f16, Quad as f128},
Float,
};
use rustc_hash::{FxHashMap, FxHashSet};
use stdx::never;
use syntax::{SyntaxNodePtr, TextRange};
@ -55,6 +59,13 @@ macro_rules! from_bytes {
Err(_) => return Err(MirEvalError::InternalError(stringify!(mismatched size in constructing $ty).into())),
}))
};
($apfloat:tt, $bits:tt, $value:expr) => {
// FIXME(#17451): Switch to builtin `f16` and `f128` once they are stable.
$apfloat::from_bits($bits::from_le_bytes(match ($value).try_into() {
Ok(it) => it,
Err(_) => return Err(MirEvalError::InternalError(stringify!(mismatched size in constructing $apfloat).into())),
}).into())
};
}
macro_rules! not_supported {
@ -1110,6 +1121,10 @@ impl Evaluator<'_> {
}
if let TyKind::Scalar(chalk_ir::Scalar::Float(f)) = ty.kind(Interner) {
match f {
chalk_ir::FloatTy::F16 => {
let c = -from_bytes!(f16, u16, c);
Owned(u16::try_from(c.to_bits()).unwrap().to_le_bytes().into())
}
chalk_ir::FloatTy::F32 => {
let c = -from_bytes!(f32, c);
Owned(c.to_le_bytes().into())
@ -1118,6 +1133,10 @@ impl Evaluator<'_> {
let c = -from_bytes!(f64, c);
Owned(c.to_le_bytes().into())
}
chalk_ir::FloatTy::F128 => {
let c = -from_bytes!(f128, u128, c);
Owned(c.to_bits().to_le_bytes().into())
}
}
} else {
let mut c = c.to_vec();
@ -1169,6 +1188,39 @@ impl Evaluator<'_> {
}
if let TyKind::Scalar(chalk_ir::Scalar::Float(f)) = ty.kind(Interner) {
match f {
chalk_ir::FloatTy::F16 => {
let l = from_bytes!(f16, u16, lc);
let r = from_bytes!(f16, u16, rc);
match op {
BinOp::Ge
| BinOp::Gt
| BinOp::Le
| BinOp::Lt
| BinOp::Eq
| BinOp::Ne => {
let r = op.run_compare(l, r) as u8;
Owned(vec![r])
}
BinOp::Add | BinOp::Sub | BinOp::Mul | BinOp::Div => {
let r = match op {
BinOp::Add => l + r,
BinOp::Sub => l - r,
BinOp::Mul => l * r,
BinOp::Div => l / r,
_ => unreachable!(),
};
Owned(
u16::try_from(r.value.to_bits())
.unwrap()
.to_le_bytes()
.into(),
)
}
it => not_supported!(
"invalid binop {it:?} on floating point operators"
),
}
}
chalk_ir::FloatTy::F32 => {
let l = from_bytes!(f32, lc);
let r = from_bytes!(f32, rc);
@ -1225,6 +1277,34 @@ impl Evaluator<'_> {
),
}
}
chalk_ir::FloatTy::F128 => {
let l = from_bytes!(f128, u128, lc);
let r = from_bytes!(f128, u128, rc);
match op {
BinOp::Ge
| BinOp::Gt
| BinOp::Le
| BinOp::Lt
| BinOp::Eq
| BinOp::Ne => {
let r = op.run_compare(l, r) as u8;
Owned(vec![r])
}
BinOp::Add | BinOp::Sub | BinOp::Mul | BinOp::Div => {
let r = match op {
BinOp::Add => l + r,
BinOp::Sub => l - r,
BinOp::Mul => l * r,
BinOp::Div => l / r,
_ => unreachable!(),
};
Owned(r.value.to_bits().to_le_bytes().into())
}
it => not_supported!(
"invalid binop {it:?} on floating point operators"
),
}
}
}
} else {
let is_signed = matches!(ty.as_builtin(), Some(BuiltinType::Int(_)));

View file

@ -627,6 +627,7 @@ impl Evaluator<'_> {
if let Some(name) = name.strip_prefix("atomic_") {
return self.exec_atomic_intrinsic(name, args, generic_args, destination, locals, span);
}
// FIXME(#17451): Add `f16` and `f128` intrinsics.
if let Some(name) = name.strip_suffix("f64") {
let result = match name {
"sqrt" | "sin" | "cos" | "exp" | "exp2" | "log" | "log10" | "log2" | "fabs"

View file

@ -19,6 +19,7 @@ use hir_def::{
};
use hir_expand::name::Name;
use la_arena::ArenaMap;
use rustc_apfloat::Float;
use rustc_hash::FxHashMap;
use syntax::TextRange;
use triomphe::Arc;
@ -1432,10 +1433,14 @@ 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()? {
16 => Box::new(f.to_f128().to_bits().to_le_bytes()),
8 => Box::new(f.to_f64().to_le_bytes()),
4 => Box::new(f.to_f32().to_le_bytes()),
2 => Box::new(u16::try_from(f.to_f16().to_bits()).unwrap().to_le_bytes()),
_ => {
return Err(MirLowerError::TypeError("float with size other than 4 or 8 bytes"))
return Err(MirLowerError::TypeError(
"float with size other than 2, 4, 8 or 16 bytes",
))
}
},
};

View file

@ -27,8 +27,10 @@ pub fn uint_ty_to_string(ty: UintTy) -> &'static str {
pub fn float_ty_to_string(ty: FloatTy) -> &'static str {
match ty {
FloatTy::F16 => "f16",
FloatTy::F32 => "f32",
FloatTy::F64 => "f64",
FloatTy::F128 => "f128",
}
}
@ -56,7 +58,9 @@ pub(super) fn uint_ty_from_builtin(t: BuiltinUint) -> UintTy {
pub(super) fn float_ty_from_builtin(t: BuiltinFloat) -> FloatTy {
match t {
BuiltinFloat::F16 => FloatTy::F16,
BuiltinFloat::F32 => FloatTy::F32,
BuiltinFloat::F64 => FloatTy::F64,
BuiltinFloat::F128 => FloatTy::F128,
}
}

View file

@ -111,8 +111,10 @@ fn infer_literal_pattern() {
if let "foo" = any() {}
if let 1 = any() {}
if let 1u32 = any() {}
if let 1f16 = any() {}
if let 1f32 = any() {}
if let 1.0 = any() {}
if let 1f128 = any() {}
if let true = any() {}
}
"#,
@ -121,7 +123,7 @@ fn infer_literal_pattern() {
19..26 'loop {}': !
24..26 '{}': ()
37..38 'x': &'? i32
46..208 '{ ...) {} }': ()
46..263 '{ ...) {} }': ()
52..75 'if let...y() {}': ()
55..72 'let "f... any()': bool
59..64 '"foo"': &'static str
@ -145,25 +147,39 @@ fn infer_literal_pattern() {
124..126 '{}': ()
131..153 'if let...y() {}': ()
134..150 'let 1f... any()': bool
138..142 '1f32': f32
138..142 '1f32': f32
145..148 'any': fn any<f32>() -> f32
145..150 'any()': f32
138..142 '1f16': f16
138..142 '1f16': f16
145..148 'any': fn any<f16>() -> f16
145..150 'any()': f16
151..153 '{}': ()
158..179 'if let...y() {}': ()
161..176 'let 1.0 = any()': bool
165..168 '1.0': f64
165..168 '1.0': f64
171..174 'any': fn any<f64>() -> f64
171..176 'any()': f64
177..179 '{}': ()
184..206 'if let...y() {}': ()
187..203 'let tr... any()': bool
191..195 'true': bool
191..195 'true': bool
198..201 'any': fn any<bool>() -> bool
198..203 'any()': bool
158..180 'if let...y() {}': ()
161..177 'let 1f... any()': bool
165..169 '1f32': f32
165..169 '1f32': f32
172..175 'any': fn any<f32>() -> f32
172..177 'any()': f32
178..180 '{}': ()
185..206 'if let...y() {}': ()
188..203 'let 1.0 = any()': bool
192..195 '1.0': f64
192..195 '1.0': f64
198..201 'any': fn any<f64>() -> f64
198..203 'any()': f64
204..206 '{}': ()
211..234 'if let...y() {}': ()
214..231 'let 1f... any()': bool
218..223 '1f128': f128
218..223 '1f128': f128
226..229 'any': fn any<f128>() -> f128
226..231 'any()': f128
232..234 '{}': ()
239..261 'if let...y() {}': ()
242..258 'let tr... any()': bool
246..250 'true': bool
246..250 'true': bool
253..256 'any': fn any<bool>() -> bool
253..258 'any()': bool
259..261 '{}': ()
"#]],
);
}

View file

@ -397,8 +397,10 @@ fn infer_literals() {
r##"
fn test() {
5i32;
5f16;
5f32;
5f64;
5f128;
"hello";
b"bytes";
'c';
@ -421,26 +423,28 @@ h";
}
"##,
expect![[r##"
18..478 '{ ... }': ()
18..515 '{ ... }': ()
32..36 '5i32': i32
50..54 '5f32': f32
68..72 '5f64': f64
86..93 '"hello"': &'static str
107..115 'b"bytes"': &'static [u8; 5]
129..132 ''c'': char
146..150 'b'b'': u8
164..168 '3.14': f64
182..186 '5000': i32
200..205 'false': bool
219..223 'true': bool
237..333 'r#" ... "#': &'static str
347..357 'br#"yolo"#': &'static [u8; 4]
375..376 'a': &'static [u8; 4]
379..403 'b"a\x2... c"': &'static [u8; 4]
421..422 'b': &'static [u8; 4]
425..433 'br"g\ h"': &'static [u8; 4]
451..452 'c': &'static [u8; 6]
455..467 'br#"x"\"yb"#': &'static [u8; 6]
50..54 '5f16': f16
68..72 '5f32': f32
86..90 '5f64': f64
104..109 '5f128': f128
123..130 '"hello"': &'static str
144..152 'b"bytes"': &'static [u8; 5]
166..169 ''c'': char
183..187 'b'b'': u8
201..205 '3.14': f64
219..223 '5000': i32
237..242 'false': bool
256..260 'true': bool
274..370 'r#" ... "#': &'static str
384..394 'br#"yolo"#': &'static [u8; 4]
412..413 'a': &'static [u8; 4]
416..440 'b"a\x2... c"': &'static [u8; 4]
458..459 'b': &'static [u8; 4]
462..470 'br"g\ h"': &'static [u8; 4]
488..489 'c': &'static [u8; 6]
492..504 'br#"x"\"yb"#': &'static [u8; 6]
"##]],
);
}

View file

@ -2622,6 +2622,13 @@ impl BuiltinType {
matches!(self.inner, hir_def::builtin_type::BuiltinType::Float(_))
}
pub fn is_f16(&self) -> bool {
matches!(
self.inner,
hir_def::builtin_type::BuiltinType::Float(hir_def::builtin_type::BuiltinFloat::F16)
)
}
pub fn is_f32(&self) -> bool {
matches!(
self.inner,
@ -2636,6 +2643,13 @@ impl BuiltinType {
)
}
pub fn is_f128(&self) -> bool {
matches!(
self.inner,
hir_def::builtin_type::BuiltinType::Float(hir_def::builtin_type::BuiltinFloat::F128)
)
}
pub fn is_char(&self) -> bool {
matches!(self.inner, hir_def::builtin_type::BuiltinType::Char)
}

View file

@ -25,6 +25,7 @@ dot.workspace = true
smallvec.workspace = true
triomphe.workspace = true
nohash-hasher.workspace = true
rustc_apfloat = "0.2.0"
# local deps
cfg.workspace = true

View file

@ -16,6 +16,10 @@ use ide_db::{
RootDatabase,
};
use itertools::Itertools;
use rustc_apfloat::{
ieee::{Half as f16, Quad as f128},
Float,
};
use stdx::format_to;
use syntax::{algo, ast, match_ast, AstNode, AstToken, Direction, SyntaxToken, T};
@ -540,13 +544,22 @@ pub(super) fn literal(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) ->
ast::Char(char) => char .value().as_ref().map_err(|e| format!("{e:?}")).map(ToString::to_string),
ast::Byte(byte) => byte .value().as_ref().map_err(|e| format!("{e:?}")).map(|it| format!("0x{it:X}")),
ast::FloatNumber(num) => {
let (text, _) = num.split_into_parts();
let text = text.replace('_', "");
if ty.as_builtin().map(|it| it.is_f32()).unwrap_or(false) {
let text = num.value_string();
if ty.as_builtin().map(|it| it.is_f16()).unwrap_or(false) {
match text.parse::<f16>() {
Ok(num) => Ok(format!("{num} (bits: 0x{:X})", num.to_bits())),
Err(e) => Err(e.0.to_owned()),
}
} else if ty.as_builtin().map(|it| it.is_f32()).unwrap_or(false) {
match text.parse::<f32>() {
Ok(num) => Ok(format!("{num} (bits: 0x{:X})", num.to_bits())),
Err(e) => Err(e.to_string()),
}
} else if ty.as_builtin().map(|it| it.is_f128()).unwrap_or(false) {
match text.parse::<f128>() {
Ok(num) => Ok(format!("{num} (bits: 0x{:X})", num.to_bits())),
Err(e) => Err(e.0.to_owned()),
}
} else {
match text.parse::<f64>() {
Ok(num) => Ok(format!("{num} (bits: 0x{:X})", num.to_bits())),

View file

@ -5329,6 +5329,49 @@ const FOO$0: f32 = 1.9999999403953552_f32;
---
This is a doc
"#]],
);
// Check `f16` and `f128`
check(
r#"
/// This is a doc
const FOO$0: f16 = -1.0f16;
"#,
expect![[r#"
*FOO*
```rust
test
```
```rust
const FOO: f16 = -1.0
```
---
This is a doc
"#]],
);
check(
r#"
/// This is a doc
const FOO$0: f128 = -1.0f128;
"#,
expect![[r#"
*FOO*
```rust
test
```
```rust
const FOO: f128 = -1.0
```
---
This is a doc
"#]],
);
@ -8045,6 +8088,22 @@ fn main() {
);
check(
r#"
fn main() {
$01.0f16;
}
"#,
expect![[r#"
*1.0f16*
```rust
f16
```
___
value of literal: 1 (bits: 0x3C00)
"#]],
);
check(
r#"
fn main() {
$01.0f32;
}
@ -8061,6 +8120,22 @@ fn main() {
);
check(
r#"
fn main() {
$01.0f128;
}
"#,
expect![[r#"
*1.0f128*
```rust
f128
```
___
value of literal: 1 (bits: 0x3FFF0000000000000000000000000000)
"#]],
);
check(
r#"
fn main() {
$0134e12;
}
@ -8390,8 +8465,8 @@ impl Iterator for S {
file_id: FileId(
1,
),
full_range: 7791..7999,
focus_range: 7856..7862,
full_range: 7800..8008,
focus_range: 7865..7871,
name: "Future",
kind: Trait,
container_name: "future",
@ -8404,8 +8479,8 @@ impl Iterator for S {
file_id: FileId(
1,
),
full_range: 8629..9095,
focus_range: 8673..8681,
full_range: 8638..9104,
focus_range: 8682..8690,
name: "Iterator",
kind: Trait,
container_name: "iterator",

View file

@ -33,6 +33,7 @@ text-edit.workspace = true
[dev-dependencies]
rayon.workspace = true
expect-test = "1.4.0"
rustc_apfloat = "0.2.0"
test-utils.workspace = true

View file

@ -489,6 +489,8 @@ impl ast::Byte {
#[cfg(test)]
mod tests {
use rustc_apfloat::ieee::Quad as f128;
use crate::ast::{self, make, FloatNumber, IntNumber};
fn check_float_suffix<'a>(lit: &str, expected: impl Into<Option<&'a str>>) {
@ -499,14 +501,16 @@ mod tests {
assert_eq!(IntNumber { syntax: make::tokens::literal(lit) }.suffix(), expected.into());
}
fn check_float_value(lit: &str, expected: impl Into<Option<f64>> + Copy) {
// FIXME(#17451) Use `expected: f128` once `f128` is stabilised.
fn check_float_value(lit: &str, expected: &str) {
let expected = Some(expected.parse::<f128>().unwrap());
assert_eq!(
FloatNumber { syntax: make::tokens::literal(lit) }.value_string().parse::<f64>().ok(),
expected.into()
FloatNumber { syntax: make::tokens::literal(lit) }.value_string().parse::<f128>().ok(),
expected
);
assert_eq!(
IntNumber { syntax: make::tokens::literal(lit) }.value_string().parse::<f64>().ok(),
expected.into()
IntNumber { syntax: make::tokens::literal(lit) }.value_string().parse::<f128>().ok(),
expected
);
}
@ -520,9 +524,9 @@ mod tests {
check_float_suffix("123f32", "f32");
check_float_suffix("123.0e", None);
check_float_suffix("123.0e4", None);
check_float_suffix("123.0ef32", "f32");
check_float_suffix("123.0ef16", "f16");
check_float_suffix("123.0E4f32", "f32");
check_float_suffix("1_2_3.0_f32", "f32");
check_float_suffix("1_2_3.0_f128", "f128");
}
#[test]
@ -589,8 +593,10 @@ bcde", b"abcde",
#[test]
fn test_value_underscores() {
check_float_value("1.234567891011121_f64", 1.234567891011121_f64);
check_float_value("1__0.__0__f32", 10.0);
check_float_value("1.3_4665449586950493453___6_f128", "1.346654495869504934536");
check_float_value("1.234567891011121_f64", "1.234567891011121");
check_float_value("1__0.__0__f32", "10.0");
check_float_value("3._0_f16", "3.0");
check_int_value("0b__1_0_", 2);
check_int_value("1_1_1_1_1_1", 111111);
}

View file

@ -123,7 +123,7 @@ pub mod marker {
impl_copy! {
usize u8 u16 u32 u64 u128
isize i8 i16 i32 i64 i128
f32 f64
f16 f32 f64 f128
bool char
}
@ -180,7 +180,7 @@ pub mod default {
0; usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128
}
impl_default! {
0.0; f32 f64
0.0; f16 f32 f64 f128
}
// endregion:builtin_impls
}
@ -276,7 +276,7 @@ pub mod clone {
impl_clone! {
usize u8 u16 u32 u64 u128
isize i8 i16 i32 i64 i128
f32 f64
f16 f32 f64 f128
bool char
}
@ -796,7 +796,7 @@ pub mod ops {
)*)
}
add_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 }
add_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f64 f128 }
// endregion:builtin_impls
// endregion:add
@ -1043,7 +1043,7 @@ pub mod fmt {
impl_debug! {
usize u8 u16 u32 u64 u128
isize i8 i16 i32 i64 i128
f32 f64
f16 f32 f64 f128
bool char
}