Skip to content

Commit

Permalink
Include arguments to the precondition check in failure messages
Browse files Browse the repository at this point in the history
  • Loading branch information
saethlin committed Dec 30, 2024
1 parent 64feb9b commit 65e5e12
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 19 deletions.
30 changes: 15 additions & 15 deletions library/core/src/slice/index.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Indexing implementations for `[T]`.
use crate::panic::const_panic;
use crate::ub_checks::assert_unsafe_precondition;
use crate::ub_checks::assert_unsafe_precondition2;
use crate::{ops, range};

#[stable(feature = "rust1", since = "1.0.0")]
Expand Down Expand Up @@ -240,10 +240,10 @@ unsafe impl<T> SliceIndex<[T]> for usize {

#[inline]
unsafe fn get_unchecked(self, slice: *const [T]) -> *const T {
assert_unsafe_precondition!(
assert_unsafe_precondition2!(
check_language_ub,
"slice::get_unchecked requires that the index is within the slice",
(this: usize = self, len: usize = slice.len()) => this < len
"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
Expand All @@ -259,10 +259,10 @@ unsafe impl<T> SliceIndex<[T]> for usize {

#[inline]
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut T {
assert_unsafe_precondition!(
assert_unsafe_precondition2!(
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 { get_mut_noubcheck(slice, self) }
Expand Down Expand Up @@ -308,9 +308,9 @@ unsafe impl<T> SliceIndex<[T]> for ops::IndexRange {

#[inline]
unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
assert_unsafe_precondition!(
assert_unsafe_precondition2!(
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
Expand All @@ -322,9 +322,9 @@ unsafe impl<T> SliceIndex<[T]> for ops::IndexRange {

#[inline]
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] {
assert_unsafe_precondition!(
assert_unsafe_precondition2!(
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
);

Expand Down Expand Up @@ -387,9 +387,9 @@ unsafe impl<T> SliceIndex<[T]> for ops::Range<usize> {

#[inline]
unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
assert_unsafe_precondition!(
assert_unsafe_precondition2!(
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,
Expand All @@ -411,9 +411,9 @@ unsafe impl<T> SliceIndex<[T]> for ops::Range<usize> {

#[inline]
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] {
assert_unsafe_precondition!(
assert_unsafe_precondition2!(
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,
Expand Down
8 changes: 4 additions & 4 deletions library/core/src/slice/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,9 @@ use crate::{array, ptr, ub_checks};
pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] {
// SAFETY: the caller must uphold the safety contract for `from_raw_parts`.
unsafe {
ub_checks::assert_unsafe_precondition!(
ub_checks::assert_unsafe_precondition2!(
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::<T>(),
Expand Down Expand Up @@ -177,9 +177,9 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T]
pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a mut [T] {
// SAFETY: the caller must uphold the safety contract for `from_raw_parts_mut`.
unsafe {
ub_checks::assert_unsafe_precondition!(
ub_checks::assert_unsafe_precondition2!(
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::<T>(),
Expand Down
49 changes: 49 additions & 0 deletions library/core/src/ub_checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,55 @@ macro_rules! assert_unsafe_precondition {
}
#[unstable(feature = "ub_checks", issue = "none")]
pub use assert_unsafe_precondition;

/// assert_unsafe_precondition, but full
#[macro_export]
#[unstable(feature = "ub_checks", issue = "none")]
macro_rules! assert_unsafe_precondition2 {
($kind:ident, $message:expr, ($($name:ident:$ty:ty = $arg:expr),*$(,)?) => $e:expr $(,)?) => {
{
// This check is inlineable, but not by the MIR inliner.
// The reason for this is that the MIR inliner is in an exceptionally bad position
// to think about whether or not to inline this. In MIR, this call is gated behind `debug_assertions`,
// which will codegen to `false` in release builds. Inlining the check would be wasted work in that case and
// would be bad for compile times.
//
// LLVM on the other hand sees the constant branch, so if it's `false`, it can immediately delete it without
// inlining the check. If it's `true`, it can inline it and get significantly better performance.
#[rustc_no_mir_inline]
#[inline]
#[rustc_nounwind]
#[track_caller]
#[rustc_allow_const_fn_unstable(const_eval_select)]
const fn precondition_check($($name:$ty),*) {
if $e { return; }
crate::intrinsics::const_eval_select!(
@capture { $($name: $ty),* }:
if const #[track_caller] {
::core::panicking::panic_nounwind(
concat!("unsafe precondition(s) violated: ", $message)
);
} else #[track_caller] {
::core::panicking::panic_nounwind_fmt(
format_args!(
concat!("unsafe precondition(s) violated: ", $message),
$($name=$name),*
),
false
);
}
)
}

if ::core::ub_checks::$kind() {
precondition_check($($arg,)*);
}
}
};
}
#[unstable(feature = "ub_checks", issue = "none")]
pub use assert_unsafe_precondition2;

/// Checking library UB is always enabled when UB-checking is done
/// (and we use a reexport so that there is no unnecessary wrapper function).
#[unstable(feature = "ub_checks", issue = "none")]
Expand Down

0 comments on commit 65e5e12

Please sign in to comment.