Skip to content

Commit e3826ce

Browse files
committed
float: Use a fallback for f16 float format/parse if needed
In order to avoid crashes when compiling with Cranelift or on targets where f16 is not well supported, choose to use a fallback based on `target_has_reliable_f16`.
1 parent 5b2b504 commit e3826ce

File tree

11 files changed

+105
-2
lines changed

11 files changed

+105
-2
lines changed

library/core/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,6 @@ check-cfg = [
3636
# and to stdarch `core_arch` crate which messes-up with Cargo list
3737
# of declared features, we therefor expect any feature cfg
3838
'cfg(feature, values(any()))',
39+
# Internal features aren't marked known config by default
40+
'cfg(target_has_reliable_f16)',
3941
]

library/core/src/fmt/float.rs

+57-1
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,63 @@ macro_rules! floating {
230230
};
231231
}
232232

233-
floating! { f16 f32 f64 }
233+
floating! { f32 f64 }
234+
235+
#[cfg(bootstrap)]
236+
#[stable(feature = "rust1", since = "1.0.0")]
237+
impl Debug for f16 {
238+
#[inline]
239+
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
240+
write!(f, "{:#06x}", self.to_bits())
241+
}
242+
}
243+
244+
#[cfg(not(bootstrap))]
245+
#[cfg(target_has_reliable_f16)]
246+
floating! { f16 }
247+
248+
// FIXME(f16_f128): A fallback is used when the backend+target does not support f16 well, in order
249+
// to avoid ICEs.
250+
251+
#[cfg(not(bootstrap))]
252+
#[cfg(not(target_has_reliable_f16))]
253+
#[stable(feature = "rust1", since = "1.0.0")]
254+
impl Debug for f16 {
255+
#[inline]
256+
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
257+
write!(f, "{:#06x}", self.to_bits())
258+
}
259+
}
260+
261+
#[cfg(not(bootstrap))]
262+
#[cfg(not(target_has_reliable_f16))]
263+
#[stable(feature = "rust1", since = "1.0.0")]
264+
impl Display for f16 {
265+
#[inline]
266+
fn fmt(&self, fmt: &mut Formatter<'_>) -> Result {
267+
Debug::fmt(self, fmt)
268+
}
269+
}
270+
271+
#[cfg(not(bootstrap))]
272+
#[cfg(not(target_has_reliable_f16))]
273+
#[stable(feature = "rust1", since = "1.0.0")]
274+
impl LowerExp for f16 {
275+
#[inline]
276+
fn fmt(&self, fmt: &mut Formatter<'_>) -> Result {
277+
Debug::fmt(self, fmt)
278+
}
279+
}
280+
281+
#[cfg(not(bootstrap))]
282+
#[cfg(not(target_has_reliable_f16))]
283+
#[stable(feature = "rust1", since = "1.0.0")]
284+
impl UpperExp for f16 {
285+
#[inline]
286+
fn fmt(&self, fmt: &mut Formatter<'_>) -> Result {
287+
Debug::fmt(self, fmt)
288+
}
289+
}
234290

235291
#[stable(feature = "rust1", since = "1.0.0")]
236292
impl Debug for f128 {

library/core/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
//
9696
// Library features:
9797
// tidy-alphabetical-start
98+
#![cfg_attr(not(bootstrap), feature(cfg_target_has_reliable_f16_f128))]
9899
#![feature(array_ptr_get)]
99100
#![feature(asm_experimental_arch)]
100101
#![feature(bigint_helper_methods)]

library/core/src/num/dec2flt/float.rs

+2
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,8 @@ const fn pow2_to_pow10(a: i64) -> i64 {
218218
res as i64
219219
}
220220

221+
#[cfg(not(bootstrap))]
222+
#[cfg(target_has_reliable_f16)]
221223
impl RawFloat for f16 {
222224
type Int = u16;
223225

library/core/src/num/dec2flt/mod.rs

+16
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,26 @@ macro_rules! from_str_float_impl {
172172
};
173173
}
174174

175+
#[cfg(not(bootstrap))]
176+
#[cfg(target_has_reliable_f16)]
175177
from_str_float_impl!(f16);
176178
from_str_float_impl!(f32);
177179
from_str_float_impl!(f64);
178180

181+
// FIXME(f16_f128): A fallback is used when the backend+target does not support f16 well, in order
182+
// to avoid ICEs.
183+
184+
// After the bootstrap bump this should be: `#[cfg(not(target_has_reliable_f16))`
185+
#[cfg(any(bootstrap, all(not(bootstrap), not(target_has_reliable_f16))))]
186+
impl FromStr for f16 {
187+
type Err = ParseFloatError;
188+
189+
#[inline]
190+
fn from_str(_src: &str) -> Result<Self, ParseFloatError> {
191+
unimplemented!("requires target_has_reliable_f16")
192+
}
193+
}
194+
179195
/// An error which can be returned when parsing a float.
180196
///
181197
/// This error is used as the error type for the [`FromStr`] implementation

library/core/src/num/flt2dec/decoder.rs

+2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ pub trait DecodableFloat: RawFloat + Copy {
4545
fn min_pos_norm_value() -> Self;
4646
}
4747

48+
#[cfg(not(bootstrap))]
49+
#[cfg(target_has_reliable_f16)]
4850
impl DecodableFloat for f16 {
4951
fn min_pos_norm_value() -> Self {
5052
f16::MIN_POSITIVE

library/coretests/tests/num/dec2flt/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ mod parse;
1111
// Requires a *polymorphic literal*, i.e., one that can serve as f64 as well as f32.
1212
macro_rules! test_literal {
1313
($x: expr) => {{
14+
#[cfg(not(bootstrap))]
15+
#[cfg(target_has_reliable_f16)]
1416
let x16: f16 = $x;
1517
let x32: f32 = $x;
1618
let x64: f64 = $x;
@@ -19,11 +21,15 @@ macro_rules! test_literal {
1921
for input in inputs {
2022
assert_eq!(input.parse(), Ok(x64), "failed f64 {input}");
2123
assert_eq!(input.parse(), Ok(x32), "failed f32 {input}");
24+
#[cfg(not(bootstrap))]
25+
#[cfg(target_has_reliable_f16)]
2226
assert_eq!(input.parse(), Ok(x16), "failed f16 {input}");
2327

2428
let neg_input = format!("-{input}");
2529
assert_eq!(neg_input.parse(), Ok(-x64), "failed f64 {neg_input}");
2630
assert_eq!(neg_input.parse(), Ok(-x32), "failed f32 {neg_input}");
31+
#[cfg(not(bootstrap))]
32+
#[cfg(target_has_reliable_f16)]
2733
assert_eq!(neg_input.parse(), Ok(-x16), "failed f16 {neg_input}");
2834
}
2935
}};

library/coretests/tests/num/dec2flt/parse.rs

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ fn new_dec(e: i64, m: u64) -> Decimal {
1010
fn missing_pieces() {
1111
let permutations = &[".e", "1e", "e4", "e", ".12e", "321.e", "32.12e+", "12.32e-"];
1212
for &s in permutations {
13+
#[cfg(not(bootstrap))]
14+
#[cfg(target_has_reliable_f16)]
1315
assert_eq!(dec2flt::<f16>(s), Err(pfe_invalid()));
1416
assert_eq!(dec2flt::<f32>(s), Err(pfe_invalid()));
1517
assert_eq!(dec2flt::<f64>(s), Err(pfe_invalid()));
@@ -28,6 +30,8 @@ fn invalid_chars() {
2830
input.push_str(s);
2931
input.insert(i, c);
3032

33+
#[cfg(not(bootstrap))]
34+
#[cfg(target_has_reliable_f16)]
3135
assert_eq!(
3236
dec2flt::<f16>(&input),
3337
Err(pfe_invalid()),

src/etc/test-float-parse/Cargo.toml

+7
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,10 @@ rayon = "1"
1313

1414
[lib]
1515
name = "test_float_parse"
16+
17+
[lints.rust.unexpected_cfgs]
18+
level = "warn"
19+
check-cfg = [
20+
# Internal features aren't marked known config by default
21+
'cfg(target_has_reliable_f16)',
22+
]

src/etc/test-float-parse/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#![feature(f16)]
2+
#![feature(cfg_target_has_reliable_f16_f128)]
3+
#![expect(internal_features)] // reliable_f16_f128
24

35
mod traits;
46
mod ui;
@@ -116,6 +118,8 @@ pub fn register_tests(cfg: &Config) -> Vec<TestInfo> {
116118
let mut tests = Vec::new();
117119

118120
// Register normal generators for all floats.
121+
122+
#[cfg(target_has_reliable_f16)]
119123
register_float::<f16>(&mut tests, cfg);
120124
register_float::<f32>(&mut tests, cfg);
121125
register_float::<f64>(&mut tests, cfg);

src/etc/test-float-parse/src/traits.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,10 @@ macro_rules! impl_float {
168168
}
169169
}
170170

171-
impl_float!(f16, u16; f32, u32; f64, u64);
171+
impl_float!(f32, u32; f64, u64);
172+
173+
#[cfg(target_has_reliable_f16)]
174+
impl_float!(f16, u16);
172175

173176
/// A test generator. Should provide an iterator that produces unique patterns to parse.
174177
///

0 commit comments

Comments
 (0)