mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 02:57:37 +00:00
Rollup merge of #139145 - okaneco:safe_splits, r=Amanieu
slice: Remove some uses of unsafe in first/last chunk methods Remove unsafe `split_at_unchecked` and `split_at_mut_unchecked` in some slice `split_first_chunk`/`split_last_chunk` methods. Replace those calls with the safe `split_at` and `split_at_checked` where applicable. Add codegen tests to check for no panics when calculating the last chunk index using `checked_sub` and `split_at`. Better viewed with whitespace disabled in diff view --- The unchecked calls are mostly manual implementations of the safe methods, but with the safety condition negated from `mid <= len` to `len < mid`. ```rust if self.len() < N { None } else { // SAFETY: We manually verified the bounds of the split. let (first, tail) = unsafe { self.split_at_unchecked(N) }; // Or for the last_chunk methods let (init, last) = unsafe { self.split_at_unchecked(self.len() - N) }; ``` Unsafe is still needed for the pointer array casts. Their safety comments are unmodified.
This commit is contained in:
commit
e332aa89a7
@ -382,16 +382,11 @@ impl<T> [T] {
|
|||||||
#[stable(feature = "slice_first_last_chunk", since = "1.77.0")]
|
#[stable(feature = "slice_first_last_chunk", since = "1.77.0")]
|
||||||
#[rustc_const_stable(feature = "slice_first_last_chunk", since = "1.77.0")]
|
#[rustc_const_stable(feature = "slice_first_last_chunk", since = "1.77.0")]
|
||||||
pub const fn split_first_chunk<const N: usize>(&self) -> Option<(&[T; N], &[T])> {
|
pub const fn split_first_chunk<const N: usize>(&self) -> Option<(&[T; N], &[T])> {
|
||||||
if self.len() < N {
|
let Some((first, tail)) = self.split_at_checked(N) else { return None };
|
||||||
None
|
|
||||||
} else {
|
|
||||||
// SAFETY: We manually verified the bounds of the split.
|
|
||||||
let (first, tail) = unsafe { self.split_at_unchecked(N) };
|
|
||||||
|
|
||||||
// SAFETY: We explicitly check for the correct number of elements,
|
// SAFETY: We explicitly check for the correct number of elements,
|
||||||
// and do not let the references outlive the slice.
|
// and do not let the references outlive the slice.
|
||||||
Some((unsafe { &*(first.as_ptr().cast::<[T; N]>()) }, tail))
|
Some((unsafe { &*(first.as_ptr().cast::<[T; N]>()) }, tail))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a mutable array reference to the first `N` items in the slice and the remaining
|
/// Returns a mutable array reference to the first `N` items in the slice and the remaining
|
||||||
@ -419,17 +414,12 @@ impl<T> [T] {
|
|||||||
pub const fn split_first_chunk_mut<const N: usize>(
|
pub const fn split_first_chunk_mut<const N: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
) -> Option<(&mut [T; N], &mut [T])> {
|
) -> Option<(&mut [T; N], &mut [T])> {
|
||||||
if self.len() < N {
|
let Some((first, tail)) = self.split_at_mut_checked(N) else { return None };
|
||||||
None
|
|
||||||
} else {
|
|
||||||
// SAFETY: We manually verified the bounds of the split.
|
|
||||||
let (first, tail) = unsafe { self.split_at_mut_unchecked(N) };
|
|
||||||
|
|
||||||
// SAFETY: We explicitly check for the correct number of elements,
|
// SAFETY: We explicitly check for the correct number of elements,
|
||||||
// do not let the reference outlive the slice,
|
// do not let the reference outlive the slice,
|
||||||
// and enforce exclusive mutability of the chunk by the split.
|
// and enforce exclusive mutability of the chunk by the split.
|
||||||
Some((unsafe { &mut *(first.as_mut_ptr().cast::<[T; N]>()) }, tail))
|
Some((unsafe { &mut *(first.as_mut_ptr().cast::<[T; N]>()) }, tail))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an array reference to the last `N` items in the slice and the remaining slice.
|
/// Returns an array reference to the last `N` items in the slice and the remaining slice.
|
||||||
@ -452,16 +442,12 @@ impl<T> [T] {
|
|||||||
#[stable(feature = "slice_first_last_chunk", since = "1.77.0")]
|
#[stable(feature = "slice_first_last_chunk", since = "1.77.0")]
|
||||||
#[rustc_const_stable(feature = "slice_first_last_chunk", since = "1.77.0")]
|
#[rustc_const_stable(feature = "slice_first_last_chunk", since = "1.77.0")]
|
||||||
pub const fn split_last_chunk<const N: usize>(&self) -> Option<(&[T], &[T; N])> {
|
pub const fn split_last_chunk<const N: usize>(&self) -> Option<(&[T], &[T; N])> {
|
||||||
if self.len() < N {
|
let Some(index) = self.len().checked_sub(N) else { return None };
|
||||||
None
|
let (init, last) = self.split_at(index);
|
||||||
} else {
|
|
||||||
// SAFETY: We manually verified the bounds of the split.
|
|
||||||
let (init, last) = unsafe { self.split_at_unchecked(self.len() - N) };
|
|
||||||
|
|
||||||
// SAFETY: We explicitly check for the correct number of elements,
|
// SAFETY: We explicitly check for the correct number of elements,
|
||||||
// and do not let the references outlive the slice.
|
// and do not let the references outlive the slice.
|
||||||
Some((init, unsafe { &*(last.as_ptr().cast::<[T; N]>()) }))
|
Some((init, unsafe { &*(last.as_ptr().cast::<[T; N]>()) }))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a mutable array reference to the last `N` items in the slice and the remaining
|
/// Returns a mutable array reference to the last `N` items in the slice and the remaining
|
||||||
@ -489,17 +475,13 @@ impl<T> [T] {
|
|||||||
pub const fn split_last_chunk_mut<const N: usize>(
|
pub const fn split_last_chunk_mut<const N: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
) -> Option<(&mut [T], &mut [T; N])> {
|
) -> Option<(&mut [T], &mut [T; N])> {
|
||||||
if self.len() < N {
|
let Some(index) = self.len().checked_sub(N) else { return None };
|
||||||
None
|
let (init, last) = self.split_at_mut(index);
|
||||||
} else {
|
|
||||||
// SAFETY: We manually verified the bounds of the split.
|
|
||||||
let (init, last) = unsafe { self.split_at_mut_unchecked(self.len() - N) };
|
|
||||||
|
|
||||||
// SAFETY: We explicitly check for the correct number of elements,
|
// SAFETY: We explicitly check for the correct number of elements,
|
||||||
// do not let the reference outlive the slice,
|
// do not let the reference outlive the slice,
|
||||||
// and enforce exclusive mutability of the chunk by the split.
|
// and enforce exclusive mutability of the chunk by the split.
|
||||||
Some((init, unsafe { &mut *(last.as_mut_ptr().cast::<[T; N]>()) }))
|
Some((init, unsafe { &mut *(last.as_mut_ptr().cast::<[T; N]>()) }))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an array reference to the last `N` items in the slice.
|
/// Returns an array reference to the last `N` items in the slice.
|
||||||
@ -522,17 +504,13 @@ impl<T> [T] {
|
|||||||
#[stable(feature = "slice_first_last_chunk", since = "1.77.0")]
|
#[stable(feature = "slice_first_last_chunk", since = "1.77.0")]
|
||||||
#[rustc_const_stable(feature = "const_slice_last_chunk", since = "1.80.0")]
|
#[rustc_const_stable(feature = "const_slice_last_chunk", since = "1.80.0")]
|
||||||
pub const fn last_chunk<const N: usize>(&self) -> Option<&[T; N]> {
|
pub const fn last_chunk<const N: usize>(&self) -> Option<&[T; N]> {
|
||||||
if self.len() < N {
|
// FIXME(const-hack): Without const traits, we need this instead of `get`.
|
||||||
None
|
let Some(index) = self.len().checked_sub(N) else { return None };
|
||||||
} else {
|
let (_, last) = self.split_at(index);
|
||||||
// SAFETY: We manually verified the bounds of the slice.
|
|
||||||
// FIXME(const-hack): Without const traits, we need this instead of `get_unchecked`.
|
|
||||||
let last = unsafe { self.split_at_unchecked(self.len() - N).1 };
|
|
||||||
|
|
||||||
// SAFETY: We explicitly check for the correct number of elements,
|
// SAFETY: We explicitly check for the correct number of elements,
|
||||||
// and do not let the references outlive the slice.
|
// and do not let the references outlive the slice.
|
||||||
Some(unsafe { &*(last.as_ptr().cast::<[T; N]>()) })
|
Some(unsafe { &*(last.as_ptr().cast::<[T; N]>()) })
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a mutable array reference to the last `N` items in the slice.
|
/// Returns a mutable array reference to the last `N` items in the slice.
|
||||||
@ -556,18 +534,14 @@ impl<T> [T] {
|
|||||||
#[stable(feature = "slice_first_last_chunk", since = "1.77.0")]
|
#[stable(feature = "slice_first_last_chunk", since = "1.77.0")]
|
||||||
#[rustc_const_stable(feature = "const_slice_first_last_chunk", since = "1.83.0")]
|
#[rustc_const_stable(feature = "const_slice_first_last_chunk", since = "1.83.0")]
|
||||||
pub const fn last_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]> {
|
pub const fn last_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]> {
|
||||||
if self.len() < N {
|
// FIXME(const-hack): Without const traits, we need this instead of `get`.
|
||||||
None
|
let Some(index) = self.len().checked_sub(N) else { return None };
|
||||||
} else {
|
let (_, last) = self.split_at_mut(index);
|
||||||
// SAFETY: We manually verified the bounds of the slice.
|
|
||||||
// FIXME(const-hack): Without const traits, we need this instead of `get_unchecked`.
|
|
||||||
let last = unsafe { self.split_at_mut_unchecked(self.len() - N).1 };
|
|
||||||
|
|
||||||
// SAFETY: We explicitly check for the correct number of elements,
|
// SAFETY: We explicitly check for the correct number of elements,
|
||||||
// do not let the reference outlive the slice,
|
// do not let the reference outlive the slice,
|
||||||
// and require exclusive access to the entire slice to mutate the chunk.
|
// and require exclusive access to the entire slice to mutate the chunk.
|
||||||
Some(unsafe { &mut *(last.as_mut_ptr().cast::<[T; N]>()) })
|
Some(unsafe { &mut *(last.as_mut_ptr().cast::<[T; N]>()) })
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to an element or subslice depending on the type of
|
/// Returns a reference to an element or subslice depending on the type of
|
||||||
|
24
tests/codegen/slice-split-at.rs
Normal file
24
tests/codegen/slice-split-at.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
//@ compile-flags: -Copt-level=3
|
||||||
|
#![crate_type = "lib"]
|
||||||
|
|
||||||
|
// Check that no panic is generated in `split_at` when calculating the index for
|
||||||
|
// the tail chunk using `checked_sub`.
|
||||||
|
//
|
||||||
|
// Tests written for refactored implementations of:
|
||||||
|
// `<[T]>::{split_last_chunk, split_last_chunk_mut, last_chunk, last_chunk_mut}`
|
||||||
|
|
||||||
|
// CHECK-LABEL: @split_at_last_chunk
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn split_at_last_chunk(s: &[u8], chunk_size: usize) -> Option<(&[u8], &[u8])> {
|
||||||
|
// CHECK-NOT: panic
|
||||||
|
let Some(index) = s.len().checked_sub(chunk_size) else { return None };
|
||||||
|
Some(s.split_at(index))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: @split_at_mut_last_chunk
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn split_at_mut_last_chunk(s: &mut [u8], chunk_size: usize) -> Option<(&mut [u8], &mut [u8])> {
|
||||||
|
// CHECK-NOT: panic
|
||||||
|
let Some(index) = s.len().checked_sub(chunk_size) else { return None };
|
||||||
|
Some(s.split_at_mut(index))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user