Skip to content

Commit d095e37

Browse files
committed
Mark some f16 and f128 functions unstably const
These constifications were blocked on classification functions being added. Now that those methods are available, constify them. This brings things more in line with `f32` and `f64`.
1 parent 92f0b11 commit d095e37

File tree

2 files changed

+232
-32
lines changed

2 files changed

+232
-32
lines changed

Diff for: library/core/src/num/f128.rs

+116-16
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#![unstable(feature = "f128", issue = "116909")]
1313

1414
use crate::convert::FloatToInt;
15+
#[cfg(not(test))]
16+
use crate::intrinsics;
1517
use crate::mem;
1618
use crate::num::FpCategory;
1719

@@ -806,12 +808,59 @@ impl f128 {
806808
/// ```
807809
#[inline]
808810
#[unstable(feature = "f128", issue = "116909")]
811+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
809812
#[must_use = "this returns the result of the operation, without modifying the original"]
810-
pub fn to_bits(self) -> u128 {
811-
// SAFETY: `u128` is a plain old datatype so we can always... uh...
812-
// ...look, just pretend you forgot what you just read.
813-
// Stability concerns.
814-
unsafe { mem::transmute(self) }
813+
pub const fn to_bits(self) -> u128 {
814+
// SAFETY: `u128` is a plain old datatype so we can always transmute to it.
815+
// ...sorta.
816+
//
817+
// It turns out that at runtime, it is possible for a floating point number
818+
// to be subject to a floating point mode that alters nonzero subnormal numbers
819+
// to zero on reads and writes, aka "denormals are zero" and "flush to zero".
820+
// This is not a problem per se, but at least one tier2 platform for Rust
821+
// actually exhibits this behavior by default.
822+
//
823+
// In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled,
824+
// i.e. not soft-float, the way Rust does parameter passing can actually alter
825+
// a number that is "not infinity" to have the same exponent as infinity,
826+
// in a slightly unpredictable manner.
827+
//
828+
// And, of course evaluating to a NaN value is fairly nondeterministic.
829+
// More precisely: when NaN should be returned is knowable, but which NaN?
830+
// So far that's defined by a combination of LLVM and the CPU, not Rust.
831+
// This function, however, allows observing the bitstring of a NaN,
832+
// thus introspection on CTFE.
833+
//
834+
// In order to preserve, at least for the moment, const-to-runtime equivalence,
835+
// we reject any of these possible situations from happening.
836+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
837+
const fn ct_f128_to_u128(ct: f128) -> u128 {
838+
// FIXME(f16_f128): we should use `.classify()` like `f32` and `f64`, but that
839+
// is not avaialble on all platforms (needs `netf2` and `unordtf2`). So classify
840+
// the bits instead.
841+
842+
// SAFETY: this direction is a POD transmutation. We just can't return it unless
843+
// it is normal, infinite, or zero.
844+
let bits = unsafe { mem::transmute::<f128, u128>(ct) };
845+
match f128::classify_bits(bits) {
846+
FpCategory::Nan => {
847+
panic!("const-eval error: cannot use f128::to_bits on a NaN")
848+
}
849+
FpCategory::Subnormal => {
850+
panic!("const-eval error: cannot use f128::to_bits on a subnormal number")
851+
}
852+
FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => bits,
853+
}
854+
}
855+
856+
#[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
857+
fn rt_f128_to_u128(x: f128) -> u128 {
858+
// SAFETY: `u128` is a plain old datatype so we can always... uh...
859+
// ...look, just pretend you forgot what you just read.
860+
// Stability concerns.
861+
unsafe { mem::transmute(x) }
862+
}
863+
intrinsics::const_eval_select((self,), ct_f128_to_u128, rt_f128_to_u128)
815864
}
816865

817866
/// Raw transmutation from `u128`.
@@ -856,11 +905,56 @@ impl f128 {
856905
#[inline]
857906
#[must_use]
858907
#[unstable(feature = "f128", issue = "116909")]
859-
pub fn from_bits(v: u128) -> Self {
860-
// SAFETY: `u128 is a plain old datatype so we can always... uh...
861-
// ...look, just pretend you forgot what you just read.
862-
// Stability concerns.
863-
unsafe { mem::transmute(v) }
908+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
909+
pub const fn from_bits(v: u128) -> Self {
910+
// It turns out the safety issues with sNaN were overblown! Hooray!
911+
// SAFETY: `u128` is a plain old datatype so we can always transmute from it
912+
// ...sorta.
913+
//
914+
// It turns out that at runtime, it is possible for a floating point number
915+
// to be subject to floating point modes that alter nonzero subnormal numbers
916+
// to zero on reads and writes, aka "denormals are zero" and "flush to zero".
917+
// This is not a problem usually, but at least one tier2 platform for Rust
918+
// actually exhibits this behavior by default: thumbv7neon
919+
// aka "the Neon FPU in AArch32 state"
920+
//
921+
// In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled,
922+
// i.e. not soft-float, the way Rust does parameter passing can actually alter
923+
// a number that is "not infinity" to have the same exponent as infinity,
924+
// in a slightly unpredictable manner.
925+
//
926+
// And, of course evaluating to a NaN value is fairly nondeterministic.
927+
// More precisely: when NaN should be returned is knowable, but which NaN?
928+
// So far that's defined by a combination of LLVM and the CPU, not Rust.
929+
// This function, however, allows observing the bitstring of a NaN,
930+
// thus introspection on CTFE.
931+
//
932+
// In order to preserve, at least for the moment, const-to-runtime equivalence,
933+
// reject any of these possible situations from happening.
934+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
935+
const fn ct_u128_to_f128(ct: u128) -> f128 {
936+
match f128::classify_bits(ct) {
937+
FpCategory::Subnormal => {
938+
panic!("const-eval error: cannot use f128::from_bits on a subnormal number")
939+
}
940+
FpCategory::Nan => {
941+
panic!("const-eval error: cannot use f128::from_bits on NaN")
942+
}
943+
FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
944+
// SAFETY: It's not a frumious number
945+
unsafe { mem::transmute::<u128, f128>(ct) }
946+
}
947+
}
948+
}
949+
950+
#[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
951+
fn rt_u128_to_f128(x: u128) -> f128 {
952+
// SAFETY: `u128` is a plain old datatype so we can always... uh...
953+
// ...look, just pretend you forgot what you just read.
954+
// Stability concerns.
955+
unsafe { mem::transmute(x) }
956+
}
957+
intrinsics::const_eval_select((v,), ct_u128_to_f128, rt_u128_to_f128)
864958
}
865959

866960
/// Return the memory representation of this floating point number as a byte array in
@@ -883,8 +977,9 @@ impl f128 {
883977
/// ```
884978
#[inline]
885979
#[unstable(feature = "f128", issue = "116909")]
980+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
886981
#[must_use = "this returns the result of the operation, without modifying the original"]
887-
pub fn to_be_bytes(self) -> [u8; 16] {
982+
pub const fn to_be_bytes(self) -> [u8; 16] {
888983
self.to_bits().to_be_bytes()
889984
}
890985

@@ -908,8 +1003,9 @@ impl f128 {
9081003
/// ```
9091004
#[inline]
9101005
#[unstable(feature = "f128", issue = "116909")]
1006+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
9111007
#[must_use = "this returns the result of the operation, without modifying the original"]
912-
pub fn to_le_bytes(self) -> [u8; 16] {
1008+
pub const fn to_le_bytes(self) -> [u8; 16] {
9131009
self.to_bits().to_le_bytes()
9141010
}
9151011

@@ -944,8 +1040,9 @@ impl f128 {
9441040
/// ```
9451041
#[inline]
9461042
#[unstable(feature = "f128", issue = "116909")]
1043+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
9471044
#[must_use = "this returns the result of the operation, without modifying the original"]
948-
pub fn to_ne_bytes(self) -> [u8; 16] {
1045+
pub const fn to_ne_bytes(self) -> [u8; 16] {
9491046
self.to_bits().to_ne_bytes()
9501047
}
9511048

@@ -971,7 +1068,8 @@ impl f128 {
9711068
#[inline]
9721069
#[must_use]
9731070
#[unstable(feature = "f128", issue = "116909")]
974-
pub fn from_be_bytes(bytes: [u8; 16]) -> Self {
1071+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
1072+
pub const fn from_be_bytes(bytes: [u8; 16]) -> Self {
9751073
Self::from_bits(u128::from_be_bytes(bytes))
9761074
}
9771075

@@ -997,7 +1095,8 @@ impl f128 {
9971095
#[inline]
9981096
#[must_use]
9991097
#[unstable(feature = "f128", issue = "116909")]
1000-
pub fn from_le_bytes(bytes: [u8; 16]) -> Self {
1098+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
1099+
pub const fn from_le_bytes(bytes: [u8; 16]) -> Self {
10011100
Self::from_bits(u128::from_le_bytes(bytes))
10021101
}
10031102

@@ -1033,7 +1132,8 @@ impl f128 {
10331132
#[inline]
10341133
#[must_use]
10351134
#[unstable(feature = "f128", issue = "116909")]
1036-
pub fn from_ne_bytes(bytes: [u8; 16]) -> Self {
1135+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
1136+
pub const fn from_ne_bytes(bytes: [u8; 16]) -> Self {
10371137
Self::from_bits(u128::from_ne_bytes(bytes))
10381138
}
10391139

Diff for: library/core/src/num/f16.rs

+116-16
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#![unstable(feature = "f16", issue = "116909")]
1313

1414
use crate::convert::FloatToInt;
15+
#[cfg(not(test))]
16+
use crate::intrinsics;
1517
use crate::mem;
1618
use crate::num::FpCategory;
1719

@@ -791,12 +793,59 @@ impl f16 {
791793
/// ```
792794
#[inline]
793795
#[unstable(feature = "f16", issue = "116909")]
796+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
794797
#[must_use = "this returns the result of the operation, without modifying the original"]
795-
pub fn to_bits(self) -> u16 {
796-
// SAFETY: `u16` is a plain old datatype so we can always... uh...
797-
// ...look, just pretend you forgot what you just read.
798-
// Stability concerns.
799-
unsafe { mem::transmute(self) }
798+
pub const fn to_bits(self) -> u16 {
799+
// SAFETY: `u16` is a plain old datatype so we can always transmute to it.
800+
// ...sorta.
801+
//
802+
// It turns out that at runtime, it is possible for a floating point number
803+
// to be subject to a floating point mode that alters nonzero subnormal numbers
804+
// to zero on reads and writes, aka "denormals are zero" and "flush to zero".
805+
// This is not a problem per se, but at least one tier2 platform for Rust
806+
// actually exhibits this behavior by default.
807+
//
808+
// In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled,
809+
// i.e. not soft-float, the way Rust does parameter passing can actually alter
810+
// a number that is "not infinity" to have the same exponent as infinity,
811+
// in a slightly unpredictable manner.
812+
//
813+
// And, of course evaluating to a NaN value is fairly nondeterministic.
814+
// More precisely: when NaN should be returned is knowable, but which NaN?
815+
// So far that's defined by a combination of LLVM and the CPU, not Rust.
816+
// This function, however, allows observing the bitstring of a NaN,
817+
// thus introspection on CTFE.
818+
//
819+
// In order to preserve, at least for the moment, const-to-runtime equivalence,
820+
// we reject any of these possible situations from happening.
821+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
822+
const fn ct_f16_to_u16(ct: f16) -> u16 {
823+
// FIXME(f16_f128): we should use `.classify()` like `f32` and `f64`, but we don't
824+
// yet want to rely on that on all platforms (ABI issues). So just classify the
825+
// bits instead.
826+
827+
// SAFETY: this direction is a POD transmutation. We just can't return it unless
828+
// it is normal, infinite, or zero.
829+
let bits = unsafe { mem::transmute::<f16, u16>(ct) };
830+
match f16::classify_bits(bits) {
831+
FpCategory::Nan => {
832+
panic!("const-eval error: cannot use f16::to_bits on a NaN")
833+
}
834+
FpCategory::Subnormal => {
835+
panic!("const-eval error: cannot use f16::to_bits on a subnormal number")
836+
}
837+
FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => bits,
838+
}
839+
}
840+
841+
#[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
842+
fn rt_f16_to_u16(x: f16) -> u16 {
843+
// SAFETY: `u16` is a plain old datatype so we can always... uh...
844+
// ...look, just pretend you forgot what you just read.
845+
// Stability concerns.
846+
unsafe { mem::transmute(x) }
847+
}
848+
intrinsics::const_eval_select((self,), ct_f16_to_u16, rt_f16_to_u16)
800849
}
801850

802851
/// Raw transmutation from `u16`.
@@ -840,11 +889,56 @@ impl f16 {
840889
#[inline]
841890
#[must_use]
842891
#[unstable(feature = "f16", issue = "116909")]
843-
pub fn from_bits(v: u16) -> Self {
844-
// SAFETY: `u16` is a plain old datatype so we can always... uh...
845-
// ...look, just pretend you forgot what you just read.
846-
// Stability concerns.
847-
unsafe { mem::transmute(v) }
892+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
893+
pub const fn from_bits(v: u16) -> Self {
894+
// It turns out the safety issues with sNaN were overblown! Hooray!
895+
// SAFETY: `u16` is a plain old datatype so we can always transmute from it
896+
// ...sorta.
897+
//
898+
// It turns out that at runtime, it is possible for a floating point number
899+
// to be subject to floating point modes that alter nonzero subnormal numbers
900+
// to zero on reads and writes, aka "denormals are zero" and "flush to zero".
901+
// This is not a problem usually, but at least one tier2 platform for Rust
902+
// actually exhibits this behavior by default: thumbv7neon
903+
// aka "the Neon FPU in AArch32 state"
904+
//
905+
// In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled,
906+
// i.e. not soft-float, the way Rust does parameter passing can actually alter
907+
// a number that is "not infinity" to have the same exponent as infinity,
908+
// in a slightly unpredictable manner.
909+
//
910+
// And, of course evaluating to a NaN value is fairly nondeterministic.
911+
// More precisely: when NaN should be returned is knowable, but which NaN?
912+
// So far that's defined by a combination of LLVM and the CPU, not Rust.
913+
// This function, however, allows observing the bitstring of a NaN,
914+
// thus introspection on CTFE.
915+
//
916+
// In order to preserve, at least for the moment, const-to-runtime equivalence,
917+
// reject any of these possible situations from happening.
918+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
919+
const fn ct_u16_to_f16(ct: u16) -> f16 {
920+
match f16::classify_bits(ct) {
921+
FpCategory::Subnormal => {
922+
panic!("const-eval error: cannot use f16::from_bits on a subnormal number")
923+
}
924+
FpCategory::Nan => {
925+
panic!("const-eval error: cannot use f16::from_bits on NaN")
926+
}
927+
FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
928+
// SAFETY: It's not a frumious number
929+
unsafe { mem::transmute::<u16, f16>(ct) }
930+
}
931+
}
932+
}
933+
934+
#[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
935+
fn rt_u16_to_f16(x: u16) -> f16 {
936+
// SAFETY: `u16` is a plain old datatype so we can always... uh...
937+
// ...look, just pretend you forgot what you just read.
938+
// Stability concerns.
939+
unsafe { mem::transmute(x) }
940+
}
941+
intrinsics::const_eval_select((v,), ct_u16_to_f16, rt_u16_to_f16)
848942
}
849943

850944
/// Return the memory representation of this floating point number as a byte array in
@@ -863,8 +957,9 @@ impl f16 {
863957
/// ```
864958
#[inline]
865959
#[unstable(feature = "f16", issue = "116909")]
960+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
866961
#[must_use = "this returns the result of the operation, without modifying the original"]
867-
pub fn to_be_bytes(self) -> [u8; 2] {
962+
pub const fn to_be_bytes(self) -> [u8; 2] {
868963
self.to_bits().to_be_bytes()
869964
}
870965

@@ -884,8 +979,9 @@ impl f16 {
884979
/// ```
885980
#[inline]
886981
#[unstable(feature = "f16", issue = "116909")]
982+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
887983
#[must_use = "this returns the result of the operation, without modifying the original"]
888-
pub fn to_le_bytes(self) -> [u8; 2] {
984+
pub const fn to_le_bytes(self) -> [u8; 2] {
889985
self.to_bits().to_le_bytes()
890986
}
891987

@@ -918,8 +1014,9 @@ impl f16 {
9181014
/// ```
9191015
#[inline]
9201016
#[unstable(feature = "f16", issue = "116909")]
1017+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
9211018
#[must_use = "this returns the result of the operation, without modifying the original"]
922-
pub fn to_ne_bytes(self) -> [u8; 2] {
1019+
pub const fn to_ne_bytes(self) -> [u8; 2] {
9231020
self.to_bits().to_ne_bytes()
9241021
}
9251022

@@ -941,7 +1038,8 @@ impl f16 {
9411038
#[inline]
9421039
#[must_use]
9431040
#[unstable(feature = "f16", issue = "116909")]
944-
pub fn from_be_bytes(bytes: [u8; 2]) -> Self {
1041+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
1042+
pub const fn from_be_bytes(bytes: [u8; 2]) -> Self {
9451043
Self::from_bits(u16::from_be_bytes(bytes))
9461044
}
9471045

@@ -963,7 +1061,8 @@ impl f16 {
9631061
#[inline]
9641062
#[must_use]
9651063
#[unstable(feature = "f16", issue = "116909")]
966-
pub fn from_le_bytes(bytes: [u8; 2]) -> Self {
1064+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
1065+
pub const fn from_le_bytes(bytes: [u8; 2]) -> Self {
9671066
Self::from_bits(u16::from_le_bytes(bytes))
9681067
}
9691068

@@ -996,7 +1095,8 @@ impl f16 {
9961095
#[inline]
9971096
#[must_use]
9981097
#[unstable(feature = "f16", issue = "116909")]
999-
pub fn from_ne_bytes(bytes: [u8; 2]) -> Self {
1098+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
1099+
pub const fn from_ne_bytes(bytes: [u8; 2]) -> Self {
10001100
Self::from_bits(u16::from_ne_bytes(bytes))
10011101
}
10021102

0 commit comments

Comments
 (0)