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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -33,6 +33,7 @@ triomphe.workspace = true
nohash-hasher.workspace = true nohash-hasher.workspace = true
typed-arena = "2.0.1" typed-arena = "2.0.1"
indexmap.workspace = true indexmap.workspace = true
rustc_apfloat = "0.2.0"
ra-ap-rustc_abi.workspace = true ra-ap-rustc_abi.workspace = true
ra-ap-rustc_index.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::Bool) => Some(BuiltinType::Bool),
TyKind::Scalar(Scalar::Char) => Some(BuiltinType::Char), TyKind::Scalar(Scalar::Char) => Some(BuiltinType::Char),
TyKind::Scalar(Scalar::Float(fty)) => Some(BuiltinType::Float(match fty { TyKind::Scalar(Scalar::Float(fty)) => Some(BuiltinType::Float(match fty {
FloatTy::F128 => BuiltinFloat::F128,
FloatTy::F64 => BuiltinFloat::F64, FloatTy::F64 => BuiltinFloat::F64,
FloatTy::F32 => BuiltinFloat::F32, FloatTy::F32 => BuiltinFloat::F32,
FloatTy::F16 => BuiltinFloat::F16,
})), })),
TyKind::Scalar(Scalar::Int(ity)) => Some(BuiltinType::Int(match ity { TyKind::Scalar(Scalar::Int(ity)) => Some(BuiltinType::Int(match ity {
IntTy::Isize => BuiltinInt::Isize, IntTy::Isize => BuiltinInt::Isize,

View file

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

View file

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

View file

@ -28,6 +28,10 @@ use hir_expand::name::Name;
use intern::{Internable, Interned}; use intern::{Internable, Interned};
use itertools::Itertools; use itertools::Itertools;
use la_arena::ArenaMap; use la_arena::ArenaMap;
use rustc_apfloat::{
ieee::{Half as f16, Quad as f128},
Float,
};
use smallvec::SmallVec; use smallvec::SmallVec;
use stdx::{never, IsNoneOr}; use stdx::{never, IsNoneOr};
use triomphe::Arc; use triomphe::Arc;
@ -545,6 +549,17 @@ fn render_const_scalar(
write!(f, "{it}") write!(f, "{it}")
} }
Scalar::Float(fl) => match fl { 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 => { chalk_ir::FloatTy::F32 => {
let it = f32::from_le_bytes(b.try_into().unwrap()); let it = f32::from_le_bytes(b.try_into().unwrap());
write!(f, "{it:?}") write!(f, "{it:?}")
@ -553,6 +568,17 @@ fn render_const_scalar(
let it = f64::from_le_bytes(b.try_into().unwrap()); let it = f64::from_le_bytes(b.try_into().unwrap());
write!(f, "{it:?}") 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) { 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( chalk_ir::Scalar::Float(f) => scalar(
dl, dl,
Primitive::Float(match f { Primitive::Float(match f {
FloatTy::F16 => Float::F16,
FloatTy::F32 => Float::F32, FloatTy::F32 => Float::F32,
FloatTy::F64 => Float::F64, FloatTy::F64 => Float::F64,
FloatTy::F128 => Float::F128,
}), }),
), ),
}, },

View file

@ -426,6 +426,7 @@ fn enums() {
#[test] #[test]
fn primitives() { fn primitives() {
// FIXME(#17451): Add `f16` and `f128` once they are stabilised.
size_and_align! { size_and_align! {
struct Goal(i32, i128, isize, usize, f32, f64, bool, char); 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)), 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::F32)),
TyFingerprint::Scalar(Scalar::Float(FloatTy::F64)), TyFingerprint::Scalar(Scalar::Float(FloatTy::F64)),
TyFingerprint::Scalar(Scalar::Float(FloatTy::F128)),
]; ];
type TraitFpMap = FxHashMap<TraitId, FxHashMap<Option<TyFingerprint>, Box<[ImplId]>>>; 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 intern::Interned;
use la_arena::ArenaMap; use la_arena::ArenaMap;
use rustc_abi::TargetDataLayout; use rustc_abi::TargetDataLayout;
use rustc_apfloat::{
ieee::{Half as f16, Quad as f128},
Float,
};
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use stdx::never; use stdx::never;
use syntax::{SyntaxNodePtr, TextRange}; use syntax::{SyntaxNodePtr, TextRange};
@ -55,6 +59,13 @@ macro_rules! from_bytes {
Err(_) => return Err(MirEvalError::InternalError(stringify!(mismatched size in constructing $ty).into())), 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 { macro_rules! not_supported {
@ -1110,6 +1121,10 @@ impl Evaluator<'_> {
} }
if let TyKind::Scalar(chalk_ir::Scalar::Float(f)) = ty.kind(Interner) { if let TyKind::Scalar(chalk_ir::Scalar::Float(f)) = ty.kind(Interner) {
match f { 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 => { chalk_ir::FloatTy::F32 => {
let c = -from_bytes!(f32, c); let c = -from_bytes!(f32, c);
Owned(c.to_le_bytes().into()) Owned(c.to_le_bytes().into())
@ -1118,6 +1133,10 @@ impl Evaluator<'_> {
let c = -from_bytes!(f64, c); let c = -from_bytes!(f64, c);
Owned(c.to_le_bytes().into()) 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 { } else {
let mut c = c.to_vec(); let mut c = c.to_vec();
@ -1169,6 +1188,39 @@ impl Evaluator<'_> {
} }
if let TyKind::Scalar(chalk_ir::Scalar::Float(f)) = ty.kind(Interner) { if let TyKind::Scalar(chalk_ir::Scalar::Float(f)) = ty.kind(Interner) {
match f { 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 => { chalk_ir::FloatTy::F32 => {
let l = from_bytes!(f32, lc); let l = from_bytes!(f32, lc);
let r = from_bytes!(f32, rc); 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 { } else {
let is_signed = matches!(ty.as_builtin(), Some(BuiltinType::Int(_))); 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_") { if let Some(name) = name.strip_prefix("atomic_") {
return self.exec_atomic_intrinsic(name, args, generic_args, destination, locals, span); 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") { if let Some(name) = name.strip_suffix("f64") {
let result = match name { let result = match name {
"sqrt" | "sin" | "cos" | "exp" | "exp2" | "log" | "log10" | "log2" | "fabs" "sqrt" | "sin" | "cos" | "exp" | "exp2" | "log" | "log10" | "log2" | "fabs"

View file

@ -19,6 +19,7 @@ use hir_def::{
}; };
use hir_expand::name::Name; use hir_expand::name::Name;
use la_arena::ArenaMap; use la_arena::ArenaMap;
use rustc_apfloat::Float;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use syntax::TextRange; use syntax::TextRange;
use triomphe::Arc; 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::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::Uint(it, _) => Box::from(&it.to_le_bytes()[0..size()?]),
hir_def::hir::Literal::Float(f, _) => match 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()), 8 => Box::new(f.to_f64().to_le_bytes()),
4 => Box::new(f.to_f32().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 { pub fn float_ty_to_string(ty: FloatTy) -> &'static str {
match ty { match ty {
FloatTy::F16 => "f16",
FloatTy::F32 => "f32", FloatTy::F32 => "f32",
FloatTy::F64 => "f64", 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 { pub(super) fn float_ty_from_builtin(t: BuiltinFloat) -> FloatTy {
match t { match t {
BuiltinFloat::F16 => FloatTy::F16,
BuiltinFloat::F32 => FloatTy::F32, BuiltinFloat::F32 => FloatTy::F32,
BuiltinFloat::F64 => FloatTy::F64, BuiltinFloat::F64 => FloatTy::F64,
BuiltinFloat::F128 => FloatTy::F128,
} }
} }

View file

@ -111,8 +111,10 @@ fn infer_literal_pattern() {
if let "foo" = any() {} if let "foo" = any() {}
if let 1 = any() {} if let 1 = any() {}
if let 1u32 = any() {} if let 1u32 = any() {}
if let 1f16 = any() {}
if let 1f32 = any() {} if let 1f32 = any() {}
if let 1.0 = any() {} if let 1.0 = any() {}
if let 1f128 = any() {}
if let true = any() {} if let true = any() {}
} }
"#, "#,
@ -121,7 +123,7 @@ fn infer_literal_pattern() {
19..26 'loop {}': ! 19..26 'loop {}': !
24..26 '{}': () 24..26 '{}': ()
37..38 'x': &'? i32 37..38 'x': &'? i32
46..208 '{ ...) {} }': () 46..263 '{ ...) {} }': ()
52..75 'if let...y() {}': () 52..75 'if let...y() {}': ()
55..72 'let "f... any()': bool 55..72 'let "f... any()': bool
59..64 '"foo"': &'static str 59..64 '"foo"': &'static str
@ -145,25 +147,39 @@ fn infer_literal_pattern() {
124..126 '{}': () 124..126 '{}': ()
131..153 'if let...y() {}': () 131..153 'if let...y() {}': ()
134..150 'let 1f... any()': bool 134..150 'let 1f... any()': bool
138..142 '1f32': f32 138..142 '1f16': f16
138..142 '1f32': f32 138..142 '1f16': f16
145..148 'any': fn any<f32>() -> f32 145..148 'any': fn any<f16>() -> f16
145..150 'any()': f32 145..150 'any()': f16
151..153 '{}': () 151..153 '{}': ()
158..179 'if let...y() {}': () 158..180 'if let...y() {}': ()
161..176 'let 1.0 = any()': bool 161..177 'let 1f... any()': bool
165..168 '1.0': f64 165..169 '1f32': f32
165..168 '1.0': f64 165..169 '1f32': f32
171..174 'any': fn any<f64>() -> f64 172..175 'any': fn any<f32>() -> f32
171..176 'any()': f64 172..177 'any()': f32
177..179 '{}': () 178..180 '{}': ()
184..206 'if let...y() {}': () 185..206 'if let...y() {}': ()
187..203 'let tr... any()': bool 188..203 'let 1.0 = any()': bool
191..195 'true': bool 192..195 '1.0': f64
191..195 'true': bool 192..195 '1.0': f64
198..201 'any': fn any<bool>() -> bool 198..201 'any': fn any<f64>() -> f64
198..203 'any()': bool 198..203 'any()': f64
204..206 '{}': () 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##" r##"
fn test() { fn test() {
5i32; 5i32;
5f16;
5f32; 5f32;
5f64; 5f64;
5f128;
"hello"; "hello";
b"bytes"; b"bytes";
'c'; 'c';
@ -421,26 +423,28 @@ h";
} }
"##, "##,
expect![[r##" expect![[r##"
18..478 '{ ... }': () 18..515 '{ ... }': ()
32..36 '5i32': i32 32..36 '5i32': i32
50..54 '5f32': f32 50..54 '5f16': f16
68..72 '5f64': f64 68..72 '5f32': f32
86..93 '"hello"': &'static str 86..90 '5f64': f64
107..115 'b"bytes"': &'static [u8; 5] 104..109 '5f128': f128
129..132 ''c'': char 123..130 '"hello"': &'static str
146..150 'b'b'': u8 144..152 'b"bytes"': &'static [u8; 5]
164..168 '3.14': f64 166..169 ''c'': char
182..186 '5000': i32 183..187 'b'b'': u8
200..205 'false': bool 201..205 '3.14': f64
219..223 'true': bool 219..223 '5000': i32
237..333 'r#" ... "#': &'static str 237..242 'false': bool
347..357 'br#"yolo"#': &'static [u8; 4] 256..260 'true': bool
375..376 'a': &'static [u8; 4] 274..370 'r#" ... "#': &'static str
379..403 'b"a\x2... c"': &'static [u8; 4] 384..394 'br#"yolo"#': &'static [u8; 4]
421..422 'b': &'static [u8; 4] 412..413 'a': &'static [u8; 4]
425..433 'br"g\ h"': &'static [u8; 4] 416..440 'b"a\x2... c"': &'static [u8; 4]
451..452 'c': &'static [u8; 6] 458..459 'b': &'static [u8; 4]
455..467 'br#"x"\"yb"#': &'static [u8; 6] 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(_)) 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 { pub fn is_f32(&self) -> bool {
matches!( matches!(
self.inner, 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 { pub fn is_char(&self) -> bool {
matches!(self.inner, hir_def::builtin_type::BuiltinType::Char) matches!(self.inner, hir_def::builtin_type::BuiltinType::Char)
} }

View file

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

View file

@ -16,6 +16,10 @@ use ide_db::{
RootDatabase, RootDatabase,
}; };
use itertools::Itertools; use itertools::Itertools;
use rustc_apfloat::{
ieee::{Half as f16, Quad as f128},
Float,
};
use stdx::format_to; use stdx::format_to;
use syntax::{algo, ast, match_ast, AstNode, AstToken, Direction, SyntaxToken, T}; 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::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::Byte(byte) => byte .value().as_ref().map_err(|e| format!("{e:?}")).map(|it| format!("0x{it:X}")),
ast::FloatNumber(num) => { ast::FloatNumber(num) => {
let (text, _) = num.split_into_parts(); let text = num.value_string();
let text = text.replace('_', ""); if ty.as_builtin().map(|it| it.is_f16()).unwrap_or(false) {
if ty.as_builtin().map(|it| it.is_f32()).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>() { match text.parse::<f32>() {
Ok(num) => Ok(format!("{num} (bits: 0x{:X})", num.to_bits())), Ok(num) => Ok(format!("{num} (bits: 0x{:X})", num.to_bits())),
Err(e) => Err(e.to_string()), 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 { } else {
match text.parse::<f64>() { match text.parse::<f64>() {
Ok(num) => Ok(format!("{num} (bits: 0x{:X})", num.to_bits())), 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 This is a doc
"#]], "#]],
); );
@ -8045,6 +8088,22 @@ fn main() {
); );
check( check(
r#" r#"
fn main() {
$01.0f16;
}
"#,
expect![[r#"
*1.0f16*
```rust
f16
```
___
value of literal: 1 (bits: 0x3C00)
"#]],
);
check(
r#"
fn main() { fn main() {
$01.0f32; $01.0f32;
} }
@ -8061,6 +8120,22 @@ fn main() {
); );
check( check(
r#" r#"
fn main() {
$01.0f128;
}
"#,
expect![[r#"
*1.0f128*
```rust
f128
```
___
value of literal: 1 (bits: 0x3FFF0000000000000000000000000000)
"#]],
);
check(
r#"
fn main() { fn main() {
$0134e12; $0134e12;
} }
@ -8390,8 +8465,8 @@ impl Iterator for S {
file_id: FileId( file_id: FileId(
1, 1,
), ),
full_range: 7791..7999, full_range: 7800..8008,
focus_range: 7856..7862, focus_range: 7865..7871,
name: "Future", name: "Future",
kind: Trait, kind: Trait,
container_name: "future", container_name: "future",
@ -8404,8 +8479,8 @@ impl Iterator for S {
file_id: FileId( file_id: FileId(
1, 1,
), ),
full_range: 8629..9095, full_range: 8638..9104,
focus_range: 8673..8681, focus_range: 8682..8690,
name: "Iterator", name: "Iterator",
kind: Trait, kind: Trait,
container_name: "iterator", container_name: "iterator",

View file

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

View file

@ -489,6 +489,8 @@ impl ast::Byte {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use rustc_apfloat::ieee::Quad as f128;
use crate::ast::{self, make, FloatNumber, IntNumber}; use crate::ast::{self, make, FloatNumber, IntNumber};
fn check_float_suffix<'a>(lit: &str, expected: impl Into<Option<&'a str>>) { 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()); 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!( assert_eq!(
FloatNumber { syntax: make::tokens::literal(lit) }.value_string().parse::<f64>().ok(), FloatNumber { syntax: make::tokens::literal(lit) }.value_string().parse::<f128>().ok(),
expected.into() expected
); );
assert_eq!( assert_eq!(
IntNumber { syntax: make::tokens::literal(lit) }.value_string().parse::<f64>().ok(), IntNumber { syntax: make::tokens::literal(lit) }.value_string().parse::<f128>().ok(),
expected.into() expected
); );
} }
@ -520,9 +524,9 @@ mod tests {
check_float_suffix("123f32", "f32"); check_float_suffix("123f32", "f32");
check_float_suffix("123.0e", None); check_float_suffix("123.0e", None);
check_float_suffix("123.0e4", 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("123.0E4f32", "f32");
check_float_suffix("1_2_3.0_f32", "f32"); check_float_suffix("1_2_3.0_f128", "f128");
} }
#[test] #[test]
@ -589,8 +593,10 @@ bcde", b"abcde",
#[test] #[test]
fn test_value_underscores() { fn test_value_underscores() {
check_float_value("1.234567891011121_f64", 1.234567891011121_f64); check_float_value("1.3_4665449586950493453___6_f128", "1.346654495869504934536");
check_float_value("1__0.__0__f32", 10.0); 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("0b__1_0_", 2);
check_int_value("1_1_1_1_1_1", 111111); check_int_value("1_1_1_1_1_1", 111111);
} }

View file

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