Skip to content

Commit bea3f46

Browse files
authored
Rollup merge of rust-lang#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.
2 parents 408585e + 002019c commit bea3f46

File tree

1 file changed

+34
-60
lines changed

1 file changed

+34
-60
lines changed

Diff for: core/src/slice/mod.rs

+34-60
Original file line numberDiff line numberDiff line change
@@ -382,16 +382,11 @@ impl<T> [T] {
382382
#[stable(feature = "slice_first_last_chunk", since = "1.77.0")]
383383
#[rustc_const_stable(feature = "slice_first_last_chunk", since = "1.77.0")]
384384
pub const fn split_first_chunk<const N: usize>(&self) -> Option<(&[T; N], &[T])> {
385-
if self.len() < N {
386-
None
387-
} else {
388-
// SAFETY: We manually verified the bounds of the split.
389-
let (first, tail) = unsafe { self.split_at_unchecked(N) };
385+
let Some((first, tail)) = self.split_at_checked(N) else { return None };
390386

391-
// SAFETY: We explicitly check for the correct number of elements,
392-
// and do not let the references outlive the slice.
393-
Some((unsafe { &*(first.as_ptr().cast::<[T; N]>()) }, tail))
394-
}
387+
// SAFETY: We explicitly check for the correct number of elements,
388+
// and do not let the references outlive the slice.
389+
Some((unsafe { &*(first.as_ptr().cast::<[T; N]>()) }, tail))
395390
}
396391

397392
/// Returns a mutable array reference to the first `N` items in the slice and the remaining
@@ -419,17 +414,12 @@ impl<T> [T] {
419414
pub const fn split_first_chunk_mut<const N: usize>(
420415
&mut self,
421416
) -> Option<(&mut [T; N], &mut [T])> {
422-
if self.len() < N {
423-
None
424-
} else {
425-
// SAFETY: We manually verified the bounds of the split.
426-
let (first, tail) = unsafe { self.split_at_mut_unchecked(N) };
417+
let Some((first, tail)) = self.split_at_mut_checked(N) else { return None };
427418

428-
// SAFETY: We explicitly check for the correct number of elements,
429-
// do not let the reference outlive the slice,
430-
// and enforce exclusive mutability of the chunk by the split.
431-
Some((unsafe { &mut *(first.as_mut_ptr().cast::<[T; N]>()) }, tail))
432-
}
419+
// SAFETY: We explicitly check for the correct number of elements,
420+
// do not let the reference outlive the slice,
421+
// and enforce exclusive mutability of the chunk by the split.
422+
Some((unsafe { &mut *(first.as_mut_ptr().cast::<[T; N]>()) }, tail))
433423
}
434424

435425
/// Returns an array reference to the last `N` items in the slice and the remaining slice.
@@ -452,16 +442,12 @@ impl<T> [T] {
452442
#[stable(feature = "slice_first_last_chunk", since = "1.77.0")]
453443
#[rustc_const_stable(feature = "slice_first_last_chunk", since = "1.77.0")]
454444
pub const fn split_last_chunk<const N: usize>(&self) -> Option<(&[T], &[T; N])> {
455-
if self.len() < N {
456-
None
457-
} else {
458-
// SAFETY: We manually verified the bounds of the split.
459-
let (init, last) = unsafe { self.split_at_unchecked(self.len() - N) };
445+
let Some(index) = self.len().checked_sub(N) else { return None };
446+
let (init, last) = self.split_at(index);
460447

461-
// SAFETY: We explicitly check for the correct number of elements,
462-
// and do not let the references outlive the slice.
463-
Some((init, unsafe { &*(last.as_ptr().cast::<[T; N]>()) }))
464-
}
448+
// SAFETY: We explicitly check for the correct number of elements,
449+
// and do not let the references outlive the slice.
450+
Some((init, unsafe { &*(last.as_ptr().cast::<[T; N]>()) }))
465451
}
466452

467453
/// Returns a mutable array reference to the last `N` items in the slice and the remaining
@@ -489,17 +475,13 @@ impl<T> [T] {
489475
pub const fn split_last_chunk_mut<const N: usize>(
490476
&mut self,
491477
) -> Option<(&mut [T], &mut [T; N])> {
492-
if self.len() < N {
493-
None
494-
} else {
495-
// SAFETY: We manually verified the bounds of the split.
496-
let (init, last) = unsafe { self.split_at_mut_unchecked(self.len() - N) };
478+
let Some(index) = self.len().checked_sub(N) else { return None };
479+
let (init, last) = self.split_at_mut(index);
497480

498-
// SAFETY: We explicitly check for the correct number of elements,
499-
// do not let the reference outlive the slice,
500-
// and enforce exclusive mutability of the chunk by the split.
501-
Some((init, unsafe { &mut *(last.as_mut_ptr().cast::<[T; N]>()) }))
502-
}
481+
// SAFETY: We explicitly check for the correct number of elements,
482+
// do not let the reference outlive the slice,
483+
// and enforce exclusive mutability of the chunk by the split.
484+
Some((init, unsafe { &mut *(last.as_mut_ptr().cast::<[T; N]>()) }))
503485
}
504486

505487
/// Returns an array reference to the last `N` items in the slice.
@@ -522,17 +504,13 @@ impl<T> [T] {
522504
#[stable(feature = "slice_first_last_chunk", since = "1.77.0")]
523505
#[rustc_const_stable(feature = "const_slice_last_chunk", since = "1.80.0")]
524506
pub const fn last_chunk<const N: usize>(&self) -> Option<&[T; N]> {
525-
if self.len() < N {
526-
None
527-
} else {
528-
// SAFETY: We manually verified the bounds of the slice.
529-
// FIXME(const-hack): Without const traits, we need this instead of `get_unchecked`.
530-
let last = unsafe { self.split_at_unchecked(self.len() - N).1 };
507+
// FIXME(const-hack): Without const traits, we need this instead of `get`.
508+
let Some(index) = self.len().checked_sub(N) else { return None };
509+
let (_, last) = self.split_at(index);
531510

532-
// SAFETY: We explicitly check for the correct number of elements,
533-
// and do not let the references outlive the slice.
534-
Some(unsafe { &*(last.as_ptr().cast::<[T; N]>()) })
535-
}
511+
// SAFETY: We explicitly check for the correct number of elements,
512+
// and do not let the references outlive the slice.
513+
Some(unsafe { &*(last.as_ptr().cast::<[T; N]>()) })
536514
}
537515

538516
/// Returns a mutable array reference to the last `N` items in the slice.
@@ -556,18 +534,14 @@ impl<T> [T] {
556534
#[stable(feature = "slice_first_last_chunk", since = "1.77.0")]
557535
#[rustc_const_stable(feature = "const_slice_first_last_chunk", since = "1.83.0")]
558536
pub const fn last_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]> {
559-
if self.len() < N {
560-
None
561-
} else {
562-
// SAFETY: We manually verified the bounds of the slice.
563-
// FIXME(const-hack): Without const traits, we need this instead of `get_unchecked`.
564-
let last = unsafe { self.split_at_mut_unchecked(self.len() - N).1 };
565-
566-
// SAFETY: We explicitly check for the correct number of elements,
567-
// do not let the reference outlive the slice,
568-
// and require exclusive access to the entire slice to mutate the chunk.
569-
Some(unsafe { &mut *(last.as_mut_ptr().cast::<[T; N]>()) })
570-
}
537+
// FIXME(const-hack): Without const traits, we need this instead of `get`.
538+
let Some(index) = self.len().checked_sub(N) else { return None };
539+
let (_, last) = self.split_at_mut(index);
540+
541+
// SAFETY: We explicitly check for the correct number of elements,
542+
// do not let the reference outlive the slice,
543+
// and require exclusive access to the entire slice to mutate the chunk.
544+
Some(unsafe { &mut *(last.as_mut_ptr().cast::<[T; N]>()) })
571545
}
572546

573547
/// Returns a reference to an element or subslice depending on the type of

0 commit comments

Comments
 (0)