diff --git a/src/libcore/convert/mod.rs b/src/libcore/convert/mod.rs index 08802b3a97a0c..16d5375059fee 100644 --- a/src/libcore/convert/mod.rs +++ b/src/libcore/convert/mod.rs @@ -40,6 +40,11 @@ #![stable(feature = "rust1", since = "1.0.0")] +mod num; + +#[unstable(feature = "convert_float_to_int", issue = "67057")] +pub use num::FloatToInt; + /// The identity function. /// /// Two things are important to note about this function: diff --git a/src/libcore/convert/num.rs b/src/libcore/convert/num.rs new file mode 100644 index 0000000000000..076a1107479e7 --- /dev/null +++ b/src/libcore/convert/num.rs @@ -0,0 +1,38 @@ +mod private { + /// This trait being unreachable from outside the crate + /// prevents other implementations of the `FloatToInt` trait, + /// which allows potentially adding more trait methods after the trait is `#[stable]`. + #[unstable(feature = "convert_float_to_int", issue = "67057")] + pub trait Sealed {} +} + +/// Supporting trait for inherent methods of `f32` and `f64` such as `round_unchecked_to`. +/// Typically doesn’t need to be used directly. +#[unstable(feature = "convert_float_to_int", issue = "67057")] +pub trait FloatToInt: private::Sealed + Sized { + #[cfg(not(bootstrap))] + #[unstable(feature = "float_approx_unchecked_to", issue = "67058")] + #[doc(hidden)] + unsafe fn approx_unchecked(self) -> Int; +} + +macro_rules! impl_float_to_int { + ( $Float: ident => $( $Int: ident )+ ) => { + #[unstable(feature = "convert_float_to_int", issue = "67057")] + impl private::Sealed for $Float {} + $( + #[unstable(feature = "convert_float_to_int", issue = "67057")] + impl FloatToInt<$Int> for $Float { + #[cfg(not(bootstrap))] + #[doc(hidden)] + #[inline] + unsafe fn approx_unchecked(self) -> $Int { + crate::intrinsics::float_to_int_approx_unchecked(self) + } + } + )+ + } +} + +impl_float_to_int!(f32 => u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize); +impl_float_to_int!(f64 => u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize); diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 19928f30f2ea5..18aae59573d7d 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1144,6 +1144,11 @@ extern "rust-intrinsic" { /// May assume inputs are finite. pub fn frem_fast(a: T, b: T) -> T; + /// Convert with LLVM’s fptoui/fptosi, which may return undef for values out of range + /// https://github.com/rust-lang/rust/issues/10184 + #[cfg(not(bootstrap))] + pub fn float_to_int_approx_unchecked(value: Float) -> Int; + /// Returns the number of bits set in an integer type `T` pub fn ctpop(x: T) -> T; diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs index 913c0f96d118d..ac06f95e244b6 100644 --- a/src/libcore/num/f32.rs +++ b/src/libcore/num/f32.rs @@ -7,9 +7,10 @@ #![stable(feature = "rust1", since = "1.0.0")] +#[cfg(not(bootstrap))] +use crate::convert::FloatToInt; #[cfg(not(test))] use crate::intrinsics; - use crate::mem; use crate::num::FpCategory; @@ -400,6 +401,35 @@ impl f32 { intrinsics::minnumf32(self, other) } + /// Rounds toward zero and converts to any primitive integer type, + /// assuming that the value is finite and fits in that type. + /// + /// ``` + /// #![feature(float_approx_unchecked_to)] + /// + /// let value = 4.6_f32; + /// let rounded = unsafe { value.approx_unchecked_to::() }; + /// assert_eq!(rounded, 4); + /// + /// let value = -128.9_f32; + /// let rounded = unsafe { value.approx_unchecked_to::() }; + /// assert_eq!(rounded, std::i8::MIN); + /// ``` + /// + /// # Safety + /// + /// The value must: + /// + /// * Not be `NaN` + /// * Not be infinite + /// * Be representable in the return type `Int`, after truncating off its fractional part + #[cfg(not(bootstrap))] + #[unstable(feature = "float_approx_unchecked_to", issue = "67058")] + #[inline] + pub unsafe fn approx_unchecked_to(self) -> Int where Self: FloatToInt { + FloatToInt::::approx_unchecked(self) + } + /// Raw transmutation to `u32`. /// /// This is currently identical to `transmute::(self)` on all platforms. diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs index 6ca830b1f38af..794f77fcfc1be 100644 --- a/src/libcore/num/f64.rs +++ b/src/libcore/num/f64.rs @@ -7,9 +7,10 @@ #![stable(feature = "rust1", since = "1.0.0")] +#[cfg(not(bootstrap))] +use crate::convert::FloatToInt; #[cfg(not(test))] use crate::intrinsics; - use crate::mem; use crate::num::FpCategory; @@ -413,6 +414,35 @@ impl f64 { intrinsics::minnumf64(self, other) } + /// Rounds toward zero and converts to any primitive integer type, + /// assuming that the value is finite and fits in that type. + /// + /// ``` + /// #![feature(float_approx_unchecked_to)] + /// + /// let value = 4.6_f32; + /// let rounded = unsafe { value.approx_unchecked_to::() }; + /// assert_eq!(rounded, 4); + /// + /// let value = -128.9_f32; + /// let rounded = unsafe { value.approx_unchecked_to::() }; + /// assert_eq!(rounded, std::i8::MIN); + /// ``` + /// + /// # Safety + /// + /// The value must: + /// + /// * Not be `NaN` + /// * Not be infinite + /// * Be representable in the return type `Int`, after truncating off its fractional part + #[cfg(not(bootstrap))] + #[unstable(feature = "float_approx_unchecked_to", issue = "67058")] + #[inline] + pub unsafe fn approx_unchecked_to(self) -> Int where Self: FloatToInt { + FloatToInt::::approx_unchecked(self) + } + /// Raw transmutation to `u64`. /// /// This is currently identical to `transmute::(self)` on all platforms. diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index 9df75a800f123..1767ad118e7c0 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -516,9 +516,36 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { return; } } - }, + "float_to_int_approx_unchecked" => { + if float_type_width(arg_tys[0]).is_none() { + span_invalid_monomorphization_error( + tcx.sess, span, + &format!("invalid monomorphization of `float_to_int_approx_unchecked` \ + intrinsic: expected basic float type, \ + found `{}`", arg_tys[0])); + return; + } + match int_type_width_signed(ret_ty, self.cx) { + Some((width, signed)) => { + if signed { + self.fptosi(args[0].immediate(), self.cx.type_ix(width)) + } else { + self.fptoui(args[0].immediate(), self.cx.type_ix(width)) + } + } + None => { + span_invalid_monomorphization_error( + tcx.sess, span, + &format!("invalid monomorphization of `float_to_int_approx_unchecked` \ + intrinsic: expected basic integer type, \ + found `{}`", ret_ty)); + return; + } + } + } + "discriminant_value" => { args[0].deref(self.cx()).codegen_get_discr(self, ret_ty) } diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index 9f034e65b6eaa..b967c6e36e35e 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -336,6 +336,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem) { (1, vec![param(0), param(0)], param(0)), "fadd_fast" | "fsub_fast" | "fmul_fast" | "fdiv_fast" | "frem_fast" => (1, vec![param(0), param(0)], param(0)), + "float_to_int_approx_unchecked" => (2, vec![ param(0) ], param(1)), "assume" => (0, vec![tcx.types.bool], tcx.mk_unit()), "likely" => (0, vec![tcx.types.bool], tcx.types.bool),