diff --git a/library/core/src/fmt/num_buffer.rs b/library/core/src/fmt/num_buffer.rs index 7d42091b05086..e224878466916 100644 --- a/library/core/src/fmt/num_buffer.rs +++ b/library/core/src/fmt/num_buffer.rs @@ -56,6 +56,7 @@ impl_NumBufferTrait! { /// assert_eq!(n2.format_into(&mut buf), "-1972"); /// ``` #[stable(feature = "int_format_into", since = "1.98.0")] +#[repr(transparent)] pub struct NumBuffer { pub(crate) buf: T::Buf, phantom: core::marker::PhantomData, @@ -77,3 +78,44 @@ impl NumBuffer { NumBuffer { buf: T::DEFAULT, phantom: core::marker::PhantomData } } } + +impl NumBuffer { + /// Allows to cast between `NumBuffer` types at compile-time without new allocation. + /// + /// # Examples + /// + /// ``` + /// use core::fmt::NumBuffer; + /// + /// let mut buf = NumBuffer::::new(); + /// + /// assert_eq!(16u16.format_into(buf.cast_into::()), "16"); + /// assert_eq!(u16::MAX.format_into(buf.cast_into::()), u16::MAX.to_string()); + /// + /// assert_eq!(-16i16.format_into(buf.cast_into::()), "-16"); + /// assert_eq!(i16::MIN.format_into(buf.cast_into::()), i16::MIN.to_string()); + /// ``` + /// + /// If you try to cast to a `NumBuffer` with a bigger buffer size, it will not compile: + /// + /// ```compile_fail + /// use core::fmt::NumBuffer; + /// + /// let mut buf = NumBuffer::::new(); + /// // Cannot work since `i32` requires a bigger buffer (because of the `-` sign). + /// let buf = buf.cast_into::(); + /// ``` + #[unstable(feature = "fmt_internals", issue = "none")] + #[rustc_const_unstable(feature = "fmt_internals", issue = "none")] + #[track_caller] + pub const fn cast_into(&mut self) -> &mut NumBuffer { + const { + assert!( + core::mem::size_of::() >= core::mem::size_of::(), + "target `NumBuffer` size must be smaller or equal to source `NumBuffer` size" + ); + } + // SAFETY: The target `NumBuffer` buffer is not bigger so this conversion is ok. + unsafe { core::mem::transmute::<&mut NumBuffer, &mut NumBuffer>(self) } + } +} diff --git a/tests/ui/numeric/numbuffer-case.rs b/tests/ui/numeric/numbuffer-case.rs new file mode 100644 index 0000000000000..af393b832a1ee --- /dev/null +++ b/tests/ui/numeric/numbuffer-case.rs @@ -0,0 +1,16 @@ +// Test to ensure that you cannot use `cast_into` to convert a `NumBuffer` to +// a bigger buffer. + +//@ build-fail + +#![feature(fmt_internals)] + +extern crate core; + +use core::fmt::NumBuffer; + +fn main() { + let mut x = NumBuffer::::new(); + let mut y = x.cast_into::(); + //~? ERROR: target `NumBuffer` size must be smaller or equal to source `NumBuffer` size +} diff --git a/tests/ui/numeric/numbuffer-case.stderr b/tests/ui/numeric/numbuffer-case.stderr new file mode 100644 index 0000000000000..86d31f95b8540 --- /dev/null +++ b/tests/ui/numeric/numbuffer-case.stderr @@ -0,0 +1,21 @@ +error[E0080]: evaluation panicked: target `NumBuffer` size must be smaller or equal to source `NumBuffer` size + --> $SRC_DIR/core/src/panic.rs:LL:COL + | + = note: evaluation of `core::fmt::NumBuffer::::cast_into::::{constant#0}` failed here + --> $SRC_DIR/core/src/fmt/num_buffer.rs:LL:COL + ::: $SRC_DIR/core/src/fmt/num_buffer.rs:LL:COL + | + = note: in this macro invocation + +note: erroneous constant encountered + --> $SRC_DIR/core/src/fmt/num_buffer.rs:LL:COL + +note: the above error was encountered while instantiating `fn NumBuffer::::cast_into::` + --> $DIR/numbuffer-case.rs:14:17 + | +LL | let mut y = x.cast_into::(); + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`.