Skip to content

Commit 3a709fe

Browse files
committed
Add precisions about ZSTs and fix nits raised in review
1 parent 92b1975 commit 3a709fe

File tree

1 file changed

+62
-20
lines changed

1 file changed

+62
-20
lines changed

library/core/src/slice/mod.rs

+62-20
Original file line numberDiff line numberDiff line change
@@ -512,15 +512,15 @@ impl<T> [T] {
512512
#[stable(feature = "rust1", since = "1.0.0")]
513513
#[inline]
514514
pub fn swap(&mut self, a: usize, b: usize) {
515+
// Can't take two mutable loans from one vector, so instead just cast
516+
// them to their raw pointers to do the swap.
517+
let pa: *mut T = &mut self[a];
518+
let pb: *mut T = &mut self[b];
515519
// SAFETY: `pa` and `pb` have been created from safe mutable references and refer
516520
// to elements in the slice and therefore are guaranteed to be valid and aligned.
517521
// Note that accessing the elements behind `a` and `b` is checked and will
518522
// panic when out of bounds.
519523
unsafe {
520-
// Can't take two mutable loans from one vector, so instead just cast
521-
// them to their raw pointers to do the swap
522-
let pa: *mut T = &mut self[a];
523-
let pb: *mut T = &mut self[b];
524524
ptr::swap(pa, pb);
525525
}
526526
}
@@ -559,15 +559,21 @@ impl<T> [T] {
559559
// Use the llvm.bswap intrinsic to reverse u8s in a usize
560560
let chunk = mem::size_of::<usize>();
561561
while i + chunk - 1 < ln / 2 {
562-
// SAFETY: An unaligned u32 can be read from `i` if `i + 1 < ln`
563-
// (and obviously `i < ln`), because each element is 2 bytes and
564-
// we're reading 4.
562+
// SAFETY: An unaligned usize can be read from `i` if `i + 1 < ln`
563+
// (and obviously `i < ln`), because each element is 1 byte and
564+
// we're reading 2.
565+
//
565566
// `i + chunk - 1 < ln / 2` # while condition
566567
// `i + 2 - 1 < ln / 2`
567568
// `i + 1 < ln / 2`
569+
//
568570
// Since it's less than the length divided by 2, then it must be
569571
// in bounds.
570572
//
573+
// This also means that the condition `0 < i + chunk <= ln` is
574+
// always respected, ensuring the `pb` pointer can be used
575+
// safely.
576+
//
571577
// Note: when updating this comment, update the others in the
572578
// function too.
573579
unsafe {
@@ -589,12 +595,18 @@ impl<T> [T] {
589595
// SAFETY: An unaligned u32 can be read from `i` if `i + 1 < ln`
590596
// (and obviously `i < ln`), because each element is 2 bytes and
591597
// we're reading 4.
598+
//
592599
// `i + chunk - 1 < ln / 2` # while condition
593600
// `i + 2 - 1 < ln / 2`
594601
// `i + 1 < ln / 2`
602+
//
595603
// Since it's less than the length divided by 2, then it must be
596604
// in bounds.
597605
//
606+
// This also means that the condition `0 < i + chunk <= ln` is
607+
// always respected, ensuring the `pb` pointer can be used
608+
// safely.
609+
//
598610
// Note: when updating this comment, update the others in the
599611
// function too.
600612
unsafe {
@@ -641,11 +653,23 @@ impl<T> [T] {
641653
#[stable(feature = "rust1", since = "1.0.0")]
642654
#[inline]
643655
pub fn iter(&self) -> Iter<'_, T> {
644-
// SAFETY: adding `self.len()` to the starting pointer gives a pointer
645-
// at the end of `self`, which fulfills the expectations of `ptr.add()`
646-
// and `NonNull::new_unchecked()`.
656+
let ptr = self.as_ptr();
657+
// SAFETY: There are several things here:
658+
//
659+
// `ptr` has been checked for nullity before being passed to `NonNull` via
660+
// `new_unchecked`.
661+
//
662+
// Adding `self.len()` to the starting pointer gives a pointer
663+
// at the end of `self`. `end` will never be dereferenced, only checked
664+
// for direct pointer equality with `ptr` to check if the iterator is
665+
// done.
666+
//
667+
// In the case of a ZST, the end pointer is just the start pointer plus
668+
// the length, to also allows for the fast `ptr == end` check.
669+
//
670+
// See the `next_unchecked!` and `is_empty!` macros as well as the
671+
// `post_inc_start` method for more informations.
647672
unsafe {
648-
let ptr = self.as_ptr();
649673
assume(!ptr.is_null());
650674

651675
let end = if mem::size_of::<T>() == 0 {
@@ -672,11 +696,23 @@ impl<T> [T] {
672696
#[stable(feature = "rust1", since = "1.0.0")]
673697
#[inline]
674698
pub fn iter_mut(&mut self) -> IterMut<'_, T> {
675-
// SAFETY: adding `self.len()` to the starting pointer gives a pointer
676-
// at the end of `self`, which fulfills the expectations of `ptr.add()`
677-
// and `NonNull::new_unchecked()`.
699+
let ptr = self.as_mut_ptr();
700+
// SAFETY: There are several things here:
701+
//
702+
// `ptr` has been checked for nullity before being passed to `NonNull` via
703+
// `new_unchecked`.
704+
//
705+
// Adding `self.len()` to the starting pointer gives a pointer
706+
// at the end of `self`. `end` will never be dereferenced, only checked
707+
// for direct pointer equality with `ptr` to check if the iterator is
708+
// done.
709+
//
710+
// In the case of a ZST, the end pointer is just the start pointer plus
711+
// the length, to also allows for the fast `ptr == end` check.
712+
//
713+
// See the `next_unchecked!` and `is_empty!` macros as well as the
714+
// `post_inc_start` method for more informations.
678715
unsafe {
679-
let ptr = self.as_mut_ptr();
680716
assume(!ptr.is_null());
681717

682718
let end = if mem::size_of::<T>() == 0 {
@@ -2170,6 +2206,11 @@ impl<T> [T] {
21702206
//
21712207
// `next_write` is also incremented at most once per loop at most meaning
21722208
// no element is skipped when it may need to be swapped.
2209+
//
2210+
// `ptr_read` and `prev_ptr_write` never point to the same element. This
2211+
// is required for `&mut *ptr_read`, `&mut *prev_ptr_write` to be safe.
2212+
// The explanation is simply that `next_read >= next_write` is always true,
2213+
// thus `next_read > next_write - 1` is too.
21732214
unsafe {
21742215
// Avoid bounds checks by using raw pointers.
21752216
while next_read < len {
@@ -2253,11 +2294,11 @@ impl<T> [T] {
22532294
pub fn rotate_left(&mut self, mid: usize) {
22542295
assert!(mid <= self.len());
22552296
let k = self.len() - mid;
2297+
let p = self.as_mut_ptr();
22562298

2257-
// SAFETY: `[mid - mid;mid+k]` corresponds to the entire
2299+
// SAFETY: `[mid; mid+k]` corresponds to the entire
22582300
// `self` slice, thus is valid for reads and writes.
22592301
unsafe {
2260-
let p = self.as_mut_ptr();
22612302
rotate::ptr_rotate(mid, p.add(mid), k);
22622303
}
22632304
}
@@ -2296,11 +2337,11 @@ impl<T> [T] {
22962337
pub fn rotate_right(&mut self, k: usize) {
22972338
assert!(k <= self.len());
22982339
let mid = self.len() - k;
2340+
let p = self.as_mut_ptr();
22992341

2300-
// SAFETY: `[mid - mid;mid+k]` corresponds to the entire
2342+
// SAFETY: `[mid; mid+k]` corresponds to the entire
23012343
// `self` slice, thus is valid for reads and writes.
23022344
unsafe {
2303-
let p = self.as_mut_ptr();
23042345
rotate::ptr_rotate(mid, p.add(mid), k);
23052346
}
23062347
}
@@ -2517,7 +2558,8 @@ impl<T> [T] {
25172558
assert!(src_end <= self.len(), "src is out of bounds");
25182559
let count = src_end - src_start;
25192560
assert!(dest <= self.len() - count, "dest is out of bounds");
2520-
// SAFETY: the conditions for `ptr::copy` have all been checked above.
2561+
// SAFETY: the conditions for `ptr::copy` have all been checked above,
2562+
// as have those for `ptr::add`.
25212563
unsafe {
25222564
ptr::copy(self.as_ptr().add(src_start), self.as_mut_ptr().add(dest), count);
25232565
}

0 commit comments

Comments
 (0)