Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 6b7c61e

Browse files
committedAug 16, 2024·
Auto merge of rust-lang#129158 - matthiaskrgr:rollup-taw1cmy, r=matthiaskrgr
Rollup of 3 pull requests Successful merges: - rust-lang#128598 (float to/from bits and classify: update for float semantics RFC) - rust-lang#128990 (Re-enable more debuginfo tests on freebsd) - rust-lang#129042 (Special-case alias ty during the delayed bug emission in `try_from_lit`) r? `@ghost` `@rustbot` modify labels: rollup
2 parents a73bc4a + 955e9c1 commit 6b7c61e

File tree

13 files changed

+114
-688
lines changed

13 files changed

+114
-688
lines changed
 

‎compiler/rustc_middle/src/ty/consts.rs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,10 @@ impl<'tcx> Const<'tcx> {
305305
// mir.
306306
match tcx.at(expr.span).lit_to_const(lit_input) {
307307
Ok(c) => return Some(c),
308+
Err(_) if lit_input.ty.has_aliases() => {
309+
// allow the `ty` to be an alias type, though we cannot handle it here
310+
return None;
311+
}
308312
Err(e) => {
309313
tcx.dcx().span_delayed_bug(
310314
expr.span,

‎library/core/src/num/f128.rs‎

Lines changed: 9 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ impl f128 {
290290
#[inline]
291291
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
292292
pub(crate) const fn abs_private(self) -> f128 {
293-
// SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
293+
// SAFETY: This transmutation is fine just like in `to_bits`/`from_bits`.
294294
unsafe {
295295
mem::transmute::<u128, f128>(mem::transmute::<f128, u128>(self) & !Self::SIGN_MASK)
296296
}
@@ -439,22 +439,12 @@ impl f128 {
439439
#[unstable(feature = "f128", issue = "116909")]
440440
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
441441
pub const fn classify(self) -> FpCategory {
442-
// Other float types cannot use a bitwise classify because they may suffer a variety
443-
// of errors if the backend chooses to cast to different float types (x87). `f128` cannot
444-
// fit into any other float types so this is not a concern, and we rely on bit patterns.
442+
// Other float types suffer from various platform bugs that violate the usual IEEE semantics
443+
// and also make bitwise classification not always work reliably. However, `f128` cannot fit
444+
// into any other float types so this is not a concern, and we can rely on bit patterns.
445445

446-
// SAFETY: POD bitcast, same as in `to_bits`.
447-
let bits = unsafe { mem::transmute::<f128, u128>(self) };
448-
Self::classify_bits(bits)
449-
}
450-
451-
/// This operates on bits, and only bits, so it can ignore concerns about weird FPUs.
452-
/// FIXME(jubilee): In a just world, this would be the entire impl for classify,
453-
/// plus a transmute. We do not live in a just world, but we can make it more so.
454-
#[inline]
455-
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
456-
const fn classify_bits(b: u128) -> FpCategory {
457-
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
446+
let bits = self.to_bits();
447+
match (bits & Self::MAN_MASK, bits & Self::EXP_MASK) {
458448
(0, Self::EXP_MASK) => FpCategory::Infinite,
459449
(_, Self::EXP_MASK) => FpCategory::Nan,
460450
(0, 0) => FpCategory::Zero,
@@ -922,48 +912,7 @@ impl f128 {
922912
#[must_use = "this returns the result of the operation, without modifying the original"]
923913
pub const fn to_bits(self) -> u128 {
924914
// SAFETY: `u128` is a plain old datatype so we can always transmute to it.
925-
// ...sorta.
926-
//
927-
// It turns out that at runtime, it is possible for a floating point number
928-
// to be subject to a floating point mode that alters nonzero subnormal numbers
929-
// to zero on reads and writes, aka "denormals are zero" and "flush to zero".
930-
//
931-
// And, of course evaluating to a NaN value is fairly nondeterministic.
932-
// More precisely: when NaN should be returned is knowable, but which NaN?
933-
// So far that's defined by a combination of LLVM and the CPU, not Rust.
934-
// This function, however, allows observing the bitstring of a NaN,
935-
// thus introspection on CTFE.
936-
//
937-
// In order to preserve, at least for the moment, const-to-runtime equivalence,
938-
// we reject any of these possible situations from happening.
939-
#[inline]
940-
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
941-
const fn ct_f128_to_u128(ct: f128) -> u128 {
942-
// FIXME(f16_f128): we should use `.classify()` like `f32` and `f64`, but that
943-
// is not available on all platforms (needs `netf2` and `unordtf2`). So classify
944-
// the bits instead.
945-
946-
// SAFETY: this is a POD transmutation
947-
let bits = unsafe { mem::transmute::<f128, u128>(ct) };
948-
match f128::classify_bits(bits) {
949-
FpCategory::Nan => {
950-
panic!("const-eval error: cannot use f128::to_bits on a NaN")
951-
}
952-
FpCategory::Subnormal => {
953-
panic!("const-eval error: cannot use f128::to_bits on a subnormal number")
954-
}
955-
FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => bits,
956-
}
957-
}
958-
959-
#[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
960-
fn rt_f128_to_u128(x: f128) -> u128 {
961-
// SAFETY: `u128` is a plain old datatype so we can always... uh...
962-
// ...look, just pretend you forgot what you just read.
963-
// Stability concerns.
964-
unsafe { mem::transmute(x) }
965-
}
966-
intrinsics::const_eval_select((self,), ct_f128_to_u128, rt_f128_to_u128)
915+
unsafe { mem::transmute(self) }
967916
}
968917

969918
/// Raw transmutation from `u128`.
@@ -1011,49 +960,8 @@ impl f128 {
1011960
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
1012961
pub const fn from_bits(v: u128) -> Self {
1013962
// It turns out the safety issues with sNaN were overblown! Hooray!
1014-
// SAFETY: `u128` is a plain old datatype so we can always transmute from it
1015-
// ...sorta.
1016-
//
1017-
// It turns out that at runtime, it is possible for a floating point number
1018-
// to be subject to floating point modes that alter nonzero subnormal numbers
1019-
// to zero on reads and writes, aka "denormals are zero" and "flush to zero".
1020-
// This is not a problem usually, but at least one tier2 platform for Rust
1021-
// actually exhibits this behavior by default: thumbv7neon
1022-
// aka "the Neon FPU in AArch32 state"
1023-
//
1024-
// And, of course evaluating to a NaN value is fairly nondeterministic.
1025-
// More precisely: when NaN should be returned is knowable, but which NaN?
1026-
// So far that's defined by a combination of LLVM and the CPU, not Rust.
1027-
// This function, however, allows observing the bitstring of a NaN,
1028-
// thus introspection on CTFE.
1029-
//
1030-
// In order to preserve, at least for the moment, const-to-runtime equivalence,
1031-
// reject any of these possible situations from happening.
1032-
#[inline]
1033-
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
1034-
const fn ct_u128_to_f128(ct: u128) -> f128 {
1035-
match f128::classify_bits(ct) {
1036-
FpCategory::Subnormal => {
1037-
panic!("const-eval error: cannot use f128::from_bits on a subnormal number")
1038-
}
1039-
FpCategory::Nan => {
1040-
panic!("const-eval error: cannot use f128::from_bits on NaN")
1041-
}
1042-
FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
1043-
// SAFETY: It's not a frumious number
1044-
unsafe { mem::transmute::<u128, f128>(ct) }
1045-
}
1046-
}
1047-
}
1048-
1049-
#[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
1050-
fn rt_u128_to_f128(x: u128) -> f128 {
1051-
// SAFETY: `u128` is a plain old datatype so we can always... uh...
1052-
// ...look, just pretend you forgot what you just read.
1053-
// Stability concerns.
1054-
unsafe { mem::transmute(x) }
1055-
}
1056-
intrinsics::const_eval_select((v,), ct_u128_to_f128, rt_u128_to_f128)
963+
// SAFETY: `u128` is a plain old datatype so we can always transmute from it.
964+
unsafe { mem::transmute(v) }
1057965
}
1058966

1059967
/// Returns the memory representation of this floating point number as a byte array in

‎library/core/src/num/f16.rs‎

Lines changed: 26 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ impl f16 {
284284
#[inline]
285285
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
286286
pub(crate) const fn abs_private(self) -> f16 {
287-
// SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
287+
// SAFETY: This transmutation is fine just like in `to_bits`/`from_bits`.
288288
unsafe { mem::transmute::<u16, f16>(mem::transmute::<f16, u16>(self) & !Self::SIGN_MASK) }
289289
}
290290

@@ -426,15 +426,15 @@ impl f16 {
426426
pub const fn classify(self) -> FpCategory {
427427
// A previous implementation for f32/f64 tried to only use bitmask-based checks,
428428
// using `to_bits` to transmute the float to its bit repr and match on that.
429-
// Unfortunately, floating point numbers can be much worse than that.
430-
// This also needs to not result in recursive evaluations of `to_bits`.
429+
// If we only cared about being "technically" correct, that's an entirely legit
430+
// implementation.
431431
//
432-
433-
// Platforms without native support generally convert to `f32` to perform operations,
434-
// and most of these platforms correctly round back to `f16` after each operation.
435-
// However, some platforms have bugs where they keep the excess `f32` precision (e.g.
436-
// WASM, see llvm/llvm-project#96437). This implementation makes a best-effort attempt
437-
// to account for that excess precision.
432+
// Unfortunately, there are platforms out there that do not correctly implement the IEEE
433+
// float semantics Rust relies on: some hardware flushes denormals to zero, and some
434+
// platforms convert to `f32` to perform operations without properly rounding back (e.g.
435+
// WASM, see llvm/llvm-project#96437). These are platforms bugs, and Rust will misbehave on
436+
// such platforms, but we can at least try to make things seem as sane as possible by being
437+
// careful here.
438438
if self.is_infinite() {
439439
// Thus, a value may compare unequal to infinity, despite having a "full" exponent mask.
440440
FpCategory::Infinite
@@ -446,49 +446,20 @@ impl f16 {
446446
// as correctness requires avoiding equality tests that may be Subnormal == -0.0
447447
// because it may be wrong under "denormals are zero" and "flush to zero" modes.
448448
// Most of std's targets don't use those, but they are used for thumbv7neon.
449-
// So, this does use bitpattern matching for the rest.
450-
451-
// SAFETY: f16 to u16 is fine. Usually.
452-
// If classify has gotten this far, the value is definitely in one of these categories.
453-
unsafe { f16::partial_classify(self) }
454-
}
455-
}
456-
457-
/// This doesn't actually return a right answer for NaN on purpose,
458-
/// seeing as how it cannot correctly discern between a floating point NaN,
459-
/// and some normal floating point numbers truncated from an x87 FPU.
460-
///
461-
/// # Safety
462-
///
463-
/// This requires making sure you call this function for values it answers correctly on,
464-
/// otherwise it returns a wrong answer. This is not important for memory safety per se,
465-
/// but getting floats correct is important for not accidentally leaking const eval
466-
/// runtime-deviating logic which may or may not be acceptable.
467-
#[inline]
468-
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
469-
const unsafe fn partial_classify(self) -> FpCategory {
470-
// SAFETY: The caller is not asking questions for which this will tell lies.
471-
let b = unsafe { mem::transmute::<f16, u16>(self) };
472-
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
473-
(0, Self::EXP_MASK) => FpCategory::Infinite,
474-
(0, 0) => FpCategory::Zero,
475-
(_, 0) => FpCategory::Subnormal,
476-
_ => FpCategory::Normal,
477-
}
478-
}
479-
480-
/// This operates on bits, and only bits, so it can ignore concerns about weird FPUs.
481-
/// FIXME(jubilee): In a just world, this would be the entire impl for classify,
482-
/// plus a transmute. We do not live in a just world, but we can make it more so.
483-
#[inline]
484-
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
485-
const fn classify_bits(b: u16) -> FpCategory {
486-
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
487-
(0, Self::EXP_MASK) => FpCategory::Infinite,
488-
(_, Self::EXP_MASK) => FpCategory::Nan,
489-
(0, 0) => FpCategory::Zero,
490-
(_, 0) => FpCategory::Subnormal,
491-
_ => FpCategory::Normal,
449+
// So, this does use bitpattern matching for the rest. On x87, due to the incorrect
450+
// float codegen on this hardware, this doesn't actually return a right answer for NaN
451+
// because it cannot correctly discern between a floating point NaN, and some normal
452+
// floating point numbers truncated from an x87 FPU -- but we took care of NaN above, so
453+
// we are fine.
454+
// FIXME(jubilee): This probably could at least answer things correctly for Infinity,
455+
// like the f64 version does, but I need to run more checks on how things go on x86.
456+
// I fear losing mantissa data that would have answered that differently.
457+
let b = self.to_bits();
458+
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
459+
(0, 0) => FpCategory::Zero,
460+
(_, 0) => FpCategory::Subnormal,
461+
_ => FpCategory::Normal,
462+
}
492463
}
493464
}
494465

@@ -952,48 +923,7 @@ impl f16 {
952923
#[must_use = "this returns the result of the operation, without modifying the original"]
953924
pub const fn to_bits(self) -> u16 {
954925
// SAFETY: `u16` is a plain old datatype so we can always transmute to it.
955-
// ...sorta.
956-
//
957-
// It turns out that at runtime, it is possible for a floating point number
958-
// to be subject to a floating point mode that alters nonzero subnormal numbers
959-
// to zero on reads and writes, aka "denormals are zero" and "flush to zero".
960-
//
961-
// And, of course evaluating to a NaN value is fairly nondeterministic.
962-
// More precisely: when NaN should be returned is knowable, but which NaN?
963-
// So far that's defined by a combination of LLVM and the CPU, not Rust.
964-
// This function, however, allows observing the bitstring of a NaN,
965-
// thus introspection on CTFE.
966-
//
967-
// In order to preserve, at least for the moment, const-to-runtime equivalence,
968-
// we reject any of these possible situations from happening.
969-
#[inline]
970-
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
971-
const fn ct_f16_to_u16(ct: f16) -> u16 {
972-
// FIXME(f16_f128): we should use `.classify()` like `f32` and `f64`, but we don't yet
973-
// want to rely on that on all platforms because it is nondeterministic (e.g. x86 has
974-
// convention discrepancies calling intrinsics). So just classify the bits instead.
975-
976-
// SAFETY: this is a POD transmutation
977-
let bits = unsafe { mem::transmute::<f16, u16>(ct) };
978-
match f16::classify_bits(bits) {
979-
FpCategory::Nan => {
980-
panic!("const-eval error: cannot use f16::to_bits on a NaN")
981-
}
982-
FpCategory::Subnormal => {
983-
panic!("const-eval error: cannot use f16::to_bits on a subnormal number")
984-
}
985-
FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => bits,
986-
}
987-
}
988-
989-
#[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
990-
fn rt_f16_to_u16(x: f16) -> u16 {
991-
// SAFETY: `u16` is a plain old datatype so we can always... uh...
992-
// ...look, just pretend you forgot what you just read.
993-
// Stability concerns.
994-
unsafe { mem::transmute(x) }
995-
}
996-
intrinsics::const_eval_select((self,), ct_f16_to_u16, rt_f16_to_u16)
926+
unsafe { mem::transmute(self) }
997927
}
998928

999929
/// Raw transmutation from `u16`.
@@ -1040,49 +970,8 @@ impl f16 {
1040970
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
1041971
pub const fn from_bits(v: u16) -> Self {
1042972
// It turns out the safety issues with sNaN were overblown! Hooray!
1043-
// SAFETY: `u16` is a plain old datatype so we can always transmute from it
1044-
// ...sorta.
1045-
//
1046-
// It turns out that at runtime, it is possible for a floating point number
1047-
// to be subject to floating point modes that alter nonzero subnormal numbers
1048-
// to zero on reads and writes, aka "denormals are zero" and "flush to zero".
1049-
// This is not a problem usually, but at least one tier2 platform for Rust
1050-
// actually exhibits this behavior by default: thumbv7neon
1051-
// aka "the Neon FPU in AArch32 state"
1052-
//
1053-
// And, of course evaluating to a NaN value is fairly nondeterministic.
1054-
// More precisely: when NaN should be returned is knowable, but which NaN?
1055-
// So far that's defined by a combination of LLVM and the CPU, not Rust.
1056-
// This function, however, allows observing the bitstring of a NaN,
1057-
// thus introspection on CTFE.
1058-
//
1059-
// In order to preserve, at least for the moment, const-to-runtime equivalence,
1060-
// reject any of these possible situations from happening.
1061-
#[inline]
1062-
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
1063-
const fn ct_u16_to_f16(ct: u16) -> f16 {
1064-
match f16::classify_bits(ct) {
1065-
FpCategory::Subnormal => {
1066-
panic!("const-eval error: cannot use f16::from_bits on a subnormal number")
1067-
}
1068-
FpCategory::Nan => {
1069-
panic!("const-eval error: cannot use f16::from_bits on NaN")
1070-
}
1071-
FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
1072-
// SAFETY: It's not a frumious number
1073-
unsafe { mem::transmute::<u16, f16>(ct) }
1074-
}
1075-
}
1076-
}
1077-
1078-
#[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
1079-
fn rt_u16_to_f16(x: u16) -> f16 {
1080-
// SAFETY: `u16` is a plain old datatype so we can always... uh...
1081-
// ...look, just pretend you forgot what you just read.
1082-
// Stability concerns.
1083-
unsafe { mem::transmute(x) }
1084-
}
1085-
intrinsics::const_eval_select((v,), ct_u16_to_f16, rt_u16_to_f16)
973+
// SAFETY: `u16` is a plain old datatype so we can always transmute from it.
974+
unsafe { mem::transmute(v) }
1086975
}
1087976

1088977
/// Returns the memory representation of this floating point number as a byte array in

‎library/core/src/num/f32.rs‎

Lines changed: 27 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@ impl f32 {
529529
#[inline]
530530
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
531531
pub(crate) const fn abs_private(self) -> f32 {
532-
// SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
532+
// SAFETY: This transmutation is fine just like in `to_bits`/`from_bits`.
533533
unsafe { mem::transmute::<u32, f32>(mem::transmute::<f32, u32>(self) & !Self::SIGN_MASK) }
534534
}
535535

@@ -654,18 +654,20 @@ impl f32 {
654654
pub const fn classify(self) -> FpCategory {
655655
// A previous implementation tried to only use bitmask-based checks,
656656
// using f32::to_bits to transmute the float to its bit repr and match on that.
657-
// Unfortunately, floating point numbers can be much worse than that.
658-
// This also needs to not result in recursive evaluations of f64::to_bits.
657+
// If we only cared about being "technically" correct, that's an entirely legit
658+
// implementation.
659+
//
660+
// Unfortunately, there is hardware out there that does not correctly implement the IEEE
661+
// float semantics Rust relies on: x87 uses a too-large mantissa and exponent, and some
662+
// hardware flushes subnormals to zero. These are platforms bugs, and Rust will misbehave on
663+
// such hardware, but we can at least try to make things seem as sane as possible by being
664+
// careful here.
659665
//
660-
// On some processors, in some cases, LLVM will "helpfully" lower floating point ops,
661-
// in spite of a request for them using f32 and f64, to things like x87 operations.
662-
// These have an f64's mantissa, but can have a larger than normal exponent.
663666
// FIXME(jubilee): Using x87 operations is never necessary in order to function
664667
// on x86 processors for Rust-to-Rust calls, so this issue should not happen.
665668
// Code generation should be adjusted to use non-C calling conventions, avoiding this.
666-
//
667669
if self.is_infinite() {
668-
// Thus, a value may compare unequal to infinity, despite having a "full" exponent mask.
670+
// A value may compare unequal to infinity, despite having a "full" exponent mask.
669671
FpCategory::Infinite
670672
} else if self.is_nan() {
671673
// And it may not be NaN, as it can simply be an "overextended" finite value.
@@ -675,48 +677,20 @@ impl f32 {
675677
// as correctness requires avoiding equality tests that may be Subnormal == -0.0
676678
// because it may be wrong under "denormals are zero" and "flush to zero" modes.
677679
// Most of std's targets don't use those, but they are used for thumbv7neon.
678-
// So, this does use bitpattern matching for the rest.
679-
680-
// SAFETY: f32 to u32 is fine. Usually.
681-
// If classify has gotten this far, the value is definitely in one of these categories.
682-
unsafe { f32::partial_classify(self) }
683-
}
684-
}
685-
686-
// This doesn't actually return a right answer for NaN on purpose,
687-
// seeing as how it cannot correctly discern between a floating point NaN,
688-
// and some normal floating point numbers truncated from an x87 FPU.
689-
// FIXME(jubilee): This probably could at least answer things correctly for Infinity,
690-
// like the f64 version does, but I need to run more checks on how things go on x86.
691-
// I fear losing mantissa data that would have answered that differently.
692-
//
693-
// # Safety
694-
// This requires making sure you call this function for values it answers correctly on,
695-
// otherwise it returns a wrong answer. This is not important for memory safety per se,
696-
// but getting floats correct is important for not accidentally leaking const eval
697-
// runtime-deviating logic which may or may not be acceptable.
698-
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
699-
const unsafe fn partial_classify(self) -> FpCategory {
700-
// SAFETY: The caller is not asking questions for which this will tell lies.
701-
let b = unsafe { mem::transmute::<f32, u32>(self) };
702-
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
703-
(0, 0) => FpCategory::Zero,
704-
(_, 0) => FpCategory::Subnormal,
705-
_ => FpCategory::Normal,
706-
}
707-
}
708-
709-
// This operates on bits, and only bits, so it can ignore concerns about weird FPUs.
710-
// FIXME(jubilee): In a just world, this would be the entire impl for classify,
711-
// plus a transmute. We do not live in a just world, but we can make it more so.
712-
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
713-
const fn classify_bits(b: u32) -> FpCategory {
714-
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
715-
(0, Self::EXP_MASK) => FpCategory::Infinite,
716-
(_, Self::EXP_MASK) => FpCategory::Nan,
717-
(0, 0) => FpCategory::Zero,
718-
(_, 0) => FpCategory::Subnormal,
719-
_ => FpCategory::Normal,
680+
// So, this does use bitpattern matching for the rest. On x87, due to the incorrect
681+
// float codegen on this hardware, this doesn't actually return a right answer for NaN
682+
// because it cannot correctly discern between a floating point NaN, and some normal
683+
// floating point numbers truncated from an x87 FPU -- but we took care of NaN above, so
684+
// we are fine.
685+
// FIXME(jubilee): This probably could at least answer things correctly for Infinity,
686+
// like the f64 version does, but I need to run more checks on how things go on x86.
687+
// I fear losing mantissa data that would have answered that differently.
688+
let b = self.to_bits();
689+
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
690+
(0, 0) => FpCategory::Zero,
691+
(_, 0) => FpCategory::Subnormal,
692+
_ => FpCategory::Normal,
693+
}
720694
}
721695
}
722696

@@ -1143,51 +1117,7 @@ impl f32 {
11431117
#[inline]
11441118
pub const fn to_bits(self) -> u32 {
11451119
// SAFETY: `u32` is a plain old datatype so we can always transmute to it.
1146-
// ...sorta.
1147-
//
1148-
// It turns out that at runtime, it is possible for a floating point number
1149-
// to be subject to a floating point mode that alters nonzero subnormal numbers
1150-
// to zero on reads and writes, aka "denormals are zero" and "flush to zero".
1151-
// This is not a problem per se, but at least one tier2 platform for Rust
1152-
// actually exhibits this behavior by default.
1153-
//
1154-
// In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled,
1155-
// i.e. not soft-float, the way Rust does parameter passing can actually alter
1156-
// a number that is "not infinity" to have the same exponent as infinity,
1157-
// in a slightly unpredictable manner.
1158-
//
1159-
// And, of course evaluating to a NaN value is fairly nondeterministic.
1160-
// More precisely: when NaN should be returned is knowable, but which NaN?
1161-
// So far that's defined by a combination of LLVM and the CPU, not Rust.
1162-
// This function, however, allows observing the bitstring of a NaN,
1163-
// thus introspection on CTFE.
1164-
//
1165-
// In order to preserve, at least for the moment, const-to-runtime equivalence,
1166-
// we reject any of these possible situations from happening.
1167-
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
1168-
const fn ct_f32_to_u32(ct: f32) -> u32 {
1169-
match ct.classify() {
1170-
FpCategory::Nan => {
1171-
panic!("const-eval error: cannot use f32::to_bits on a NaN")
1172-
}
1173-
FpCategory::Subnormal => {
1174-
panic!("const-eval error: cannot use f32::to_bits on a subnormal number")
1175-
}
1176-
FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
1177-
// SAFETY: We have a normal floating point number. Now we transmute, i.e. do a bitcopy.
1178-
unsafe { mem::transmute::<f32, u32>(ct) }
1179-
}
1180-
}
1181-
}
1182-
1183-
#[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
1184-
fn rt_f32_to_u32(x: f32) -> u32 {
1185-
// SAFETY: `u32` is a plain old datatype so we can always... uh...
1186-
// ...look, just pretend you forgot what you just read.
1187-
// Stability concerns.
1188-
unsafe { mem::transmute(x) }
1189-
}
1190-
intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32)
1120+
unsafe { mem::transmute(self) }
11911121
}
11921122

11931123
/// Raw transmutation from `u32`.
@@ -1232,53 +1162,8 @@ impl f32 {
12321162
#[inline]
12331163
pub const fn from_bits(v: u32) -> Self {
12341164
// It turns out the safety issues with sNaN were overblown! Hooray!
1235-
// SAFETY: `u32` is a plain old datatype so we can always transmute from it
1236-
// ...sorta.
1237-
//
1238-
// It turns out that at runtime, it is possible for a floating point number
1239-
// to be subject to floating point modes that alter nonzero subnormal numbers
1240-
// to zero on reads and writes, aka "denormals are zero" and "flush to zero".
1241-
// This is not a problem usually, but at least one tier2 platform for Rust
1242-
// actually exhibits this behavior by default: thumbv7neon
1243-
// aka "the Neon FPU in AArch32 state"
1244-
//
1245-
// In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled,
1246-
// i.e. not soft-float, the way Rust does parameter passing can actually alter
1247-
// a number that is "not infinity" to have the same exponent as infinity,
1248-
// in a slightly unpredictable manner.
1249-
//
1250-
// And, of course evaluating to a NaN value is fairly nondeterministic.
1251-
// More precisely: when NaN should be returned is knowable, but which NaN?
1252-
// So far that's defined by a combination of LLVM and the CPU, not Rust.
1253-
// This function, however, allows observing the bitstring of a NaN,
1254-
// thus introspection on CTFE.
1255-
//
1256-
// In order to preserve, at least for the moment, const-to-runtime equivalence,
1257-
// reject any of these possible situations from happening.
1258-
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
1259-
const fn ct_u32_to_f32(ct: u32) -> f32 {
1260-
match f32::classify_bits(ct) {
1261-
FpCategory::Subnormal => {
1262-
panic!("const-eval error: cannot use f32::from_bits on a subnormal number")
1263-
}
1264-
FpCategory::Nan => {
1265-
panic!("const-eval error: cannot use f32::from_bits on NaN")
1266-
}
1267-
FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
1268-
// SAFETY: It's not a frumious number
1269-
unsafe { mem::transmute::<u32, f32>(ct) }
1270-
}
1271-
}
1272-
}
1273-
1274-
#[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
1275-
fn rt_u32_to_f32(x: u32) -> f32 {
1276-
// SAFETY: `u32` is a plain old datatype so we can always... uh...
1277-
// ...look, just pretend you forgot what you just read.
1278-
// Stability concerns.
1279-
unsafe { mem::transmute(x) }
1280-
}
1281-
intrinsics::const_eval_select((v,), ct_u32_to_f32, rt_u32_to_f32)
1165+
// SAFETY: `u32` is a plain old datatype so we can always transmute from it.
1166+
unsafe { mem::transmute(v) }
12821167
}
12831168

12841169
/// Returns the memory representation of this floating point number as a byte array in

‎library/core/src/num/f64.rs‎

Lines changed: 23 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,7 @@ impl f64 {
528528
#[inline]
529529
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
530530
pub(crate) const fn abs_private(self) -> f64 {
531-
// SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
531+
// SAFETY: This transmutation is fine just like in `to_bits`/`from_bits`.
532532
unsafe { mem::transmute::<u64, f64>(mem::transmute::<f64, u64>(self) & !Self::SIGN_MASK) }
533533
}
534534

@@ -653,12 +653,14 @@ impl f64 {
653653
pub const fn classify(self) -> FpCategory {
654654
// A previous implementation tried to only use bitmask-based checks,
655655
// using f64::to_bits to transmute the float to its bit repr and match on that.
656-
// Unfortunately, floating point numbers can be much worse than that.
657-
// This also needs to not result in recursive evaluations of f64::to_bits.
656+
// If we only cared about being "technically" correct, that's an entirely legit
657+
// implementation.
658+
//
659+
// Unfortunately, there is hardware out there that does not correctly implement the IEEE
660+
// float semantics Rust relies on: x87 uses a too-large exponent, and some hardware flushes
661+
// subnormals to zero. These are platforms bugs, and Rust will misbehave on such hardware,
662+
// but we can at least try to make things seem as sane as possible by being careful here.
658663
//
659-
// On some processors, in some cases, LLVM will "helpfully" lower floating point ops,
660-
// in spite of a request for them using f32 and f64, to things like x87 operations.
661-
// These have an f64's mantissa, but can have a larger than normal exponent.
662664
// FIXME(jubilee): Using x87 operations is never necessary in order to function
663665
// on x86 processors for Rust-to-Rust calls, so this issue should not happen.
664666
// Code generation should be adjusted to use non-C calling conventions, avoiding this.
@@ -672,41 +674,18 @@ impl f64 {
672674
// as correctness requires avoiding equality tests that may be Subnormal == -0.0
673675
// because it may be wrong under "denormals are zero" and "flush to zero" modes.
674676
// Most of std's targets don't use those, but they are used for thumbv7neon.
675-
// So, this does use bitpattern matching for the rest.
676-
677-
// SAFETY: f64 to u64 is fine. Usually.
678-
// If control flow has gotten this far, the value is definitely in one of the categories
679-
// that f64::partial_classify can correctly analyze.
680-
unsafe { f64::partial_classify(self) }
681-
}
682-
}
683-
684-
// This doesn't actually return a right answer for NaN on purpose,
685-
// seeing as how it cannot correctly discern between a floating point NaN,
686-
// and some normal floating point numbers truncated from an x87 FPU.
687-
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
688-
const unsafe fn partial_classify(self) -> FpCategory {
689-
// SAFETY: The caller is not asking questions for which this will tell lies.
690-
let b = unsafe { mem::transmute::<f64, u64>(self) };
691-
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
692-
(0, Self::EXP_MASK) => FpCategory::Infinite,
693-
(0, 0) => FpCategory::Zero,
694-
(_, 0) => FpCategory::Subnormal,
695-
_ => FpCategory::Normal,
696-
}
697-
}
698-
699-
// This operates on bits, and only bits, so it can ignore concerns about weird FPUs.
700-
// FIXME(jubilee): In a just world, this would be the entire impl for classify,
701-
// plus a transmute. We do not live in a just world, but we can make it more so.
702-
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
703-
const fn classify_bits(b: u64) -> FpCategory {
704-
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
705-
(0, Self::EXP_MASK) => FpCategory::Infinite,
706-
(_, Self::EXP_MASK) => FpCategory::Nan,
707-
(0, 0) => FpCategory::Zero,
708-
(_, 0) => FpCategory::Subnormal,
709-
_ => FpCategory::Normal,
677+
// So, this does use bitpattern matching for the rest. On x87, due to the incorrect
678+
// float codegen on this hardware, this doesn't actually return a right answer for NaN
679+
// because it cannot correctly discern between a floating point NaN, and some normal
680+
// floating point numbers truncated from an x87 FPU -- but we took care of NaN above, so
681+
// we are fine.
682+
let b = self.to_bits();
683+
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
684+
(0, Self::EXP_MASK) => FpCategory::Infinite,
685+
(0, 0) => FpCategory::Zero,
686+
(_, 0) => FpCategory::Subnormal,
687+
_ => FpCategory::Normal,
688+
}
710689
}
711690
}
712691

@@ -1134,33 +1113,7 @@ impl f64 {
11341113
#[inline]
11351114
pub const fn to_bits(self) -> u64 {
11361115
// SAFETY: `u64` is a plain old datatype so we can always transmute to it.
1137-
// ...sorta.
1138-
//
1139-
// See the SAFETY comment in f64::from_bits for more.
1140-
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
1141-
const fn ct_f64_to_u64(ct: f64) -> u64 {
1142-
match ct.classify() {
1143-
FpCategory::Nan => {
1144-
panic!("const-eval error: cannot use f64::to_bits on a NaN")
1145-
}
1146-
FpCategory::Subnormal => {
1147-
panic!("const-eval error: cannot use f64::to_bits on a subnormal number")
1148-
}
1149-
FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
1150-
// SAFETY: We have a normal floating point number. Now we transmute, i.e. do a bitcopy.
1151-
unsafe { mem::transmute::<f64, u64>(ct) }
1152-
}
1153-
}
1154-
}
1155-
1156-
#[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
1157-
fn rt_f64_to_u64(rt: f64) -> u64 {
1158-
// SAFETY: `u64` is a plain old datatype so we can always... uh...
1159-
// ...look, just pretend you forgot what you just read.
1160-
// Stability concerns.
1161-
unsafe { mem::transmute::<f64, u64>(rt) }
1162-
}
1163-
intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64)
1116+
unsafe { mem::transmute(self) }
11641117
}
11651118

11661119
/// Raw transmutation from `u64`.
@@ -1205,58 +1158,8 @@ impl f64 {
12051158
#[inline]
12061159
pub const fn from_bits(v: u64) -> Self {
12071160
// It turns out the safety issues with sNaN were overblown! Hooray!
1208-
// SAFETY: `u64` is a plain old datatype so we can always transmute from it
1209-
// ...sorta.
1210-
//
1211-
// It turns out that at runtime, it is possible for a floating point number
1212-
// to be subject to floating point modes that alter nonzero subnormal numbers
1213-
// to zero on reads and writes, aka "denormals are zero" and "flush to zero".
1214-
// This is not a problem usually, but at least one tier2 platform for Rust
1215-
// actually exhibits an FTZ behavior by default: thumbv7neon
1216-
// aka "the Neon FPU in AArch32 state"
1217-
//
1218-
// Even with this, not all instructions exhibit the FTZ behaviors on thumbv7neon,
1219-
// so this should load the same bits if LLVM emits the "correct" instructions,
1220-
// but LLVM sometimes makes interesting choices about float optimization,
1221-
// and other FPUs may do similar. Thus, it is wise to indulge luxuriously in caution.
1222-
//
1223-
// In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled,
1224-
// i.e. not soft-float, the way Rust does parameter passing can actually alter
1225-
// a number that is "not infinity" to have the same exponent as infinity,
1226-
// in a slightly unpredictable manner.
1227-
//
1228-
// And, of course evaluating to a NaN value is fairly nondeterministic.
1229-
// More precisely: when NaN should be returned is knowable, but which NaN?
1230-
// So far that's defined by a combination of LLVM and the CPU, not Rust.
1231-
// This function, however, allows observing the bitstring of a NaN,
1232-
// thus introspection on CTFE.
1233-
//
1234-
// In order to preserve, at least for the moment, const-to-runtime equivalence,
1235-
// reject any of these possible situations from happening.
1236-
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
1237-
const fn ct_u64_to_f64(ct: u64) -> f64 {
1238-
match f64::classify_bits(ct) {
1239-
FpCategory::Subnormal => {
1240-
panic!("const-eval error: cannot use f64::from_bits on a subnormal number")
1241-
}
1242-
FpCategory::Nan => {
1243-
panic!("const-eval error: cannot use f64::from_bits on NaN")
1244-
}
1245-
FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
1246-
// SAFETY: It's not a frumious number
1247-
unsafe { mem::transmute::<u64, f64>(ct) }
1248-
}
1249-
}
1250-
}
1251-
1252-
#[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
1253-
fn rt_u64_to_f64(rt: u64) -> f64 {
1254-
// SAFETY: `u64` is a plain old datatype so we can always... uh...
1255-
// ...look, just pretend you forgot what you just read.
1256-
// Stability concerns.
1257-
unsafe { mem::transmute::<u64, f64>(rt) }
1258-
}
1259-
intrinsics::const_eval_select((v,), ct_u64_to_f64, rt_u64_to_f64)
1161+
// SAFETY: `u64` is a plain old datatype so we can always transmute from it.
1162+
unsafe { mem::transmute(v) }
12601163
}
12611164

12621165
/// Returns the memory representation of this floating point number as a byte array in

‎tests/debuginfo/pretty-huge-vec.rs‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
//@ ignore-windows failing on win32 bot
2-
//@ ignore-freebsd: gdb package too new
32
//@ ignore-android: FIXME(#10381)
43
//@ compile-flags:-g
54
//@ min-gdb-version: 8.1

‎tests/debuginfo/pretty-std-collections.rs‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
//@ ignore-windows failing on win32 bot
2-
//@ ignore-freebsd: gdb package too new
32
//@ ignore-android: FIXME(#10381)
43
//@ ignore-windows-gnu: #128981
54
//@ compile-flags:-g

‎tests/debuginfo/pretty-std.rs‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// ignore-tidy-linelength
2-
//@ ignore-freebsd: gdb package too new
32
//@ ignore-windows-gnu: #128981
43
//@ ignore-android: FIXME(#10381)
54
//@ compile-flags:-g

‎tests/debuginfo/pretty-uninitialized-vec.rs‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
//@ ignore-windows failing on win32 bot
2-
//@ ignore-freebsd: gdb package too new
32
//@ ignore-android: FIXME(#10381)
43
//@ compile-flags:-g
54
//@ min-gdb-version: 8.1

‎tests/crashes/116308.rs‎ renamed to ‎tests/ui/const-generics/adt_const_params/116308.rs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
//@ known-bug: #116308
1+
//@ check-pass
22
#![feature(adt_const_params)]
33

4+
// Regression test for #116308
5+
46
pub trait Identity {
57
type Identity;
68
}

‎tests/ui/consts/const-float-bits-conv.rs‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,17 @@ fn f32() {
3838
const_assert!(f32::from_bits(0x44a72000), 1337.0);
3939
const_assert!(f32::from_ne_bytes(0x44a72000u32.to_ne_bytes()), 1337.0);
4040
const_assert!(f32::from_bits(0xc1640000), -14.25);
41+
42+
// Check that NaNs roundtrip their bits regardless of signalingness
43+
// 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
44+
// ...actually, let's just check that these break. :D
45+
const MASKED_NAN1: u32 = f32::NAN.to_bits() ^ 0x002A_AAAA;
46+
const MASKED_NAN2: u32 = f32::NAN.to_bits() ^ 0x0055_5555;
47+
48+
const_assert!(f32::from_bits(MASKED_NAN1).is_nan());
49+
const_assert!(f32::from_bits(MASKED_NAN1).is_nan());
50+
const_assert!(f32::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
51+
const_assert!(f32::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
4152
}
4253

4354
fn f64() {
@@ -55,6 +66,17 @@ fn f64() {
5566
const_assert!(f64::from_bits(0x4094e40000000000), 1337.0);
5667
const_assert!(f64::from_ne_bytes(0x4094e40000000000u64.to_ne_bytes()), 1337.0);
5768
const_assert!(f64::from_bits(0xc02c800000000000), -14.25);
69+
70+
// Check that NaNs roundtrip their bits regardless of signalingness
71+
// 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
72+
// ...actually, let's just check that these break. :D
73+
const MASKED_NAN1: u64 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA;
74+
const MASKED_NAN2: u64 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555;
75+
76+
const_assert!(f64::from_bits(MASKED_NAN1).is_nan());
77+
const_assert!(f64::from_bits(MASKED_NAN1).is_nan());
78+
const_assert!(f64::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
79+
const_assert!(f64::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
5880
}
5981

6082
fn main() {

‎tests/ui/consts/const-float-bits-reject-conv.rs‎

Lines changed: 0 additions & 68 deletions
This file was deleted.

‎tests/ui/consts/const-float-bits-reject-conv.stderr‎

Lines changed: 0 additions & 115 deletions
This file was deleted.

0 commit comments

Comments
 (0)
This repository has been archived.