From 65a3d91d085efe37ec768770f35299cdf063e3a6 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 11 Oct 2025 20:52:29 -0400 Subject: [PATCH 1/9] Report arguments from assert_unsafe_precondition --- library/core/src/alloc/layout.rs | 2 +- library/core/src/ascii/ascii_char.rs | 2 +- library/core/src/char/convert.rs | 2 +- library/core/src/char/methods.rs | 4 +- library/core/src/displaywrapper.rs | 144 ++++++++++++++++++ library/core/src/fmt/mod.rs | 1 + library/core/src/intrinsics/mod.rs | 2 +- library/core/src/lib.rs | 3 + library/core/src/num/mod.rs | 2 + library/core/src/num/nonzero.rs | 2 + library/core/src/ops/index_range.rs | 3 +- library/core/src/ptr/alignment.rs | 3 +- library/core/src/ptr/const_ptr.rs | 12 +- library/core/src/ptr/mod.rs | 27 ++-- library/core/src/ptr/mut_ptr.rs | 9 +- library/core/src/ptr/non_null.rs | 2 +- library/core/src/slice/index.rs | 24 +-- library/core/src/slice/mod.rs | 15 +- library/core/src/slice/raw.rs | 6 +- library/core/src/str/mod.rs | 4 +- library/core/src/str/traits.rs | 12 +- library/core/src/ub_checks.rs | 19 ++- .../const-eval/ub-slice-get-unchecked.stderr | 12 +- tests/ui/precondition-checks/alignment.rs | 2 +- .../ascii-char-digit_unchecked.rs | 2 +- .../precondition-checks/assert_unchecked.rs | 2 +- .../char-from_u32_unchecked.rs | 2 +- .../copy-nonoverlapping.rs | 2 +- tests/ui/precondition-checks/copy.rs | 2 +- tests/ui/precondition-checks/layout.rs | 2 +- tests/ui/precondition-checks/nonnull.rs | 2 +- .../nonzero-from_mut_unchecked.rs | 2 +- .../nonzero-new_unchecked.rs | 2 +- tests/ui/precondition-checks/read.rs | 2 +- tests/ui/precondition-checks/read_volatile.rs | 2 +- tests/ui/precondition-checks/replace.rs | 2 +- .../slice-from-raw-parts-mut.rs | 2 +- .../slice-from-raw-parts.rs | 2 +- .../slice-get_unchecked.rs | 2 +- .../slice-get_unchecked_mut.rs | 2 +- .../precondition-checks/str-get_unchecked.rs | 2 +- .../str-get_unchecked_mut.rs | 2 +- .../swap-nonoverlapping.rs | 2 +- tests/ui/precondition-checks/unchecked_add.rs | 2 +- tests/ui/precondition-checks/unchecked_mul.rs | 2 +- tests/ui/precondition-checks/unchecked_shl.rs | 2 +- tests/ui/precondition-checks/unchecked_shr.rs | 2 +- tests/ui/precondition-checks/unchecked_sub.rs | 2 +- .../unreachable_unchecked.rs | 2 +- .../ui/precondition-checks/vec-from-parts.rs | 2 +- .../precondition-checks/vec-from-raw-parts.rs | 2 +- tests/ui/precondition-checks/vec-set-len.rs | 2 +- tests/ui/precondition-checks/write.rs | 2 +- tests/ui/precondition-checks/write_bytes.rs | 2 +- .../ui/precondition-checks/write_volatile.rs | 2 +- 55 files changed, 289 insertions(+), 87 deletions(-) create mode 100644 library/core/src/displaywrapper.rs diff --git a/library/core/src/alloc/layout.rs b/library/core/src/alloc/layout.rs index cd5fd77f86597..3cfdfc8b7d5ea 100644 --- a/library/core/src/alloc/layout.rs +++ b/library/core/src/alloc/layout.rs @@ -131,7 +131,7 @@ impl Layout { assert_unsafe_precondition!( check_library_ub, "Layout::from_size_align_unchecked requires that align is a power of 2 \ - and the rounded-up allocation size does not exceed isize::MAX", + and the rounded-up allocation size does not exceed isize::MAX (size:{size}, align:{align})", ( size: usize = size, align: usize = align, diff --git a/library/core/src/ascii/ascii_char.rs b/library/core/src/ascii/ascii_char.rs index d77fafed2039b..af32aa3a5e4b5 100644 --- a/library/core/src/ascii/ascii_char.rs +++ b/library/core/src/ascii/ascii_char.rs @@ -516,7 +516,7 @@ impl AsciiChar { pub const unsafe fn digit_unchecked(d: u8) -> Self { assert_unsafe_precondition!( check_library_ub, - "`ascii::Char::digit_unchecked` input cannot exceed 9.", + "`ascii::Char::digit_unchecked` input cannot exceed 9. (d:{d})", (d: u8 = d) => d < 10 ); diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs index 6380f42d320c6..45b33f4813471 100644 --- a/library/core/src/char/convert.rs +++ b/library/core/src/char/convert.rs @@ -28,7 +28,7 @@ pub(super) const unsafe fn from_u32_unchecked(i: u32) -> char { unsafe { assert_unsafe_precondition!( check_language_ub, - "invalid value for `char`", + "invalid value for `char` ({i})", (i: u32 = i) => char_try_from_u32(i).is_ok() ); transmute(i) diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 76f54db287079..d33ee30ac4b52 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -4,7 +4,7 @@ use super::*; use crate::panic::const_panic; use crate::slice; use crate::str::from_utf8_unchecked_mut; -use crate::ub_checks::assert_unsafe_precondition; +//use crate::ub_checks::assert_unsafe_precondition; use crate::unicode::printable::is_printable; use crate::unicode::{self, conversions}; @@ -1258,11 +1258,13 @@ impl char { #[unstable(feature = "ascii_char", issue = "110998")] #[inline] pub const unsafe fn as_ascii_unchecked(&self) -> ascii::Char { + /* assert_unsafe_precondition!( check_library_ub, "as_ascii_unchecked requires that the char is valid ASCII", (it: &char = self) => it.is_ascii() ); + */ // SAFETY: the caller promised that this char is ASCII. unsafe { ascii::Char::from_u8_unchecked(*self as u8) } diff --git a/library/core/src/displaywrapper.rs b/library/core/src/displaywrapper.rs new file mode 100644 index 0000000000000..8f2552645728f --- /dev/null +++ b/library/core/src/displaywrapper.rs @@ -0,0 +1,144 @@ +use core::fmt::{Display, Formatter, Result}; + +#[allow(missing_debug_implementations)] +#[unstable(feature = "ub_checks", issue = "none")] +pub struct DisplayWrapper(#[unstable(feature = "ub_checks", issue = "none")] pub T); + +trait Displayable: Sized + Clone + Copy { + const IS_POINTER: bool; + const SIGNED: bool; + #[inline] + fn addr(self) -> usize { + unimplemented!() + } + #[inline] + fn as_u128(self) -> u128 { + unimplemented!() + } + #[inline] + fn as_i128(self) -> i128 { + unimplemented!() + } +} + +impl Displayable for *const T { + const IS_POINTER: bool = true; + const SIGNED: bool = false; + #[inline] + fn addr(self) -> usize { + self.addr() + } +} +impl Displayable for *mut T { + const IS_POINTER: bool = true; + const SIGNED: bool = false; + #[inline] + fn addr(self) -> usize { + self.addr() + } +} +impl Displayable for u8 { + const IS_POINTER: bool = false; + const SIGNED: bool = false; + #[inline] + fn as_u128(self) -> u128 { + self as u128 + } +} +impl Displayable for u32 { + const IS_POINTER: bool = false; + const SIGNED: bool = false; + #[inline] + fn as_u128(self) -> u128 { + self as u128 + } +} +impl Displayable for u64 { + const IS_POINTER: bool = false; + const SIGNED: bool = false; + #[inline] + fn as_u128(self) -> u128 { + self as u128 + } +} +impl Displayable for usize { + const IS_POINTER: bool = false; + const SIGNED: bool = false; + #[inline] + fn as_u128(self) -> u128 { + self as u128 + } +} +impl Displayable for u128 { + const IS_POINTER: bool = false; + const SIGNED: bool = false; + #[inline] + fn as_u128(self) -> u128 { + self + } +} + +impl Displayable for isize { + const IS_POINTER: bool = false; + const SIGNED: bool = true; + #[inline] + fn as_i128(self) -> i128 { + self as i128 + } +} +impl Displayable for i128 { + const IS_POINTER: bool = false; + const SIGNED: bool = true; + #[inline] + fn as_i128(self) -> i128 { + self + } +} +#[unstable(feature = "ub_checks", issue = "none")] +impl Display for DisplayWrapper { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + const HEX: [u8; 16] = *b"0123456789abcdef"; + let mut buf = [0u8; 42]; + let mut cur = buf.len(); + if T::IS_POINTER { + let mut n = self.0.addr(); + while n >= 16 { + let d = n % 16; + n /= 16; + cur -= 1; + buf[cur] = HEX[d]; + } + cur -= 1; + buf[cur] = HEX[n]; + + cur -= 1; + buf[cur] = b'x'; + cur -= 1; + buf[cur] = b'0'; + } else { + let mut is_negative = false; + let mut n = if T::SIGNED { + let signed = self.0.as_i128(); + is_negative = signed < 0; + (!(signed as u128)).wrapping_add(1) + } else { + self.0.as_u128() + }; + while n >= 10 { + let d = n % 10; + n /= 10; + cur -= 1; + buf[cur] = (d as u8) + b'0'; + } + cur -= 1; + buf[cur] = (n as u8) + b'0'; + if is_negative { + cur -= 1; + buf[cur] = b'-'; + } + } + let s = unsafe { core::str::from_utf8_unchecked(&buf[cur..]) }; + f.write_str(s) + } +} diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 0f255e57fe585..e98c6f8170e24 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -1883,6 +1883,7 @@ impl<'a> Formatter<'a> { /// assert_eq!(format!("{Foo:0>8}"), "Foo"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn write_str(&mut self, data: &str) -> Result { self.buf.write_str(data) } diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index cef700be9ea1f..26b3c1b90fdfd 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2388,7 +2388,7 @@ where /// marked as `#[inline]`. /// /// See [`const_eval_select()`] for the rules and requirements around that intrinsic. -pub(crate) macro const_eval_select { +pub macro const_eval_select { ( @capture$([$($binders:tt)*])? { $($arg:ident : $ty:ty = $val:expr),* $(,)? } $( -> $ret:ty )? : if const diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 54adf97f10020..6dbd2f3993ae7 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -350,6 +350,9 @@ mod bool; mod escape; mod tuple; mod unit; +#[doc(hidden)] +#[unstable(feature = "ub_checks", issue = "none")] +pub mod displaywrapper; #[stable(feature = "core_primitive", since = "1.43.0")] pub mod primitive; diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index c75ee11d15efe..f52618af2367a 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -506,11 +506,13 @@ impl u8 { #[unstable(feature = "ascii_char", issue = "110998")] #[inline] pub const unsafe fn as_ascii_unchecked(&self) -> ascii::Char { + /* assert_unsafe_precondition!( check_library_ub, "as_ascii_unchecked requires that the byte is valid ASCII", (it: &u8 = self) => it.is_ascii() ); + */ // SAFETY: the caller promised that this byte is ASCII. unsafe { ascii::Char::from_u8_unchecked(*self) } diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index d9184e3c9c229..3c7dde0960d90 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -420,6 +420,7 @@ where ub_checks::assert_unsafe_precondition!( check_language_ub, "NonZero::new_unchecked requires the argument to be non-zero", + // FIXME: Can't print n here because of how the check is written () => false, ); intrinsics::unreachable() @@ -461,6 +462,7 @@ where ub_checks::assert_unsafe_precondition!( check_library_ub, "NonZero::from_mut_unchecked requires the argument to dereference as non-zero", + // FIXME: Can't print n here because of how the check is written () => false, ); intrinsics::unreachable() diff --git a/library/core/src/ops/index_range.rs b/library/core/src/ops/index_range.rs index 507fa9460bea6..1f9e21297ccc1 100644 --- a/library/core/src/ops/index_range.rs +++ b/library/core/src/ops/index_range.rs @@ -23,7 +23,8 @@ impl IndexRange { pub(crate) const unsafe fn new_unchecked(start: usize, end: usize) -> Self { ub_checks::assert_unsafe_precondition!( check_library_ub, - "IndexRange::new_unchecked requires `start <= end`", + "IndexRange::new_unchecked requires `start <= end` \ + (start:{start}, end:{end})", (start: usize = start, end: usize = end) => start <= end, ); IndexRange { start, end } diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs index bc7d3a1de7151..670dc2bceef25 100644 --- a/library/core/src/ptr/alignment.rs +++ b/library/core/src/ptr/alignment.rs @@ -79,7 +79,8 @@ impl Alignment { pub const unsafe fn new_unchecked(align: usize) -> Self { assert_unsafe_precondition!( check_language_ub, - "Alignment::new_unchecked requires a power of two", + "Alignment::new_unchecked requires a power of two \ + (align:{align})", (align: usize = align) => align.is_power_of_two() ); diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 451092709443b..fa24d854b92ca 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -372,7 +372,8 @@ impl *const T { ub_checks::assert_unsafe_precondition!( check_language_ub, - "ptr::offset requires the address calculation to not overflow", + "ptr::offset requires the address calculation to not overflow \ + (ptr:{this}, count:{count}, size:{size})", ( this: *const () = self as *const (), count: isize = count, @@ -716,7 +717,8 @@ impl *const T { ub_checks::assert_unsafe_precondition!( check_language_ub, - "ptr::offset_from_unsigned requires `self >= origin`", + "ptr::offset_from_unsigned requires `self >= origin` \ + (self:{this}, origin:{origin})", ( this: *const () = self as *const (), origin: *const () = origin as *const (), @@ -851,7 +853,8 @@ impl *const T { #[cfg(debug_assertions)] // Expensive, and doesn't catch much in the wild. ub_checks::assert_unsafe_precondition!( check_language_ub, - "ptr::add requires that the address calculation does not overflow", + "ptr::add requires that the address calculation does not overflow \ + (self:{this}, count:{count}, size:{size})", ( this: *const () = self as *const (), count: usize = count, @@ -956,7 +959,8 @@ impl *const T { #[cfg(debug_assertions)] // Expensive, and doesn't catch much in the wild. ub_checks::assert_unsafe_precondition!( check_language_ub, - "ptr::sub requires that the address calculation does not overflow", + "ptr::sub requires that the address calculation does not overflow \ + (self:{this}, count:{count}, size:{size})", ( this: *const () = self as *const (), count: usize = count, diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index b29d267654252..afefd5092687b 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -527,7 +527,8 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \ - and the specified memory ranges do not overlap", + and the specified memory ranges do not overlap \ + (src{src}, dst:{dst}, size:{size}, align:{align}, count:{count})", ( src: *const () = src as *const (), dst: *mut () = dst as *mut (), @@ -625,7 +626,8 @@ pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { unsafe { ub_checks::assert_unsafe_precondition!( check_language_ub, - "ptr::copy requires that both pointer arguments are aligned and non-null", + "ptr::copy requires that both pointer arguments are aligned and non-null \ + (src{src}, dst:{dst}, align:{align})", ( src: *const () = src as *const (), dst: *mut () = dst as *mut (), @@ -699,7 +701,8 @@ pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize) { unsafe { ub_checks::assert_unsafe_precondition!( check_language_ub, - "ptr::write_bytes requires that the destination pointer is aligned and non-null", + "ptr::write_bytes requires that the destination pointer is aligned and non-null \ + (dst:{addr}, align:{align})", ( addr: *const () = dst as *const (), align: usize = align_of::(), @@ -1392,7 +1395,8 @@ pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { ub_checks::assert_unsafe_precondition!( check_library_ub, "ptr::swap_nonoverlapping requires that both pointer arguments are aligned and non-null \ - and the specified memory ranges do not overlap", + and the specified memory ranges do not overlap \ + (x:{x}, y:{y}, size:{size}, align:{align}, count:{count})", ( x: *mut () = x as *mut (), y: *mut () = y as *mut (), @@ -1577,7 +1581,8 @@ pub const unsafe fn replace(dst: *mut T, src: T) -> T { unsafe { ub_checks::assert_unsafe_precondition!( check_language_ub, - "ptr::replace requires that the pointer argument is aligned and non-null", + "ptr::replace requires that the pointer argument is aligned and non-null\ + (dst:{addr}, (align:{align}))", ( addr: *const () = dst as *const (), align: usize = align_of::(), @@ -1730,7 +1735,8 @@ pub const unsafe fn read(src: *const T) -> T { #[cfg(debug_assertions)] // Too expensive to always enable (for now?) ub_checks::assert_unsafe_precondition!( check_language_ub, - "ptr::read requires that the pointer argument is aligned and non-null", + "ptr::read requires that the pointer argument is aligned and non-null \ + (src:{addr}, align:{align})", ( addr: *const () = src as *const (), align: usize = align_of::(), @@ -1930,7 +1936,8 @@ pub const unsafe fn write(dst: *mut T, src: T) { #[cfg(debug_assertions)] // Too expensive to always enable (for now?) ub_checks::assert_unsafe_precondition!( check_language_ub, - "ptr::write requires that the pointer argument is aligned and non-null", + "ptr::write requires that the pointer argument is aligned and non-null \ + (dst:{addr}, align:{align})", ( addr: *mut () = dst as *mut (), align: usize = align_of::(), @@ -2105,7 +2112,8 @@ pub unsafe fn read_volatile(src: *const T) -> T { unsafe { ub_checks::assert_unsafe_precondition!( check_language_ub, - "ptr::read_volatile requires that the pointer argument is aligned", + "ptr::read_volatile requires that the pointer argument is aligned \ + (src:{addr}, align:{align})", ( addr: *const () = src as *const (), align: usize = align_of::(), @@ -2192,7 +2200,8 @@ pub unsafe fn write_volatile(dst: *mut T, src: T) { unsafe { ub_checks::assert_unsafe_precondition!( check_language_ub, - "ptr::write_volatile requires that the pointer argument is aligned", + "ptr::write_volatile requires that the pointer argument is aligned \ + (dst:{addr}, align:{align})", ( addr: *mut () = dst as *mut (), align: usize = align_of::(), diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index ba78afc7ea114..e73be67473b69 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -375,7 +375,8 @@ impl *mut T { ub_checks::assert_unsafe_precondition!( check_language_ub, - "ptr::offset requires the address calculation to not overflow", + "ptr::offset requires the address calculation to not overflow \ + (self:{this}, count:{count}, size:{size})", ( this: *const () = self as *const (), count: isize = count, @@ -949,7 +950,8 @@ impl *mut T { #[cfg(debug_assertions)] // Expensive, and doesn't catch much in the wild. ub_checks::assert_unsafe_precondition!( check_language_ub, - "ptr::add requires that the address calculation does not overflow", + "ptr::add requires that the address calculation does not overflow \ + (self:{this}, count:{count}, size:{size})", ( this: *const () = self as *const (), count: usize = count, @@ -1054,7 +1056,8 @@ impl *mut T { #[cfg(debug_assertions)] // Expensive, and doesn't catch much in the wild. ub_checks::assert_unsafe_precondition!( check_language_ub, - "ptr::sub requires that the address calculation does not overflow", + "ptr::sub requires that the address calculation does not overflow \ + (self:{this}, count:{count}, size:{size})", ( this: *const () = self as *const (), count: usize = count, diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 10f83120428b9..23b6fccdba870 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -235,7 +235,7 @@ impl NonNull { unsafe { assert_unsafe_precondition!( check_language_ub, - "NonNull::new_unchecked requires that the pointer is non-null", + "NonNull::new_unchecked requires that the pointer is non-null (ptr:{ptr})", (ptr: *mut () = ptr as *mut ()) => !ptr.is_null() ); NonNull { pointer: ptr as _ } diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index de220e7e38a4b..29d711a74d9c2 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -233,9 +233,10 @@ unsafe impl const SliceIndex<[T]> for usize { #[track_caller] unsafe fn get_unchecked(self, slice: *const [T]) -> *const T { assert_unsafe_precondition!( - check_language_ub, // okay because of the `assume` below - "slice::get_unchecked requires that the index is within the slice", - (this: usize = self, len: usize = slice.len()) => this < len + check_language_ub, + "slice::get_unchecked requires that the index is within the slice \ + (index:{index}, len:{len})", + (index: usize = self, len: usize = slice.len()) => index < len ); // SAFETY: the caller guarantees that `slice` is not dangling, so it // cannot be longer than `isize::MAX`. They also guarantee that @@ -254,8 +255,9 @@ unsafe impl const SliceIndex<[T]> for usize { unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut T { assert_unsafe_precondition!( check_library_ub, - "slice::get_unchecked_mut requires that the index is within the slice", - (this: usize = self, len: usize = slice.len()) => this < len + "slice::get_unchecked_mut requires that the index is within the slice \ + (index:{index}, len:{len})", + (index: usize = self, len: usize = slice.len()) => index < len ); // SAFETY: see comments for `get_unchecked` above. unsafe { slice_get_unchecked(slice, self) } @@ -305,7 +307,8 @@ unsafe impl const SliceIndex<[T]> for ops::IndexRange { unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { assert_unsafe_precondition!( check_library_ub, - "slice::get_unchecked requires that the index is within the slice", + "slice::get_unchecked requires that the index is within the slice \ + (end:{end}, len:{len})", (end: usize = self.end(), len: usize = slice.len()) => end <= len ); // SAFETY: the caller guarantees that `slice` is not dangling, so it @@ -320,7 +323,8 @@ unsafe impl const SliceIndex<[T]> for ops::IndexRange { unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { assert_unsafe_precondition!( check_library_ub, - "slice::get_unchecked_mut requires that the index is within the slice", + "slice::get_unchecked_mut requires that the index is within the slice \ + (end:{end}, len:{len})", (end: usize = self.end(), len: usize = slice.len()) => end <= len ); @@ -387,7 +391,8 @@ unsafe impl const SliceIndex<[T]> for ops::Range { unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { assert_unsafe_precondition!( check_library_ub, - "slice::get_unchecked requires that the range is within the slice", + "slice::get_unchecked requires that the range is within the slice \ + (range:{start}..{end}, len:{len})", ( start: usize = self.start, end: usize = self.end, @@ -412,7 +417,8 @@ unsafe impl const SliceIndex<[T]> for ops::Range { unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { assert_unsafe_precondition!( check_library_ub, - "slice::get_unchecked_mut requires that the range is within the slice", + "slice::get_unchecked_mut requires that the range is within the slice \ + (range:{start}..{end}, len:{len})", ( start: usize = self.start, end: usize = self.end, diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index f7f5ee819b2e4..ba7ef58d15fa9 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -944,7 +944,8 @@ impl [T] { pub const unsafe fn swap_unchecked(&mut self, a: usize, b: usize) { assert_unsafe_precondition!( check_library_ub, - "slice::swap_unchecked requires that the indices are within the slice", + "slice::swap_unchecked requires that the indices are within the slice \ + (a:{a}, b:{b}, len:{len})", ( len: usize = self.len(), a: usize = a, @@ -1334,7 +1335,8 @@ impl [T] { pub const unsafe fn as_chunks_unchecked(&self) -> &[[T; N]] { assert_unsafe_precondition!( check_language_ub, - "slice::as_chunks_unchecked requires `N != 0` and the slice to split exactly into `N`-element chunks", + "slice::as_chunks_unchecked requires `N != 0` and the slice to split exactly into `N`-element chunks \ + (N:{n}, len:{len})", (n: usize = N, len: usize = self.len()) => n != 0 && len.is_multiple_of(n), ); // SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length @@ -1494,7 +1496,8 @@ impl [T] { pub const unsafe fn as_chunks_unchecked_mut(&mut self) -> &mut [[T; N]] { assert_unsafe_precondition!( check_language_ub, - "slice::as_chunks_unchecked requires `N != 0` and the slice to split exactly into `N`-element chunks", + "slice::as_chunks_unchecked requires `N != 0` and the slice to split exactly into `N`-element chunks \ + (N:{n}, len:{len})", (n: usize = N, len: usize = self.len()) => n != 0 && len.is_multiple_of(n) ); // SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length @@ -2039,7 +2042,8 @@ impl [T] { assert_unsafe_precondition!( check_library_ub, - "slice::split_at_unchecked requires the index to be within the slice", + "slice::split_at_unchecked requires the index to be within the slice \ + (mid:{mid}, len:{len})", (mid: usize = mid, len: usize = len) => mid <= len, ); @@ -2089,7 +2093,8 @@ impl [T] { assert_unsafe_precondition!( check_library_ub, - "slice::split_at_mut_unchecked requires the index to be within the slice", + "slice::split_at_mut_unchecked requires the index to be within the slice \ + (mid:{mid}, len:{len})", (mid: usize = mid, len: usize = len) => mid <= len, ); diff --git a/library/core/src/slice/raw.rs b/library/core/src/slice/raw.rs index 80b2176933dab..de5b82346fc22 100644 --- a/library/core/src/slice/raw.rs +++ b/library/core/src/slice/raw.rs @@ -126,7 +126,8 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] unsafe { ub_checks::assert_unsafe_precondition!( check_language_ub, - "slice::from_raw_parts requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX`", + "slice::from_raw_parts requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX` \ + (data:{data}, size:{size}, align:{align}, len:{len})", ( data: *mut () = data as *mut (), size: usize = size_of::(), @@ -181,7 +182,8 @@ pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a m unsafe { ub_checks::assert_unsafe_precondition!( check_language_ub, - "slice::from_raw_parts_mut requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX`", + "slice::from_raw_parts_mut requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX` \ + (data:{data}, size:{size}, align:{align}, len:{len})", ( data: *mut () = data as *mut (), size: usize = size_of::(), diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 3a5efa7d83511..0507e2c92b4a5 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -17,7 +17,7 @@ use self::pattern::{DoubleEndedSearcher, Pattern, ReverseSearcher, Searcher}; use crate::char::{self, EscapeDebugExtArgs}; use crate::ops::Range; use crate::slice::{self, SliceIndex}; -use crate::ub_checks::assert_unsafe_precondition; +//use crate::ub_checks::assert_unsafe_precondition; use crate::{ascii, mem}; pub mod pattern; @@ -2744,11 +2744,13 @@ impl str { #[must_use] #[inline] pub const unsafe fn as_ascii_unchecked(&self) -> &[ascii::Char] { + /* assert_unsafe_precondition!( check_library_ub, "as_ascii_unchecked requires that the string is valid ASCII", (it: &str = self) => it.is_ascii() ); + */ // SAFETY: the caller promised that every byte of this string slice // is ASCII. diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs index a7cc943994c53..0c30977a5b0c9 100644 --- a/library/core/src/str/traits.rs +++ b/library/core/src/str/traits.rs @@ -204,7 +204,8 @@ unsafe impl const SliceIndex for ops::Range { // `str::get_unchecked` without adding a special function // to `SliceIndex` just for this. check_library_ub, - "str::get_unchecked requires that the range is within the string slice", + "str::get_unchecked requires that the range is within the string slice \ + (range:{start}..{end}, len:{len})", ( start: usize = self.start, end: usize = self.end, @@ -226,7 +227,8 @@ unsafe impl const SliceIndex for ops::Range { assert_unsafe_precondition!( check_library_ub, - "str::get_unchecked_mut requires that the range is within the string slice", + "str::get_unchecked_mut requires that the range is within the string slice \ + (range:{start}..{end}, len:{len})", ( start: usize = self.start, end: usize = self.end, @@ -309,7 +311,8 @@ unsafe impl const SliceIndex for range::Range { // `str::get_unchecked` without adding a special function // to `SliceIndex` just for this. check_library_ub, - "str::get_unchecked requires that the range is within the string slice", + "str::get_unchecked requires that the range is within the string slice \ + (range:{start}..{end}, len:{len})", ( start: usize = self.start, end: usize = self.end, @@ -331,7 +334,8 @@ unsafe impl const SliceIndex for range::Range { assert_unsafe_precondition!( check_library_ub, - "str::get_unchecked_mut requires that the range is within the string slice", + "str::get_unchecked_mut requires that the range is within the string slice \ + (range:{start}..{end}, len:{len})", ( start: usize = self.start, end: usize = self.end, diff --git a/library/core/src/ub_checks.rs b/library/core/src/ub_checks.rs index 514ff93c9820e..e5db49ddf5916 100644 --- a/library/core/src/ub_checks.rs +++ b/library/core/src/ub_checks.rs @@ -65,13 +65,20 @@ macro_rules! assert_unsafe_precondition { #[inline] #[rustc_nounwind] #[track_caller] + #[rustc_allow_const_fn_unstable(const_eval_select)] const fn precondition_check($($name:$ty),*) { - if !$e { - let msg = concat!("unsafe precondition(s) violated: ", $message, - "\n\nThis indicates a bug in the program. \ - This Undefined Behavior check is optional, and cannot be relied on for safety."); - ::core::panicking::panic_nounwind_fmt(::core::fmt::Arguments::new_const(&[msg]), false); - } + if $e { return; } + $( + let $name = ::core::displaywrapper::DisplayWrapper($name); + )* + ::core::intrinsics::const_eval_select!( + @capture { $($name: ::core::displaywrapper::DisplayWrapper<$ty>),* }: + if const { + ::core::panicking::panic_nounwind($message); + } else #[allow(unused)] { + ::core::panicking::panic_nounwind_fmt(format_args!($message), false); + } + ) } if ::core::ub_checks::$kind() { diff --git a/tests/ui/consts/const-eval/ub-slice-get-unchecked.stderr b/tests/ui/consts/const-eval/ub-slice-get-unchecked.stderr index 88ea310f19c68..7bd1d8852ba35 100644 --- a/tests/ui/consts/const-eval/ub-slice-get-unchecked.stderr +++ b/tests/ui/consts/const-eval/ub-slice-get-unchecked.stderr @@ -1,10 +1,14 @@ -error[E0080]: evaluation panicked: unsafe precondition(s) violated: slice::get_unchecked requires that the range is within the slice - - This indicates a bug in the program. This Undefined Behavior check is optional, and cannot be relied on for safety. +error[E0080]: evaluation panicked: slice::get_unchecked requires that the range is within the slice (range:{start}..{end}, len:{len}) --> $DIR/ub-slice-get-unchecked.rs:7:27 | LL | const B: &[()] = unsafe { A.get_unchecked(3..1) }; - | ^^^^^^^^^^^^^^^^^^^^^ evaluation of `B` failed here + | ^^^^^^^^^^^^^^^^^^^^^ evaluation of `B` failed inside this call + | +note: inside ` as SliceIndex<[T]>>::get_unchecked::precondition_check::compiletime` + --> $SRC_DIR/core/src/slice/index.rs:LL:COL +note: inside `core::panicking::panic_nounwind` + --> $SRC_DIR/core/src/panicking.rs:LL:COL + = note: this error originates in the macro `assert_unsafe_precondition` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/precondition-checks/alignment.rs b/tests/ui/precondition-checks/alignment.rs index 038a625bed7e3..759220213c590 100644 --- a/tests/ui/precondition-checks/alignment.rs +++ b/tests/ui/precondition-checks/alignment.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: Alignment::new_unchecked requires +//@ error-pattern: Alignment::new_unchecked requires #![feature(ptr_alignment_type)] diff --git a/tests/ui/precondition-checks/ascii-char-digit_unchecked.rs b/tests/ui/precondition-checks/ascii-char-digit_unchecked.rs index 41ba2c5254a4d..d44c94225276b 100644 --- a/tests/ui/precondition-checks/ascii-char-digit_unchecked.rs +++ b/tests/ui/precondition-checks/ascii-char-digit_unchecked.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: `ascii::Char::digit_unchecked` input cannot exceed 9 +//@ error-pattern: `ascii::Char::digit_unchecked` input cannot exceed 9 #![feature(ascii_char)] diff --git a/tests/ui/precondition-checks/assert_unchecked.rs b/tests/ui/precondition-checks/assert_unchecked.rs index da5383cdea025..1ee78c8906f3a 100644 --- a/tests/ui/precondition-checks/assert_unchecked.rs +++ b/tests/ui/precondition-checks/assert_unchecked.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: hint::assert_unchecked must never be called when the condition is false +//@ error-pattern: hint::assert_unchecked must never be called when the condition is false fn main() { unsafe { diff --git a/tests/ui/precondition-checks/char-from_u32_unchecked.rs b/tests/ui/precondition-checks/char-from_u32_unchecked.rs index 7c34d926d3e9f..d92af42d6c3f6 100644 --- a/tests/ui/precondition-checks/char-from_u32_unchecked.rs +++ b/tests/ui/precondition-checks/char-from_u32_unchecked.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: invalid value for `char` +//@ error-pattern: invalid value for `char` fn main() { unsafe { diff --git a/tests/ui/precondition-checks/copy-nonoverlapping.rs b/tests/ui/precondition-checks/copy-nonoverlapping.rs index 1d584ddef4c28..2199601c0767a 100644 --- a/tests/ui/precondition-checks/copy-nonoverlapping.rs +++ b/tests/ui/precondition-checks/copy-nonoverlapping.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: ptr::copy_nonoverlapping requires +//@ error-pattern: ptr::copy_nonoverlapping requires //@ revisions: null_src null_dst misaligned_src misaligned_dst overlapping #![allow(invalid_null_arguments)] diff --git a/tests/ui/precondition-checks/copy.rs b/tests/ui/precondition-checks/copy.rs index 8faa56a880ead..7de42302be639 100644 --- a/tests/ui/precondition-checks/copy.rs +++ b/tests/ui/precondition-checks/copy.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: ptr::copy requires +//@ error-pattern: ptr::copy requires //@ revisions: null_src null_dst misaligned_src misaligned_dst #![allow(invalid_null_arguments)] diff --git a/tests/ui/precondition-checks/layout.rs b/tests/ui/precondition-checks/layout.rs index 6755ebce854e4..4021e4fdcef71 100644 --- a/tests/ui/precondition-checks/layout.rs +++ b/tests/ui/precondition-checks/layout.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: Layout::from_size_align_unchecked requires +//@ error-pattern: Layout::from_size_align_unchecked requires //@ revisions: toolarge badalign fn main() { diff --git a/tests/ui/precondition-checks/nonnull.rs b/tests/ui/precondition-checks/nonnull.rs index 75bbd65b4868b..b090bb8ffbf93 100644 --- a/tests/ui/precondition-checks/nonnull.rs +++ b/tests/ui/precondition-checks/nonnull.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: NonNull::new_unchecked requires +//@ error-pattern: NonNull::new_unchecked requires fn main() { unsafe { diff --git a/tests/ui/precondition-checks/nonzero-from_mut_unchecked.rs b/tests/ui/precondition-checks/nonzero-from_mut_unchecked.rs index d55707fdd0be2..f31895c411775 100644 --- a/tests/ui/precondition-checks/nonzero-from_mut_unchecked.rs +++ b/tests/ui/precondition-checks/nonzero-from_mut_unchecked.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: NonZero::from_mut_unchecked requires +//@ error-pattern: NonZero::from_mut_unchecked requires #![feature(nonzero_from_mut)] diff --git a/tests/ui/precondition-checks/nonzero-new_unchecked.rs b/tests/ui/precondition-checks/nonzero-new_unchecked.rs index 978f01f150f08..a04f6fb22674d 100644 --- a/tests/ui/precondition-checks/nonzero-new_unchecked.rs +++ b/tests/ui/precondition-checks/nonzero-new_unchecked.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: NonZero::new_unchecked requires +//@ error-pattern: NonZero::new_unchecked requires fn main() { unsafe { diff --git a/tests/ui/precondition-checks/read.rs b/tests/ui/precondition-checks/read.rs index d5ab7773987fc..336cec6175770 100644 --- a/tests/ui/precondition-checks/read.rs +++ b/tests/ui/precondition-checks/read.rs @@ -1,6 +1,6 @@ //@ run-fail //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: ptr::read requires +//@ error-pattern: ptr::read requires //@ revisions: null misaligned //@ ignore-test (unimplemented) diff --git a/tests/ui/precondition-checks/read_volatile.rs b/tests/ui/precondition-checks/read_volatile.rs index 33350dfbc4fa3..5913902143014 100644 --- a/tests/ui/precondition-checks/read_volatile.rs +++ b/tests/ui/precondition-checks/read_volatile.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: ptr::read_volatile requires +//@ error-pattern: ptr::read_volatile requires //@ revisions: misaligned use std::ptr; diff --git a/tests/ui/precondition-checks/replace.rs b/tests/ui/precondition-checks/replace.rs index 447a00c65723b..8b5f8ba6d2ccb 100644 --- a/tests/ui/precondition-checks/replace.rs +++ b/tests/ui/precondition-checks/replace.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: ptr::replace requires +//@ error-pattern: ptr::replace requires //@ revisions: null misaligned #![allow(invalid_null_arguments)] diff --git a/tests/ui/precondition-checks/slice-from-raw-parts-mut.rs b/tests/ui/precondition-checks/slice-from-raw-parts-mut.rs index b6397ab2a12bc..a02ce99832294 100644 --- a/tests/ui/precondition-checks/slice-from-raw-parts-mut.rs +++ b/tests/ui/precondition-checks/slice-from-raw-parts-mut.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: slice::from_raw_parts_mut requires +//@ error-pattern: slice::from_raw_parts_mut requires //@ revisions: null misaligned toolarge #![allow(invalid_null_arguments)] diff --git a/tests/ui/precondition-checks/slice-from-raw-parts.rs b/tests/ui/precondition-checks/slice-from-raw-parts.rs index a317e3d41a0fa..d73ce0219d903 100644 --- a/tests/ui/precondition-checks/slice-from-raw-parts.rs +++ b/tests/ui/precondition-checks/slice-from-raw-parts.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: slice::from_raw_parts requires +//@ error-pattern: slice::from_raw_parts requires //@ revisions: null misaligned toolarge #![allow(invalid_null_arguments)] diff --git a/tests/ui/precondition-checks/slice-get_unchecked.rs b/tests/ui/precondition-checks/slice-get_unchecked.rs index 7bcb8442540a4..78e31658716b8 100644 --- a/tests/ui/precondition-checks/slice-get_unchecked.rs +++ b/tests/ui/precondition-checks/slice-get_unchecked.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: slice::get_unchecked requires +//@ error-pattern: slice::get_unchecked requires //@ revisions: usize range range_to range_from backwards_range fn main() { diff --git a/tests/ui/precondition-checks/slice-get_unchecked_mut.rs b/tests/ui/precondition-checks/slice-get_unchecked_mut.rs index 2ba3227f39ea8..fc6c97f9739d3 100644 --- a/tests/ui/precondition-checks/slice-get_unchecked_mut.rs +++ b/tests/ui/precondition-checks/slice-get_unchecked_mut.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: slice::get_unchecked_mut requires +//@ error-pattern: slice::get_unchecked_mut requires //@ revisions: usize range range_to range_from backwards_range fn main() { diff --git a/tests/ui/precondition-checks/str-get_unchecked.rs b/tests/ui/precondition-checks/str-get_unchecked.rs index 2273190e9f4c8..6a6964a9e6411 100644 --- a/tests/ui/precondition-checks/str-get_unchecked.rs +++ b/tests/ui/precondition-checks/str-get_unchecked.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: str::get_unchecked requires +//@ error-pattern: str::get_unchecked requires //@ revisions: range range_to range_from backwards_range fn main() { diff --git a/tests/ui/precondition-checks/str-get_unchecked_mut.rs b/tests/ui/precondition-checks/str-get_unchecked_mut.rs index 53e6ee64d470c..9795cf9b72691 100644 --- a/tests/ui/precondition-checks/str-get_unchecked_mut.rs +++ b/tests/ui/precondition-checks/str-get_unchecked_mut.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: str::get_unchecked_mut requires +//@ error-pattern: str::get_unchecked_mut requires //@ revisions: range range_to range_from backwards_range fn main() { diff --git a/tests/ui/precondition-checks/swap-nonoverlapping.rs b/tests/ui/precondition-checks/swap-nonoverlapping.rs index 81ba72382c0da..b05717ecdfdee 100644 --- a/tests/ui/precondition-checks/swap-nonoverlapping.rs +++ b/tests/ui/precondition-checks/swap-nonoverlapping.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: ptr::swap_nonoverlapping requires +//@ error-pattern: ptr::swap_nonoverlapping requires //@ revisions: null_src null_dst misaligned_src misaligned_dst overlapping #![allow(invalid_null_arguments)] diff --git a/tests/ui/precondition-checks/unchecked_add.rs b/tests/ui/precondition-checks/unchecked_add.rs index b7727aeb9682c..33614ed542c7f 100644 --- a/tests/ui/precondition-checks/unchecked_add.rs +++ b/tests/ui/precondition-checks/unchecked_add.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: u8::unchecked_add cannot overflow +//@ error-pattern: u8::unchecked_add cannot overflow fn main() { unsafe { diff --git a/tests/ui/precondition-checks/unchecked_mul.rs b/tests/ui/precondition-checks/unchecked_mul.rs index 3eea8b66abbac..3734e705ff1bb 100644 --- a/tests/ui/precondition-checks/unchecked_mul.rs +++ b/tests/ui/precondition-checks/unchecked_mul.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: u8::unchecked_add cannot overflow +//@ error-pattern: u8::unchecked_add cannot overflow fn main() { unsafe { diff --git a/tests/ui/precondition-checks/unchecked_shl.rs b/tests/ui/precondition-checks/unchecked_shl.rs index 57c617e08455b..0dc14be63654d 100644 --- a/tests/ui/precondition-checks/unchecked_shl.rs +++ b/tests/ui/precondition-checks/unchecked_shl.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: u8::unchecked_shl cannot overflow +//@ error-pattern: u8::unchecked_shl cannot overflow #![feature(unchecked_shifts)] diff --git a/tests/ui/precondition-checks/unchecked_shr.rs b/tests/ui/precondition-checks/unchecked_shr.rs index 18502d2b64593..c251487c13034 100644 --- a/tests/ui/precondition-checks/unchecked_shr.rs +++ b/tests/ui/precondition-checks/unchecked_shr.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: u8::unchecked_shr cannot overflow +//@ error-pattern: u8::unchecked_shr cannot overflow #![feature(unchecked_shifts)] diff --git a/tests/ui/precondition-checks/unchecked_sub.rs b/tests/ui/precondition-checks/unchecked_sub.rs index bfe8f5849f592..0b0e435704fa4 100644 --- a/tests/ui/precondition-checks/unchecked_sub.rs +++ b/tests/ui/precondition-checks/unchecked_sub.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: u8::unchecked_sub cannot overflow +//@ error-pattern: u8::unchecked_sub cannot overflow fn main() { unsafe { diff --git a/tests/ui/precondition-checks/unreachable_unchecked.rs b/tests/ui/precondition-checks/unreachable_unchecked.rs index f2855d03a3e79..e2a1ede6e1c63 100644 --- a/tests/ui/precondition-checks/unreachable_unchecked.rs +++ b/tests/ui/precondition-checks/unreachable_unchecked.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: hint::unreachable_unchecked must never be reached +//@ error-pattern: hint::unreachable_unchecked must never be reached fn main() { unsafe { diff --git a/tests/ui/precondition-checks/vec-from-parts.rs b/tests/ui/precondition-checks/vec-from-parts.rs index ace90770360e5..a6cbc1cd6d54b 100644 --- a/tests/ui/precondition-checks/vec-from-parts.rs +++ b/tests/ui/precondition-checks/vec-from-parts.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Cdebug-assertions=yes -//@ error-pattern: unsafe precondition(s) violated: Vec::from_parts_in requires that length <= capacity +//@ error-pattern: Vec::from_parts_in requires that length <= capacity #![feature(allocator_api)] use std::ptr::NonNull; diff --git a/tests/ui/precondition-checks/vec-from-raw-parts.rs b/tests/ui/precondition-checks/vec-from-raw-parts.rs index 1bc8e6ada10d9..cd14cf73820ee 100644 --- a/tests/ui/precondition-checks/vec-from-raw-parts.rs +++ b/tests/ui/precondition-checks/vec-from-raw-parts.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Cdebug-assertions=yes -//@ error-pattern: unsafe precondition(s) violated: Vec::from_raw_parts_in requires that length <= capacity +//@ error-pattern: Vec::from_raw_parts_in requires that length <= capacity //@ revisions: vec_from_raw_parts vec_from_raw_parts_in string_from_raw_parts #![feature(allocator_api)] diff --git a/tests/ui/precondition-checks/vec-set-len.rs b/tests/ui/precondition-checks/vec-set-len.rs index c6bdee7dc67e2..acd0f8ad15383 100644 --- a/tests/ui/precondition-checks/vec-set-len.rs +++ b/tests/ui/precondition-checks/vec-set-len.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Cdebug-assertions=yes -//@ error-pattern: unsafe precondition(s) violated: Vec::set_len requires that new_len <= capacity() +//@ error-pattern: Vec::set_len requires that new_len <= capacity() fn main() { let mut vec: Vec = Vec::with_capacity(5); diff --git a/tests/ui/precondition-checks/write.rs b/tests/ui/precondition-checks/write.rs index 5d6b9586fad7d..937db484f2076 100644 --- a/tests/ui/precondition-checks/write.rs +++ b/tests/ui/precondition-checks/write.rs @@ -1,6 +1,6 @@ //@ run-fail //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: ptr::write requires +//@ error-pattern: ptr::write requires //@ revisions: null misaligned //@ ignore-test (unimplemented) diff --git a/tests/ui/precondition-checks/write_bytes.rs b/tests/ui/precondition-checks/write_bytes.rs index be4f5a089f035..845de7c90433c 100644 --- a/tests/ui/precondition-checks/write_bytes.rs +++ b/tests/ui/precondition-checks/write_bytes.rs @@ -1,6 +1,6 @@ //@ run-fail //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: ptr::write requires +//@ error-pattern: ptr::write requires //@ revisions: null misaligned //@ ignore-test (unimplemented) diff --git a/tests/ui/precondition-checks/write_volatile.rs b/tests/ui/precondition-checks/write_volatile.rs index d6ad6320e41c1..8c9f2a4229190 100644 --- a/tests/ui/precondition-checks/write_volatile.rs +++ b/tests/ui/precondition-checks/write_volatile.rs @@ -1,6 +1,6 @@ //@ run-crash //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes -//@ error-pattern: unsafe precondition(s) violated: ptr::write_volatile requires +//@ error-pattern: ptr::write_volatile requires //@ revisions: misaligned use std::ptr; From 8008ba1f710a43a06d097eb005b72a0b405f930c Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 11 Oct 2025 22:58:11 -0400 Subject: [PATCH 2/9] Cleanup --- library/core/src/char/methods.rs | 9 ++-- library/core/src/displaywrapper.rs | 87 +++++++++++++++++++++++------- library/core/src/lib.rs | 6 +-- library/core/src/num/mod.rs | 4 +- library/core/src/num/nonzero.rs | 2 - library/core/src/ptr/mod.rs | 2 +- library/core/src/str/mod.rs | 5 +- 7 files changed, 79 insertions(+), 36 deletions(-) diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index d33ee30ac4b52..928d46ffd7046 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -4,7 +4,7 @@ use super::*; use crate::panic::const_panic; use crate::slice; use crate::str::from_utf8_unchecked_mut; -//use crate::ub_checks::assert_unsafe_precondition; +use crate::ub_checks::assert_unsafe_precondition; use crate::unicode::printable::is_printable; use crate::unicode::{self, conversions}; @@ -1258,13 +1258,12 @@ impl char { #[unstable(feature = "ascii_char", issue = "110998")] #[inline] pub const unsafe fn as_ascii_unchecked(&self) -> ascii::Char { - /* assert_unsafe_precondition!( check_library_ub, - "as_ascii_unchecked requires that the char is valid ASCII", - (it: &char = self) => it.is_ascii() + "as_ascii_unchecked requires that the char is valid ASCII \ + (self:{it})", + (it: char = *self) => it.is_ascii() ); - */ // SAFETY: the caller promised that this char is ASCII. unsafe { ascii::Char::from_u8_unchecked(*self as u8) } diff --git a/library/core/src/displaywrapper.rs b/library/core/src/displaywrapper.rs index 8f2552645728f..1bb14734134fb 100644 --- a/library/core/src/displaywrapper.rs +++ b/library/core/src/displaywrapper.rs @@ -5,8 +5,15 @@ use core::fmt::{Display, Formatter, Result}; pub struct DisplayWrapper(#[unstable(feature = "ub_checks", issue = "none")] pub T); trait Displayable: Sized + Clone + Copy { - const IS_POINTER: bool; - const SIGNED: bool; + const IS_POINTER: bool = false; + const IS_INT: bool = false; + const IS_UINT: bool = false; + const IS_CHAR: bool = false; + const IS_STR: bool = false; + #[inline] + fn as_char(self) -> char { + unimplemented!() + } #[inline] fn addr(self) -> usize { unimplemented!() @@ -21,9 +28,16 @@ trait Displayable: Sized + Clone + Copy { } } +impl Displayable for char { + const IS_CHAR: bool = true; + #[inline] + fn as_char(self) -> char { + self + } +} + impl Displayable for *const T { const IS_POINTER: bool = true; - const SIGNED: bool = false; #[inline] fn addr(self) -> usize { self.addr() @@ -31,47 +45,42 @@ impl Displayable for *const T { } impl Displayable for *mut T { const IS_POINTER: bool = true; - const SIGNED: bool = false; #[inline] fn addr(self) -> usize { self.addr() } } + impl Displayable for u8 { - const IS_POINTER: bool = false; - const SIGNED: bool = false; + const IS_UINT: bool = true; #[inline] fn as_u128(self) -> u128 { self as u128 } } impl Displayable for u32 { - const IS_POINTER: bool = false; - const SIGNED: bool = false; + const IS_UINT: bool = true; #[inline] fn as_u128(self) -> u128 { self as u128 } } impl Displayable for u64 { - const IS_POINTER: bool = false; - const SIGNED: bool = false; + const IS_UINT: bool = true; #[inline] fn as_u128(self) -> u128 { self as u128 } } impl Displayable for usize { - const IS_POINTER: bool = false; - const SIGNED: bool = false; + const IS_UINT: bool = true; #[inline] fn as_u128(self) -> u128 { self as u128 } } impl Displayable for u128 { - const IS_POINTER: bool = false; - const SIGNED: bool = false; + const IS_UINT: bool = true; #[inline] fn as_u128(self) -> u128 { self @@ -79,28 +88,68 @@ impl Displayable for u128 { } impl Displayable for isize { - const IS_POINTER: bool = false; - const SIGNED: bool = true; + const IS_INT: bool = true; + #[inline] + fn as_i128(self) -> i128 { + self as i128 + } +} +impl Displayable for i8 { + const IS_INT: bool = true; + #[inline] + fn as_i128(self) -> i128 { + self as i128 + } +} +impl Displayable for i16 { + const IS_INT: bool = true; + #[inline] + fn as_i128(self) -> i128 { + self as i128 + } +} +impl Displayable for i32 { + const IS_INT: bool = true; + #[inline] + fn as_i128(self) -> i128 { + self as i128 + } +} +impl Displayable for i64 { + const IS_INT: bool = true; #[inline] fn as_i128(self) -> i128 { self as i128 } } impl Displayable for i128 { - const IS_POINTER: bool = false; - const SIGNED: bool = true; + const IS_INT: bool = true; #[inline] fn as_i128(self) -> i128 { self } } + #[unstable(feature = "ub_checks", issue = "none")] impl Display for DisplayWrapper { #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> Result { const HEX: [u8; 16] = *b"0123456789abcdef"; + assert!(T::IS_POINTER ^ T::IS_UINT ^ T::IS_INT ^ T::IS_CHAR); + if T::IS_CHAR { + let mut buf = [0u8; 4]; + let s = self.0.as_char().encode_utf8(&mut buf); + return f.write_str(s); + } + /* + if T::IS_STR { + return f.write_str(self.0.as_str()); + } + */ + let mut buf = [0u8; 42]; let mut cur = buf.len(); + if T::IS_POINTER { let mut n = self.0.addr(); while n >= 16 { @@ -118,7 +167,7 @@ impl Display for DisplayWrapper { buf[cur] = b'0'; } else { let mut is_negative = false; - let mut n = if T::SIGNED { + let mut n = if T::IS_INT { let signed = self.0.as_i128(); is_negative = signed < 0; (!(signed as u128)).wrapping_add(1) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 6dbd2f3993ae7..485b0707ca9bd 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -347,12 +347,12 @@ pub mod alloc; // note: does not need to be public mod bool; -mod escape; -mod tuple; -mod unit; #[doc(hidden)] #[unstable(feature = "ub_checks", issue = "none")] pub mod displaywrapper; +mod escape; +mod tuple; +mod unit; #[stable(feature = "core_primitive", since = "1.43.0")] pub mod primitive; diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index f52618af2367a..7d02bb18efe1e 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -506,13 +506,11 @@ impl u8 { #[unstable(feature = "ascii_char", issue = "110998")] #[inline] pub const unsafe fn as_ascii_unchecked(&self) -> ascii::Char { - /* assert_unsafe_precondition!( check_library_ub, "as_ascii_unchecked requires that the byte is valid ASCII", - (it: &u8 = self) => it.is_ascii() + (it: u8 = *self) => it.is_ascii() ); - */ // SAFETY: the caller promised that this byte is ASCII. unsafe { ascii::Char::from_u8_unchecked(*self) } diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 3c7dde0960d90..d9184e3c9c229 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -420,7 +420,6 @@ where ub_checks::assert_unsafe_precondition!( check_language_ub, "NonZero::new_unchecked requires the argument to be non-zero", - // FIXME: Can't print n here because of how the check is written () => false, ); intrinsics::unreachable() @@ -462,7 +461,6 @@ where ub_checks::assert_unsafe_precondition!( check_library_ub, "NonZero::from_mut_unchecked requires the argument to dereference as non-zero", - // FIXME: Can't print n here because of how the check is written () => false, ); intrinsics::unreachable() diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index afefd5092687b..536c358476e5c 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -1581,7 +1581,7 @@ pub const unsafe fn replace(dst: *mut T, src: T) -> T { unsafe { ub_checks::assert_unsafe_precondition!( check_language_ub, - "ptr::replace requires that the pointer argument is aligned and non-null\ + "ptr::replace requires that the pointer argument is aligned and non-null \ (dst:{addr}, (align:{align}))", ( addr: *const () = dst as *const (), diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 0507e2c92b4a5..29ce244cf1f08 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -17,7 +17,7 @@ use self::pattern::{DoubleEndedSearcher, Pattern, ReverseSearcher, Searcher}; use crate::char::{self, EscapeDebugExtArgs}; use crate::ops::Range; use crate::slice::{self, SliceIndex}; -//use crate::ub_checks::assert_unsafe_precondition; +use crate::ub_checks::assert_unsafe_precondition; use crate::{ascii, mem}; pub mod pattern; @@ -2744,13 +2744,12 @@ impl str { #[must_use] #[inline] pub const unsafe fn as_ascii_unchecked(&self) -> &[ascii::Char] { - /* + // FIXME: Add &str support to DisplayWrapper assert_unsafe_precondition!( check_library_ub, "as_ascii_unchecked requires that the string is valid ASCII", (it: &str = self) => it.is_ascii() ); - */ // SAFETY: the caller promised that every byte of this string slice // is ASCII. From 1bb08721b7b98bd94ac893cbfbe51ac83adbb8fa Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 11 Oct 2025 22:58:16 -0400 Subject: [PATCH 3/9] Add a missing test --- .../precondition-checks/as_ascii_unchecked.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 tests/ui/precondition-checks/as_ascii_unchecked.rs diff --git a/tests/ui/precondition-checks/as_ascii_unchecked.rs b/tests/ui/precondition-checks/as_ascii_unchecked.rs new file mode 100644 index 0000000000000..c5845d067ca4d --- /dev/null +++ b/tests/ui/precondition-checks/as_ascii_unchecked.rs @@ -0,0 +1,17 @@ +//@ run-crash +//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes +//@ error-pattern: as_ascii_unchecked requires that the +//@ revisions: char str + +#![feature(ascii_char)] + +use std::ascii::Char; + +fn main() { + unsafe { + #[cfg(char)] + let _c: Char = '🦀'.as_ascii_unchecked(); + #[cfg(str)] + let _c: &[Char] = "🦀".as_ascii_unchecked(); + } +} From 56fcd59ec642812b45406a2c1c1e1d28e3452367 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 11 Oct 2025 23:08:01 -0400 Subject: [PATCH 4/9] Document the unsafe --- library/core/src/displaywrapper.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/core/src/displaywrapper.rs b/library/core/src/displaywrapper.rs index 1bb14734134fb..e17d385e67e6f 100644 --- a/library/core/src/displaywrapper.rs +++ b/library/core/src/displaywrapper.rs @@ -187,6 +187,7 @@ impl Display for DisplayWrapper { buf[cur] = b'-'; } } + // SAFETY: The buffer is initially ASCII and we only write ASCII bytes to it. let s = unsafe { core::str::from_utf8_unchecked(&buf[cur..]) }; f.write_str(s) } From 3c8b70f179bddfe2271173fd9194b98d698070af Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 11 Oct 2025 23:42:18 -0400 Subject: [PATCH 5/9] Sink DisplayWrapper into the runtime code --- library/core/src/displaywrapper.rs | 242 +++++++++++++---------------- library/core/src/ub_checks.rs | 8 +- 2 files changed, 112 insertions(+), 138 deletions(-) diff --git a/library/core/src/displaywrapper.rs b/library/core/src/displaywrapper.rs index e17d385e67e6f..8826e52e9039c 100644 --- a/library/core/src/displaywrapper.rs +++ b/library/core/src/displaywrapper.rs @@ -2,189 +2,163 @@ use core::fmt::{Display, Formatter, Result}; #[allow(missing_debug_implementations)] #[unstable(feature = "ub_checks", issue = "none")] -pub struct DisplayWrapper(#[unstable(feature = "ub_checks", issue = "none")] pub T); +pub enum DisplayWrapper<'a> { + Bool(bool), + Char(char), + Str(&'a str), + Ptr(*const ()), + Uint(u128), + Int(i128), +} -trait Displayable: Sized + Clone + Copy { - const IS_POINTER: bool = false; - const IS_INT: bool = false; - const IS_UINT: bool = false; - const IS_CHAR: bool = false; - const IS_STR: bool = false; - #[inline] - fn as_char(self) -> char { - unimplemented!() +impl From for DisplayWrapper<'_> { + fn from(b: bool) -> Self { + Self::Bool(b) } - #[inline] - fn addr(self) -> usize { - unimplemented!() - } - #[inline] - fn as_u128(self) -> u128 { - unimplemented!() - } - #[inline] - fn as_i128(self) -> i128 { - unimplemented!() +} + +impl<'a> From<&'a str> for DisplayWrapper<'a> { + fn from(s: &'a str) -> Self { + Self::Str(s) } } -impl Displayable for char { - const IS_CHAR: bool = true; - #[inline] - fn as_char(self) -> char { - self +impl From for DisplayWrapper<'_> { + fn from(c: char) -> Self { + Self::Char(c) } } -impl Displayable for *const T { - const IS_POINTER: bool = true; - #[inline] - fn addr(self) -> usize { - self.addr() +impl From<*const ()> for DisplayWrapper<'_> { + fn from(c: *const ()) -> Self { + Self::Ptr(c) } } -impl Displayable for *mut T { - const IS_POINTER: bool = true; - #[inline] - fn addr(self) -> usize { - self.addr() +impl From<*mut ()> for DisplayWrapper<'_> { + fn from(c: *mut ()) -> Self { + Self::Ptr(c as *const ()) } } -impl Displayable for u8 { - const IS_UINT: bool = true; - #[inline] - fn as_u128(self) -> u128 { - self as u128 +impl From for DisplayWrapper<'_> { + fn from(c: u8) -> Self { + Self::Uint(c as u128) } } -impl Displayable for u32 { - const IS_UINT: bool = true; - #[inline] - fn as_u128(self) -> u128 { - self as u128 +impl From for DisplayWrapper<'_> { + fn from(c: u16) -> Self { + Self::Uint(c as u128) } } -impl Displayable for u64 { - const IS_UINT: bool = true; - #[inline] - fn as_u128(self) -> u128 { - self as u128 +impl From for DisplayWrapper<'_> { + fn from(c: u32) -> Self { + Self::Uint(c as u128) } } -impl Displayable for usize { - const IS_UINT: bool = true; - #[inline] - fn as_u128(self) -> u128 { - self as u128 +impl From for DisplayWrapper<'_> { + fn from(c: u64) -> Self { + Self::Uint(c as u128) } } -impl Displayable for u128 { - const IS_UINT: bool = true; - #[inline] - fn as_u128(self) -> u128 { - self +impl From for DisplayWrapper<'_> { + fn from(c: u128) -> Self { + Self::Uint(c as u128) + } +} +impl From for DisplayWrapper<'_> { + fn from(c: usize) -> Self { + Self::Uint(c as u128) } } -impl Displayable for isize { - const IS_INT: bool = true; - #[inline] - fn as_i128(self) -> i128 { - self as i128 +impl From for DisplayWrapper<'_> { + fn from(c: i8) -> Self { + Self::Int(c as i128) } } -impl Displayable for i8 { - const IS_INT: bool = true; - #[inline] - fn as_i128(self) -> i128 { - self as i128 +impl From for DisplayWrapper<'_> { + fn from(c: i16) -> Self { + Self::Int(c as i128) } } -impl Displayable for i16 { - const IS_INT: bool = true; - #[inline] - fn as_i128(self) -> i128 { - self as i128 +impl From for DisplayWrapper<'_> { + fn from(c: i32) -> Self { + Self::Int(c as i128) } } -impl Displayable for i32 { - const IS_INT: bool = true; - #[inline] - fn as_i128(self) -> i128 { - self as i128 +impl From for DisplayWrapper<'_> { + fn from(c: i64) -> Self { + Self::Int(c as i128) } } -impl Displayable for i64 { - const IS_INT: bool = true; - #[inline] - fn as_i128(self) -> i128 { - self as i128 +impl From for DisplayWrapper<'_> { + fn from(c: i128) -> Self { + Self::Int(c as i128) } } -impl Displayable for i128 { - const IS_INT: bool = true; - #[inline] - fn as_i128(self) -> i128 { - self +impl From for DisplayWrapper<'_> { + fn from(c: isize) -> Self { + Self::Int(c as i128) } } #[unstable(feature = "ub_checks", issue = "none")] -impl Display for DisplayWrapper { +impl Display for DisplayWrapper<'_> { #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> Result { const HEX: [u8; 16] = *b"0123456789abcdef"; - assert!(T::IS_POINTER ^ T::IS_UINT ^ T::IS_INT ^ T::IS_CHAR); - if T::IS_CHAR { - let mut buf = [0u8; 4]; - let s = self.0.as_char().encode_utf8(&mut buf); - return f.write_str(s); - } - /* - if T::IS_STR { - return f.write_str(self.0.as_str()); - } - */ - let mut buf = [0u8; 42]; let mut cur = buf.len(); - if T::IS_POINTER { - let mut n = self.0.addr(); - while n >= 16 { - let d = n % 16; - n /= 16; - cur -= 1; - buf[cur] = HEX[d]; + match *self { + Self::Bool(_) | Self::Str(_) => panic!(), + Self::Char(c) => { + let mut buf = [0u8; 4]; + let s = c.encode_utf8(&mut buf); + return f.write_str(s); } - cur -= 1; - buf[cur] = HEX[n]; + Self::Ptr(ptr) => { + let mut n = ptr.addr(); + while n >= 16 { + let d = n % 16; + n /= 16; + cur -= 1; + buf[cur] = HEX[d]; + } + cur -= 1; + buf[cur] = HEX[n]; - cur -= 1; - buf[cur] = b'x'; - cur -= 1; - buf[cur] = b'0'; - } else { - let mut is_negative = false; - let mut n = if T::IS_INT { - let signed = self.0.as_i128(); - is_negative = signed < 0; - (!(signed as u128)).wrapping_add(1) - } else { - self.0.as_u128() - }; - while n >= 10 { - let d = n % 10; - n /= 10; cur -= 1; - buf[cur] = (d as u8) + b'0'; + buf[cur] = b'x'; + cur -= 1; + buf[cur] = b'0'; + } + Self::Uint(mut n) => { + while n >= 10 { + let d = n % 10; + n /= 10; + cur -= 1; + buf[cur] = (d as u8) + b'0'; + } + cur -= 1; + buf[cur] = (n as u8) + b'0'; } - cur -= 1; - buf[cur] = (n as u8) + b'0'; - if is_negative { + Self::Int(n) => { + let is_negative = n < 0; + let mut n = (!(n as u128)).wrapping_add(1); + + while n >= 10 { + let d = n % 10; + n /= 10; + cur -= 1; + buf[cur] = (d as u8) + b'0'; + } cur -= 1; - buf[cur] = b'-'; + buf[cur] = (n as u8) + b'0'; + if is_negative { + cur -= 1; + buf[cur] = b'-'; + } } } // SAFETY: The buffer is initially ASCII and we only write ASCII bytes to it. diff --git a/library/core/src/ub_checks.rs b/library/core/src/ub_checks.rs index e5db49ddf5916..0e55ddc3acb9e 100644 --- a/library/core/src/ub_checks.rs +++ b/library/core/src/ub_checks.rs @@ -68,14 +68,14 @@ macro_rules! assert_unsafe_precondition { #[rustc_allow_const_fn_unstable(const_eval_select)] const fn precondition_check($($name:$ty),*) { if $e { return; } - $( - let $name = ::core::displaywrapper::DisplayWrapper($name); - )* ::core::intrinsics::const_eval_select!( - @capture { $($name: ::core::displaywrapper::DisplayWrapper<$ty>),* }: + @capture { $($name: $ty),* }: if const { ::core::panicking::panic_nounwind($message); } else #[allow(unused)] { + $( + let $name = ::core::displaywrapper::DisplayWrapper::from($name); + )* ::core::panicking::panic_nounwind_fmt(format_args!($message), false); } ) From aeba2530311f8b983aac7cd8a4bb99d9e3159940 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 11 Oct 2025 23:52:47 -0400 Subject: [PATCH 6/9] Fix item-collection test --- .../codegen-units/item-collection/opaque-return-impls.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/codegen-units/item-collection/opaque-return-impls.rs b/tests/codegen-units/item-collection/opaque-return-impls.rs index 7d5f4f5b66982..97152b5c274e0 100644 --- a/tests/codegen-units/item-collection/opaque-return-impls.rs +++ b/tests/codegen-units/item-collection/opaque-return-impls.rs @@ -87,3 +87,11 @@ pub fn foo3() -> Box> { //~ MONO_ITEM fn std::boxed::Box::::new //~ MONO_ITEM fn Counter::new //~ MONO_ITEM fn core::fmt::rt::>::new_const::<1> +//~ MONO_ITEM fn as std::fmt::Display>::fmt +//~ MONO_ITEM fn as std::slice::SliceIndex<[T]>>::get_unchecked::precondition_check +//~ MONO_ITEM fn as std::slice::SliceIndex<[T]>>::get_unchecked::precondition_check::runtime +//~ MONO_ITEM fn as std::slice::SliceIndex<[u8]>>::index +//~ MONO_ITEM fn std::char::encode_utf8_raw +//~ MONO_ITEM fn std::char::encode_utf8_raw_unchecked +//~ MONO_ITEM fn std::slice::from_raw_parts_mut::precondition_check +//~ MONO_ITEM fn std::slice::from_raw_parts_mut::precondition_check::runtime From ae8af35c17e5df39b4f62f800dc103d096a0cbdb Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sun, 12 Oct 2025 01:05:13 -0400 Subject: [PATCH 7/9] Bless a Miri test --- src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr b/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr index c5f6e62b86906..a8f16aa6e09b5 100644 --- a/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr +++ b/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr @@ -1,8 +1,6 @@ -thread 'main' ($TID) panicked at tests/fail/ptr_swap_nonoverlapping.rs:LL:CC: -unsafe precondition(s) violated: ptr::swap_nonoverlapping requires that both pointer arguments are aligned and non-null and the specified memory ranges do not overlap - -This indicates a bug in the program. This Undefined Behavior check is optional, and cannot be relied on for safety. +thread 'main' ($TID) panicked at RUSTLIB/core/src/ptr/mod.rs:LL:CC: +ptr::swap_nonoverlapping requires that both pointer arguments are aligned and non-null and the specified memory ranges do not overlap (x:$HEX, y:$HEX, size:8, align:8, count:1) note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect thread caused non-unwinding panic. aborting. @@ -17,6 +15,7 @@ LL | crate::process::abort(); = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC = note: inside `std::panicking::panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::ptr::swap_nonoverlapping::precondition_check::runtime` at RUSTLIB/core/src/ub_checks.rs:LL:CC note: inside `main` --> tests/fail/ptr_swap_nonoverlapping.rs:LL:CC | From 103ca9c3c56384753797a33aaa41e2bd5205533e Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sun, 12 Oct 2025 11:36:19 -0400 Subject: [PATCH 8/9] Change to a generics-based impl --- library/core/src/displaywrapper.rs | 244 +++++++----------- library/core/src/ub_checks.rs | 2 +- .../item-collection/opaque-return-impls.rs | 9 +- 3 files changed, 105 insertions(+), 150 deletions(-) diff --git a/library/core/src/displaywrapper.rs b/library/core/src/displaywrapper.rs index 8826e52e9039c..23fbfbd045989 100644 --- a/library/core/src/displaywrapper.rs +++ b/library/core/src/displaywrapper.rs @@ -1,168 +1,124 @@ use core::fmt::{Display, Formatter, Result}; #[allow(missing_debug_implementations)] -#[unstable(feature = "ub_checks", issue = "none")] -pub enum DisplayWrapper<'a> { - Bool(bool), - Char(char), - Str(&'a str), - Ptr(*const ()), - Uint(u128), - Int(i128), -} +pub struct DisplayWrapper(pub T); -impl From for DisplayWrapper<'_> { - fn from(b: bool) -> Self { - Self::Bool(b) - } +macro_rules! display_int { + ($ty:ty) => { + impl Display for DisplayWrapper<$ty> { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + let n = self.0; + let is_negative = n < 0; + let n = (!(n as u128)).wrapping_add(1); + display_int(n, is_negative, f) + } + } + }; } -impl<'a> From<&'a str> for DisplayWrapper<'a> { - fn from(s: &'a str) -> Self { - Self::Str(s) - } -} +display_int!(i8); +display_int!(i16); +display_int!(i32); +display_int!(i64); +display_int!(i128); +display_int!(isize); -impl From for DisplayWrapper<'_> { - fn from(c: char) -> Self { - Self::Char(c) - } +macro_rules! display_uint { + ($ty:ty) => { + impl Display for DisplayWrapper<$ty> { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + display_int(self.0 as u128, false, f) + } + } + }; } -impl From<*const ()> for DisplayWrapper<'_> { - fn from(c: *const ()) -> Self { - Self::Ptr(c) - } -} -impl From<*mut ()> for DisplayWrapper<'_> { - fn from(c: *mut ()) -> Self { - Self::Ptr(c as *const ()) - } -} +display_uint!(u8); +display_uint!(u16); +display_uint!(u32); +display_uint!(u64); +display_uint!(u128); +display_uint!(usize); -impl From for DisplayWrapper<'_> { - fn from(c: u8) -> Self { - Self::Uint(c as u128) - } -} -impl From for DisplayWrapper<'_> { - fn from(c: u16) -> Self { - Self::Uint(c as u128) - } -} -impl From for DisplayWrapper<'_> { - fn from(c: u32) -> Self { - Self::Uint(c as u128) - } -} -impl From for DisplayWrapper<'_> { - fn from(c: u64) -> Self { - Self::Uint(c as u128) - } -} -impl From for DisplayWrapper<'_> { - fn from(c: u128) -> Self { - Self::Uint(c as u128) +impl Display for DisplayWrapper<*const ()> { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + format_ptr(self.0.addr(), f) } } -impl From for DisplayWrapper<'_> { - fn from(c: usize) -> Self { - Self::Uint(c as u128) +impl Display for DisplayWrapper<*mut ()> { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + format_ptr(self.0.addr(), f) } } -impl From for DisplayWrapper<'_> { - fn from(c: i8) -> Self { - Self::Int(c as i128) - } -} -impl From for DisplayWrapper<'_> { - fn from(c: i16) -> Self { - Self::Int(c as i128) - } -} -impl From for DisplayWrapper<'_> { - fn from(c: i32) -> Self { - Self::Int(c as i128) - } -} -impl From for DisplayWrapper<'_> { - fn from(c: i64) -> Self { - Self::Int(c as i128) - } -} -impl From for DisplayWrapper<'_> { - fn from(c: i128) -> Self { - Self::Int(c as i128) - } +#[inline] +fn format_ptr(addr: usize, f: &mut Formatter<'_>) -> Result { + const HEX: [u8; 16] = *b"0123456789abcdef"; + let mut buf = [0u8; 42]; + let mut cur = buf.len(); + + let mut n = addr; + while n >= 16 { + let d = n % 16; + n /= 16; + cur -= 1; + buf[cur] = HEX[d]; + } + cur -= 1; + buf[cur] = HEX[n]; + + cur -= 1; + buf[cur] = b'x'; + cur -= 1; + buf[cur] = b'0'; + + // SAFETY: The buffer is initially ASCII and we only write ASCII bytes to it. + let s = unsafe { core::str::from_utf8_unchecked(&buf[cur..]) }; + f.write_str(s) } -impl From for DisplayWrapper<'_> { - fn from(c: isize) -> Self { - Self::Int(c as i128) + +impl Display for DisplayWrapper { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + let mut buf = [0u8; 4]; + let s = self.0.encode_utf8(&mut buf); + f.write_str(s) } } -#[unstable(feature = "ub_checks", issue = "none")] -impl Display for DisplayWrapper<'_> { +impl Display for DisplayWrapper { #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> Result { - const HEX: [u8; 16] = *b"0123456789abcdef"; - let mut buf = [0u8; 42]; - let mut cur = buf.len(); - - match *self { - Self::Bool(_) | Self::Str(_) => panic!(), - Self::Char(c) => { - let mut buf = [0u8; 4]; - let s = c.encode_utf8(&mut buf); - return f.write_str(s); - } - Self::Ptr(ptr) => { - let mut n = ptr.addr(); - while n >= 16 { - let d = n % 16; - n /= 16; - cur -= 1; - buf[cur] = HEX[d]; - } - cur -= 1; - buf[cur] = HEX[n]; - - cur -= 1; - buf[cur] = b'x'; - cur -= 1; - buf[cur] = b'0'; - } - Self::Uint(mut n) => { - while n >= 10 { - let d = n % 10; - n /= 10; - cur -= 1; - buf[cur] = (d as u8) + b'0'; - } - cur -= 1; - buf[cur] = (n as u8) + b'0'; - } - Self::Int(n) => { - let is_negative = n < 0; - let mut n = (!(n as u128)).wrapping_add(1); - - while n >= 10 { - let d = n % 10; - n /= 10; - cur -= 1; - buf[cur] = (d as u8) + b'0'; - } - cur -= 1; - buf[cur] = (n as u8) + b'0'; - if is_negative { - cur -= 1; - buf[cur] = b'-'; - } - } - } - // SAFETY: The buffer is initially ASCII and we only write ASCII bytes to it. - let s = unsafe { core::str::from_utf8_unchecked(&buf[cur..]) }; + let s = match self.0 { + true => "true", + false => "false", + }; f.write_str(s) } } + +#[inline] +fn display_int(mut n: u128, is_negative: bool, f: &mut Formatter<'_>) -> Result { + let mut buf = [0u8; 42]; + let mut cur = buf.len(); + + while n >= 10 { + let d = n % 10; + n /= 10; + cur -= 1; + buf[cur] = (d as u8) + b'0'; + } + cur -= 1; + buf[cur] = (n as u8) + b'0'; + if is_negative { + cur -= 1; + buf[cur] = b'-'; + } + // SAFETY: The buffer is initially ASCII and we only write ASCII bytes to it. + let s = unsafe { core::str::from_utf8_unchecked(&buf[cur..]) }; + f.write_str(s) +} diff --git a/library/core/src/ub_checks.rs b/library/core/src/ub_checks.rs index 0e55ddc3acb9e..1cc50f2211915 100644 --- a/library/core/src/ub_checks.rs +++ b/library/core/src/ub_checks.rs @@ -74,7 +74,7 @@ macro_rules! assert_unsafe_precondition { ::core::panicking::panic_nounwind($message); } else #[allow(unused)] { $( - let $name = ::core::displaywrapper::DisplayWrapper::from($name); + let $name = ::core::displaywrapper::DisplayWrapper($name); )* ::core::panicking::panic_nounwind_fmt(format_args!($message), false); } diff --git a/tests/codegen-units/item-collection/opaque-return-impls.rs b/tests/codegen-units/item-collection/opaque-return-impls.rs index 97152b5c274e0..142228c5e087d 100644 --- a/tests/codegen-units/item-collection/opaque-return-impls.rs +++ b/tests/codegen-units/item-collection/opaque-return-impls.rs @@ -87,11 +87,10 @@ pub fn foo3() -> Box> { //~ MONO_ITEM fn std::boxed::Box::::new //~ MONO_ITEM fn Counter::new //~ MONO_ITEM fn core::fmt::rt::>::new_const::<1> -//~ MONO_ITEM fn as std::fmt::Display>::fmt //~ MONO_ITEM fn as std::slice::SliceIndex<[T]>>::get_unchecked::precondition_check //~ MONO_ITEM fn as std::slice::SliceIndex<[T]>>::get_unchecked::precondition_check::runtime //~ MONO_ITEM fn as std::slice::SliceIndex<[u8]>>::index -//~ MONO_ITEM fn std::char::encode_utf8_raw -//~ MONO_ITEM fn std::char::encode_utf8_raw_unchecked -//~ MONO_ITEM fn std::slice::from_raw_parts_mut::precondition_check -//~ MONO_ITEM fn std::slice::from_raw_parts_mut::precondition_check::runtime +//~ MONO_ITEM fn as std::fmt::Display>::fmt +//~ MONO_ITEM fn as std::fmt::Display>::fmt +//~ MONO_ITEM fn core::displaywrapper::display_int +//~ MONO_ITEM fn core::displaywrapper::format_ptr From c67897093a0baf23cb6f682d512bc4bbae652b96 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sun, 12 Oct 2025 20:18:23 -0400 Subject: [PATCH 9/9] Try some golf --- library/core/src/displaywrapper.rs | 72 ++++++++++++++---------------- 1 file changed, 34 insertions(+), 38 deletions(-) diff --git a/library/core/src/displaywrapper.rs b/library/core/src/displaywrapper.rs index 23fbfbd045989..127f0cfdd2395 100644 --- a/library/core/src/displaywrapper.rs +++ b/library/core/src/displaywrapper.rs @@ -1,4 +1,5 @@ use core::fmt::{Display, Formatter, Result}; +use core::num::NonZeroU128; #[allow(missing_debug_implementations)] pub struct DisplayWrapper(pub T); @@ -55,32 +56,6 @@ impl Display for DisplayWrapper<*mut ()> { } } -#[inline] -fn format_ptr(addr: usize, f: &mut Formatter<'_>) -> Result { - const HEX: [u8; 16] = *b"0123456789abcdef"; - let mut buf = [0u8; 42]; - let mut cur = buf.len(); - - let mut n = addr; - while n >= 16 { - let d = n % 16; - n /= 16; - cur -= 1; - buf[cur] = HEX[d]; - } - cur -= 1; - buf[cur] = HEX[n]; - - cur -= 1; - buf[cur] = b'x'; - cur -= 1; - buf[cur] = b'0'; - - // SAFETY: The buffer is initially ASCII and we only write ASCII bytes to it. - let s = unsafe { core::str::from_utf8_unchecked(&buf[cur..]) }; - f.write_str(s) -} - impl Display for DisplayWrapper { #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> Result { @@ -101,22 +76,43 @@ impl Display for DisplayWrapper { } } +const ALPHABET: &[u8; 16] = b"0123456789abcdef"; + #[inline] -fn display_int(mut n: u128, is_negative: bool, f: &mut Formatter<'_>) -> Result { - let mut buf = [0u8; 42]; +fn format_with_radix(mut n: u128, buf: &mut [u8], radix: NonZeroU128) -> usize { let mut cur = buf.len(); - - while n >= 10 { - let d = n % 10; - n /= 10; - cur -= 1; - buf[cur] = (d as u8) + b'0'; + while n >= radix.get() { + let d = n % radix; + n /= radix; + cur = cur.wrapping_sub(1); + buf[cur] = ALPHABET[d as usize]; } - cur -= 1; - buf[cur] = (n as u8) + b'0'; + cur = cur.wrapping_sub(1); + buf[cur] = ALPHABET[n as usize]; + cur +} + +#[inline] +pub fn format_ptr(addr: usize, f: &mut Formatter<'_>) -> Result { + let mut buf = [b'0'; 42]; + let mut cur = + format_with_radix(addr as u128, &mut buf, const { NonZeroU128::new(16).unwrap() }); + + cur = cur.wrapping_sub(1); + buf[cur] = b'x'; + cur = cur.wrapping_sub(1); + + // SAFETY: The buffer is initially ASCII and we only write ASCII bytes to it. + let s = unsafe { core::str::from_utf8_unchecked(&buf[cur..]) }; + f.write_str(s) +} + +#[inline] +pub fn display_int(n: u128, is_negative: bool, f: &mut Formatter<'_>) -> Result { + let mut buf = [b'-'; 42]; + let mut cur = format_with_radix(n, &mut buf, const { NonZeroU128::new(10).unwrap() }); if is_negative { - cur -= 1; - buf[cur] = b'-'; + cur = cur.wrapping_sub(1); } // SAFETY: The buffer is initially ASCII and we only write ASCII bytes to it. let s = unsafe { core::str::from_utf8_unchecked(&buf[cur..]) };