Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 14c24b1

Browse files
committedJul 15, 2024
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 f710e38 commit 14c24b1

File tree

2 files changed

+210
-32
lines changed

2 files changed

+210
-32
lines changed
 

‎core/src/num/f128.rs

+105-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

@@ -758,12 +760,52 @@ impl f128 {
758760
/// ```
759761
#[inline]
760762
#[unstable(feature = "f128", issue = "116909")]
763+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
761764
#[must_use = "this returns the result of the operation, without modifying the original"]
762-
pub fn to_bits(self) -> u128 {
763-
// SAFETY: `u128` is a plain old datatype so we can always... uh...
764-
// ...look, just pretend you forgot what you just read.
765-
// Stability concerns.
766-
unsafe { mem::transmute(self) }
765+
pub const fn to_bits(self) -> u128 {
766+
// SAFETY: `u128` is a plain old datatype so we can always transmute to it.
767+
// ...sorta.
768+
//
769+
// It turns out that at runtime, it is possible for a floating point number
770+
// to be subject to a floating point mode that alters nonzero subnormal numbers
771+
// to zero on reads and writes, aka "denormals are zero" and "flush to zero".
772+
//
773+
// And, of course evaluating to a NaN value is fairly nondeterministic.
774+
// More precisely: when NaN should be returned is knowable, but which NaN?
775+
// So far that's defined by a combination of LLVM and the CPU, not Rust.
776+
// This function, however, allows observing the bitstring of a NaN,
777+
// thus introspection on CTFE.
778+
//
779+
// In order to preserve, at least for the moment, const-to-runtime equivalence,
780+
// we reject any of these possible situations from happening.
781+
#[inline]
782+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
783+
const fn ct_f128_to_u128(ct: f128) -> u128 {
784+
// FIXME(f16_f128): we should use `.classify()` like `f32` and `f64`, but that
785+
// is not available on all platforms (needs `netf2` and `unordtf2`). So classify
786+
// the bits instead.
787+
788+
// SAFETY: this is a POD transmutation
789+
let bits = unsafe { mem::transmute::<f128, u128>(ct) };
790+
match f128::classify_bits(bits) {
791+
FpCategory::Nan => {
792+
panic!("const-eval error: cannot use f128::to_bits on a NaN")
793+
}
794+
FpCategory::Subnormal => {
795+
panic!("const-eval error: cannot use f128::to_bits on a subnormal number")
796+
}
797+
FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => bits,
798+
}
799+
}
800+
801+
#[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
802+
fn rt_f128_to_u128(x: f128) -> u128 {
803+
// SAFETY: `u128` is a plain old datatype so we can always... uh...
804+
// ...look, just pretend you forgot what you just read.
805+
// Stability concerns.
806+
unsafe { mem::transmute(x) }
807+
}
808+
intrinsics::const_eval_select((self,), ct_f128_to_u128, rt_f128_to_u128)
767809
}
768810

769811
/// Raw transmutation from `u128`.
@@ -808,11 +850,52 @@ impl f128 {
808850
#[inline]
809851
#[must_use]
810852
#[unstable(feature = "f128", issue = "116909")]
811-
pub fn from_bits(v: u128) -> Self {
812-
// SAFETY: `u128 is a plain old datatype so we can always... uh...
813-
// ...look, just pretend you forgot what you just read.
814-
// Stability concerns.
815-
unsafe { mem::transmute(v) }
853+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
854+
pub const fn from_bits(v: u128) -> Self {
855+
// It turns out the safety issues with sNaN were overblown! Hooray!
856+
// SAFETY: `u128` is a plain old datatype so we can always transmute from it
857+
// ...sorta.
858+
//
859+
// It turns out that at runtime, it is possible for a floating point number
860+
// to be subject to floating point modes that alter nonzero subnormal numbers
861+
// to zero on reads and writes, aka "denormals are zero" and "flush to zero".
862+
// This is not a problem usually, but at least one tier2 platform for Rust
863+
// actually exhibits this behavior by default: thumbv7neon
864+
// aka "the Neon FPU in AArch32 state"
865+
//
866+
// And, of course evaluating to a NaN value is fairly nondeterministic.
867+
// More precisely: when NaN should be returned is knowable, but which NaN?
868+
// So far that's defined by a combination of LLVM and the CPU, not Rust.
869+
// This function, however, allows observing the bitstring of a NaN,
870+
// thus introspection on CTFE.
871+
//
872+
// In order to preserve, at least for the moment, const-to-runtime equivalence,
873+
// reject any of these possible situations from happening.
874+
#[inline]
875+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
876+
const fn ct_u128_to_f128(ct: u128) -> f128 {
877+
match f128::classify_bits(ct) {
878+
FpCategory::Subnormal => {
879+
panic!("const-eval error: cannot use f128::from_bits on a subnormal number")
880+
}
881+
FpCategory::Nan => {
882+
panic!("const-eval error: cannot use f128::from_bits on NaN")
883+
}
884+
FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
885+
// SAFETY: It's not a frumious number
886+
unsafe { mem::transmute::<u128, f128>(ct) }
887+
}
888+
}
889+
}
890+
891+
#[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
892+
fn rt_u128_to_f128(x: u128) -> f128 {
893+
// SAFETY: `u128` is a plain old datatype so we can always... uh...
894+
// ...look, just pretend you forgot what you just read.
895+
// Stability concerns.
896+
unsafe { mem::transmute(x) }
897+
}
898+
intrinsics::const_eval_select((v,), ct_u128_to_f128, rt_u128_to_f128)
816899
}
817900

818901
/// Return the memory representation of this floating point number as a byte array in
@@ -835,8 +918,9 @@ impl f128 {
835918
/// ```
836919
#[inline]
837920
#[unstable(feature = "f128", issue = "116909")]
921+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
838922
#[must_use = "this returns the result of the operation, without modifying the original"]
839-
pub fn to_be_bytes(self) -> [u8; 16] {
923+
pub const fn to_be_bytes(self) -> [u8; 16] {
840924
self.to_bits().to_be_bytes()
841925
}
842926

@@ -860,8 +944,9 @@ impl f128 {
860944
/// ```
861945
#[inline]
862946
#[unstable(feature = "f128", issue = "116909")]
947+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
863948
#[must_use = "this returns the result of the operation, without modifying the original"]
864-
pub fn to_le_bytes(self) -> [u8; 16] {
949+
pub const fn to_le_bytes(self) -> [u8; 16] {
865950
self.to_bits().to_le_bytes()
866951
}
867952

@@ -896,8 +981,9 @@ impl f128 {
896981
/// ```
897982
#[inline]
898983
#[unstable(feature = "f128", issue = "116909")]
984+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
899985
#[must_use = "this returns the result of the operation, without modifying the original"]
900-
pub fn to_ne_bytes(self) -> [u8; 16] {
986+
pub const fn to_ne_bytes(self) -> [u8; 16] {
901987
self.to_bits().to_ne_bytes()
902988
}
903989

@@ -923,7 +1009,8 @@ impl f128 {
9231009
#[inline]
9241010
#[must_use]
9251011
#[unstable(feature = "f128", issue = "116909")]
926-
pub fn from_be_bytes(bytes: [u8; 16]) -> Self {
1012+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
1013+
pub const fn from_be_bytes(bytes: [u8; 16]) -> Self {
9271014
Self::from_bits(u128::from_be_bytes(bytes))
9281015
}
9291016

@@ -949,7 +1036,8 @@ impl f128 {
9491036
#[inline]
9501037
#[must_use]
9511038
#[unstable(feature = "f128", issue = "116909")]
952-
pub fn from_le_bytes(bytes: [u8; 16]) -> Self {
1039+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
1040+
pub const fn from_le_bytes(bytes: [u8; 16]) -> Self {
9531041
Self::from_bits(u128::from_le_bytes(bytes))
9541042
}
9551043

@@ -985,7 +1073,8 @@ impl f128 {
9851073
#[inline]
9861074
#[must_use]
9871075
#[unstable(feature = "f128", issue = "116909")]
988-
pub fn from_ne_bytes(bytes: [u8; 16]) -> Self {
1076+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
1077+
pub const fn from_ne_bytes(bytes: [u8; 16]) -> Self {
9891078
Self::from_bits(u128::from_ne_bytes(bytes))
9901079
}
9911080

‎core/src/num/f16.rs

+105-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

@@ -788,12 +790,52 @@ impl f16 {
788790
/// ```
789791
#[inline]
790792
#[unstable(feature = "f16", issue = "116909")]
793+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
791794
#[must_use = "this returns the result of the operation, without modifying the original"]
792-
pub fn to_bits(self) -> u16 {
793-
// SAFETY: `u16` is a plain old datatype so we can always... uh...
794-
// ...look, just pretend you forgot what you just read.
795-
// Stability concerns.
796-
unsafe { mem::transmute(self) }
795+
pub const fn to_bits(self) -> u16 {
796+
// SAFETY: `u16` is a plain old datatype so we can always transmute to it.
797+
// ...sorta.
798+
//
799+
// It turns out that at runtime, it is possible for a floating point number
800+
// to be subject to a floating point mode that alters nonzero subnormal numbers
801+
// to zero on reads and writes, aka "denormals are zero" and "flush to zero".
802+
//
803+
// And, of course evaluating to a NaN value is fairly nondeterministic.
804+
// More precisely: when NaN should be returned is knowable, but which NaN?
805+
// So far that's defined by a combination of LLVM and the CPU, not Rust.
806+
// This function, however, allows observing the bitstring of a NaN,
807+
// thus introspection on CTFE.
808+
//
809+
// In order to preserve, at least for the moment, const-to-runtime equivalence,
810+
// we reject any of these possible situations from happening.
811+
#[inline]
812+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
813+
const fn ct_f16_to_u16(ct: f16) -> u16 {
814+
// FIXME(f16_f128): we should use `.classify()` like `f32` and `f64`, but we don't yet
815+
// want to rely on that on all platforms because it is nondeterministic (e.g. x86 has
816+
// convention discrepancies calling intrinsics). So just classify the bits instead.
817+
818+
// SAFETY: this is a POD transmutation
819+
let bits = unsafe { mem::transmute::<f16, u16>(ct) };
820+
match f16::classify_bits(bits) {
821+
FpCategory::Nan => {
822+
panic!("const-eval error: cannot use f16::to_bits on a NaN")
823+
}
824+
FpCategory::Subnormal => {
825+
panic!("const-eval error: cannot use f16::to_bits on a subnormal number")
826+
}
827+
FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => bits,
828+
}
829+
}
830+
831+
#[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
832+
fn rt_f16_to_u16(x: f16) -> u16 {
833+
// SAFETY: `u16` is a plain old datatype so we can always... uh...
834+
// ...look, just pretend you forgot what you just read.
835+
// Stability concerns.
836+
unsafe { mem::transmute(x) }
837+
}
838+
intrinsics::const_eval_select((self,), ct_f16_to_u16, rt_f16_to_u16)
797839
}
798840

799841
/// Raw transmutation from `u16`.
@@ -837,11 +879,52 @@ impl f16 {
837879
#[inline]
838880
#[must_use]
839881
#[unstable(feature = "f16", issue = "116909")]
840-
pub fn from_bits(v: u16) -> Self {
841-
// SAFETY: `u16` is a plain old datatype so we can always... uh...
842-
// ...look, just pretend you forgot what you just read.
843-
// Stability concerns.
844-
unsafe { mem::transmute(v) }
882+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
883+
pub const fn from_bits(v: u16) -> Self {
884+
// It turns out the safety issues with sNaN were overblown! Hooray!
885+
// SAFETY: `u16` is a plain old datatype so we can always transmute from it
886+
// ...sorta.
887+
//
888+
// It turns out that at runtime, it is possible for a floating point number
889+
// to be subject to floating point modes that alter nonzero subnormal numbers
890+
// to zero on reads and writes, aka "denormals are zero" and "flush to zero".
891+
// This is not a problem usually, but at least one tier2 platform for Rust
892+
// actually exhibits this behavior by default: thumbv7neon
893+
// aka "the Neon FPU in AArch32 state"
894+
//
895+
// And, of course evaluating to a NaN value is fairly nondeterministic.
896+
// More precisely: when NaN should be returned is knowable, but which NaN?
897+
// So far that's defined by a combination of LLVM and the CPU, not Rust.
898+
// This function, however, allows observing the bitstring of a NaN,
899+
// thus introspection on CTFE.
900+
//
901+
// In order to preserve, at least for the moment, const-to-runtime equivalence,
902+
// reject any of these possible situations from happening.
903+
#[inline]
904+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
905+
const fn ct_u16_to_f16(ct: u16) -> f16 {
906+
match f16::classify_bits(ct) {
907+
FpCategory::Subnormal => {
908+
panic!("const-eval error: cannot use f16::from_bits on a subnormal number")
909+
}
910+
FpCategory::Nan => {
911+
panic!("const-eval error: cannot use f16::from_bits on NaN")
912+
}
913+
FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
914+
// SAFETY: It's not a frumious number
915+
unsafe { mem::transmute::<u16, f16>(ct) }
916+
}
917+
}
918+
}
919+
920+
#[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
921+
fn rt_u16_to_f16(x: u16) -> f16 {
922+
// SAFETY: `u16` is a plain old datatype so we can always... uh...
923+
// ...look, just pretend you forgot what you just read.
924+
// Stability concerns.
925+
unsafe { mem::transmute(x) }
926+
}
927+
intrinsics::const_eval_select((v,), ct_u16_to_f16, rt_u16_to_f16)
845928
}
846929

847930
/// Return the memory representation of this floating point number as a byte array in
@@ -860,8 +943,9 @@ impl f16 {
860943
/// ```
861944
#[inline]
862945
#[unstable(feature = "f16", issue = "116909")]
946+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
863947
#[must_use = "this returns the result of the operation, without modifying the original"]
864-
pub fn to_be_bytes(self) -> [u8; 2] {
948+
pub const fn to_be_bytes(self) -> [u8; 2] {
865949
self.to_bits().to_be_bytes()
866950
}
867951

@@ -881,8 +965,9 @@ impl f16 {
881965
/// ```
882966
#[inline]
883967
#[unstable(feature = "f16", issue = "116909")]
968+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
884969
#[must_use = "this returns the result of the operation, without modifying the original"]
885-
pub fn to_le_bytes(self) -> [u8; 2] {
970+
pub const fn to_le_bytes(self) -> [u8; 2] {
886971
self.to_bits().to_le_bytes()
887972
}
888973

@@ -915,8 +1000,9 @@ impl f16 {
9151000
/// ```
9161001
#[inline]
9171002
#[unstable(feature = "f16", issue = "116909")]
1003+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
9181004
#[must_use = "this returns the result of the operation, without modifying the original"]
919-
pub fn to_ne_bytes(self) -> [u8; 2] {
1005+
pub const fn to_ne_bytes(self) -> [u8; 2] {
9201006
self.to_bits().to_ne_bytes()
9211007
}
9221008

@@ -938,7 +1024,8 @@ impl f16 {
9381024
#[inline]
9391025
#[must_use]
9401026
#[unstable(feature = "f16", issue = "116909")]
941-
pub fn from_be_bytes(bytes: [u8; 2]) -> Self {
1027+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
1028+
pub const fn from_be_bytes(bytes: [u8; 2]) -> Self {
9421029
Self::from_bits(u16::from_be_bytes(bytes))
9431030
}
9441031

@@ -960,7 +1047,8 @@ impl f16 {
9601047
#[inline]
9611048
#[must_use]
9621049
#[unstable(feature = "f16", issue = "116909")]
963-
pub fn from_le_bytes(bytes: [u8; 2]) -> Self {
1050+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
1051+
pub const fn from_le_bytes(bytes: [u8; 2]) -> Self {
9641052
Self::from_bits(u16::from_le_bytes(bytes))
9651053
}
9661054

@@ -993,7 +1081,8 @@ impl f16 {
9931081
#[inline]
9941082
#[must_use]
9951083
#[unstable(feature = "f16", issue = "116909")]
996-
pub fn from_ne_bytes(bytes: [u8; 2]) -> Self {
1084+
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
1085+
pub const fn from_ne_bytes(bytes: [u8; 2]) -> Self {
9971086
Self::from_bits(u16::from_ne_bytes(bytes))
9981087
}
9991088

0 commit comments

Comments
 (0)
Failed to load comments.