rust-lang/rust · #162289

alloc: a bunch of safety comments

nia-e · merged Sep 5, 20264 files · 93 + / 44
library/alloc/src/boxed.rs29 + / 12
@@ -923,7 +923,7 @@ impl<T> Box<[T]> {     #[stable(feature = "new_uninit", since = "1.82.0")]     #[must_use]     pub fn new_uninit_slice(len: usize) -> Box<[mem::MaybeUninit<T>]> {-        // ignore-tidy-undocumented-unsafe+        // SAFETY: `len` is exactly the capacity of this `RawVec`.         unsafe { RawVec::with_capacity(len).into_box(len) }     } @@ -947,7 +947,7 @@ impl<T> Box<[T]> {     #[stable(feature = "new_zeroed_alloc", since = "1.92.0")]     #[must_use]     pub fn new_zeroed_slice(len: usize) -> Box<[mem::MaybeUninit<T>]> {-        // ignore-tidy-undocumented-unsafe+        // SAFETY: `len` is exactly the capacity of this `RawVec`.         unsafe { RawVec::with_capacity_zeroed(len).into_box(len) }     } @@ -981,7 +981,10 @@ impl<T> Box<[T]> {             };             Global.allocate(layout)?.cast()         };-        // ignore-tidy-undocumented-unsafe+        // SAFETY: `ptr` was just allocated with `Global` with the layout for an array of length+        // `len`, and the layout creation would have failed if `len` overflowed an isize.+        // `into_box` is sound to call since `len` corresponds to the length of the just-created+        // `RawVec`.         unsafe { Ok(RawVec::from_raw_parts_in(ptr.as_ptr(), len, Global).into_box(len)) }     } @@ -1016,7 +1019,10 @@ impl<T> Box<[T]> {             };             Global.allocate_zeroed(layout)?.cast()         };-        // ignore-tidy-undocumented-unsafe+        // SAFETY: `ptr` was just allocated with `Global` with the layout for an array of length+        // `len`, and the layout creation would have failed if `len` overflowed an isize.+        // `into_box` is sound to call since `len` corresponds to the length of the just-created+        // `RawVec`.         unsafe { Ok(RawVec::from_raw_parts_in(ptr.as_ptr(), len, Global).into_box(len)) }     } }@@ -1044,7 +1050,7 @@ impl<T, A: Allocator> Box<[T], A> {     #[unstable(feature = "allocator_api", issue = "32838")]     #[must_use]     pub fn new_uninit_slice_in(len: usize, alloc: A) -> Box<[mem::MaybeUninit<T>], A> {-        // ignore-tidy-undocumented-unsafe+        // SAFETY: `len` is exactly the capacity of this `RawVec`.         unsafe { RawVec::with_capacity_in(len, alloc).into_box(len) }     } @@ -1072,7 +1078,7 @@ impl<T, A: Allocator> Box<[T], A> {     #[unstable(feature = "allocator_api", issue = "32838")]     #[must_use]     pub fn new_zeroed_slice_in(len: usize, alloc: A) -> Box<[mem::MaybeUninit<T>], A> {-        // ignore-tidy-undocumented-unsafe+        // SAFETY: `len` is exactly the capacity of this `RawVec`.         unsafe { RawVec::with_capacity_zeroed_in(len, alloc).into_box(len) }     } @@ -1111,7 +1117,10 @@ impl<T, A: Allocator> Box<[T], A> {             };             alloc.allocate(layout)?.cast()         };-        // ignore-tidy-undocumented-unsafe+        // SAFETY: `ptr` was just allocated with `alloc` with the layout for an array of length+        // `len`, and the layout creation would have failed if `len` overflowed an isize.+        // `into_box` is sound to call since `len` corresponds to the length of the just-created+        // `RawVec`.         unsafe { Ok(RawVec::from_raw_parts_in(ptr.as_ptr(), len, alloc).into_box(len)) }     } @@ -1151,7 +1160,10 @@ impl<T, A: Allocator> Box<[T], A> {             };             alloc.allocate_zeroed(layout)?.cast()         };-        // ignore-tidy-undocumented-unsafe+        // SAFETY: `ptr` was just allocated with `alloc` with the layout for an array of length+        // `len`, and the layout creation would have failed if `len` overflowed an isize.+        // `into_box` is sound to call since `len` corresponds to the length of the just-created+        // `RawVec`.         unsafe { Ok(RawVec::from_raw_parts_in(ptr.as_ptr(), len, alloc).into_box(len)) }     } @@ -2013,10 +2025,15 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Box<T, A> {          let ptr = self.0; -        // ignore-tidy-undocumented-unsafe-        unsafe {-            let layout = Layout::for_value_raw(ptr.as_ptr());-            if layout.size() != 0 {+        // SAFETY: The construction site of the unsized box had ensured for us that the+        // allocation was made with a valid layout (the size does not overflow an isize,+        // possibly because the size of the type is 0).+        let layout = unsafe { Layout::for_value_raw(ptr.as_ptr()) };+        if layout.size() != 0 {+            // SAFETY: Any nonzero allocation would have been created with the allocator+            // of this box and `layout` would fit that allocation. We also are the only ones+            // responsible for doing this deallocation and know that the pointer must be valid.+            unsafe {                 self.1.deallocate(From::from(ptr.cast()), layout);             }         }
library/alloc/src/boxed/thin.rs11 + / 8
@@ -167,7 +167,7 @@ impl<T: ?Sized> Drop for ThinBox<T> {     fn drop(&mut self) {         let value = self.deref_mut();         let value = value as *mut T;-        // ignore-tidy-undocumented-unsafe+        // SAFETY: `value` is valid for reads and writes for our `T`.         unsafe {             self.with_header().drop::<T>(value);         }@@ -249,7 +249,7 @@ impl<H> WithHeader<H> {             debug_assert!(value_offset == 0 && T::IS_ZST && H::IS_ZST);             layout.dangling_ptr()         } else {-            // ignore-tidy-undocumented-unsafe+            // SAFETY: We check above that the layout size is nonzero.             let ptr = unsafe { alloc::alloc(layout) };             if ptr.is_null() {                 alloc::handle_alloc_error(layout);@@ -265,7 +265,8 @@ impl<H> WithHeader<H> {          let result = WithHeader(ptr, PhantomData); -        // ignore-tidy-undocumented-unsafe+        // SAFETY: `result.header()` promises to give us a valid place for writing+        // the header, and `result.value()` promises the same for the value.         unsafe {             ptr::write(result.header(), header);             ptr::write(result.value().cast(), value);@@ -291,7 +292,7 @@ impl<H> WithHeader<H> {             debug_assert!(value_offset == 0 && T::IS_ZST && H::IS_ZST);             layout.dangling_ptr()         } else {-            // ignore-tidy-undocumented-unsafe+            // SAFETY: We check above that the layout size is nonzero.             let ptr = unsafe { alloc::alloc(layout) };             if ptr.is_null() {                 return Err(core::alloc::AllocError);@@ -308,7 +309,8 @@ impl<H> WithHeader<H> {          let result = WithHeader(ptr, PhantomData); -        // ignore-tidy-undocumented-unsafe+        // SAFETY: `result.header()` promises to give us a valid place for writing+        // the header, and `result.value()` promises the same for the value.         unsafe {             ptr::write(result.header(), header);             ptr::write(result.value().cast(), value);@@ -368,9 +370,10 @@ impl<H> WithHeader<H> {         WithHeader(NonNull::new(value_ptr.cast()).unwrap(), PhantomData)     } -    // Safety:-    // - Assumes that either `value` can be dereferenced, or is the-    //   `NonNull::dangling()` we use when both `T` and `H` are ZSTs.+    /// # Safety+    ///+    /// `value` must point to an undropped owned `T`, and `self` must not be+    /// accessed again after this is called.     unsafe fn drop<T: ?Sized>(&self, value: *mut T) {         struct DropGuard<H> {             ptr: NonNull<u8>,
library/alloc/src/raw_vec/mod.rs30 + / 18
@@ -245,11 +245,17 @@ impl<T, A: Allocator> RawVec<T, A> {         );          let me = ManuallyDrop::new(self);-        // ignore-tidy-undocumented-unsafe-        unsafe {-            let slice = me.ptr().cast::<MaybeUninit<T>>().cast_slice(len);-            Box::from_raw_in(slice, ptr::read(&me.inner.alloc))-        }+        let slice = me.ptr().cast::<MaybeUninit<T>>().cast_slice(len);+        // SAFETY: `slice` is a valid pointer for `len` `T`s, and the+        // above `ManuallyDrop` ensures that the destructor of `me` which+        // would free the allocation is never run. The caller upholds that+        // `len` meets or exceeds the last requested capacity, ensuring that+        // the layout generated when dropping the resulting `Box` fits the+        // allocation the `RawVec` created.+        //+        // Moving the allocator out of `me.inner` is also sound since it is+        // never accessed after this point.+        unsafe { Box::from_raw_in(slice, ptr::read(&me.inner.alloc)) }     }      /// Reconstitutes a `RawVec` from a pointer, capacity, and allocator.@@ -438,7 +444,7 @@ const impl<A: [const] Allocator + [const] Destruct> RawVecInner<A> {     fn with_capacity_in(capacity: usize, alloc: A, elem_layout: Layout) -> Self {         match Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc, elem_layout) {             Ok(this) => {-                // ignore-tidy-undocumented-unsafe+                // SAFETY: We already allocated at least `capacity`.                 unsafe {                     // Make it more obvious that a subsequent Vec::reserve(capacity) will not allocate.                     hint::assert_unchecked(!this.needs_to_grow(0, capacity, elem_layout));@@ -482,7 +488,8 @@ const impl<A: [const] Allocator + [const] Destruct> RawVecInner<A> {         // here should change to `ptr.len() / size_of::<T>()`.         Ok(Self {             ptr: Unique::from(ptr.cast()),-            // ignore-tidy-undocumented-unsafe+            // SAFETY: We return early if `T` is a ZST, and if `capacity` would+            // overflow an isize layout creation would have returned early as well.             cap: unsafe { Cap::new_unchecked(capacity) },             alloc,         })@@ -554,7 +561,7 @@ const impl<A: [const] Allocator + [const] Destruct> RawVecInner<A> {     ) -> Result<NonNull<[u8]>, TryReserveError> {         let new_layout = layout_array(cap, elem_layout)?; -        // ignore-tidy-undocumented-unsafe+        // SAFETY: Upheld by caller.         let memory = if let Some((ptr, old_layout)) = unsafe { self.current_memory(elem_layout) } {             // FIXME(const-hack): switch to `debug_assert_eq`             debug_assert!(old_layout.align() == new_layout.align());@@ -644,7 +651,7 @@ impl<A: Allocator> RawVecInner<A> {             // and could hypothetically handle differences between stride and size, but this memory             // has already been allocated so we know it can't overflow and currently Rust does not             // support such types. So we can do better by skipping some checks and avoid an unwrap.-            // ignore-tidy-undocumented-unsafe+            // SAFETY: Upheld by caller, unless the element size is 0 which is checked against.             unsafe {                 let alloc_size = elem_layout.size().unchecked_mul(self.cap.as_inner());                 let layout = Layout::from_size_align_unchecked(alloc_size, elem_layout.align());@@ -678,7 +685,8 @@ impl<A: Allocator> RawVecInner<A> {         }          if self.needs_to_grow(len, additional, elem_layout) {-            // ignore-tidy-undocumented-unsafe+            // SAFETY: `needs_to_grow` ensures that `len + additional` is greater than+            // the current capacity, with the other preconditions upheld by our caller.             unsafe {                 do_reserve_and_handle(self, len, additional, elem_layout);             }@@ -701,7 +709,7 @@ impl<A: Allocator> RawVecInner<A> {                 self.grow_amortized(len, additional, elem_layout)?;             }         }-        // ignore-tidy-undocumented-unsafe+        // SAFETY: If we've already grown, we will not need to again immediately after.         unsafe {             // Inform the optimizer that the reservation has succeeded or wasn't needed             hint::assert_unchecked(!self.needs_to_grow(len, additional, elem_layout));@@ -737,7 +745,7 @@ impl<A: Allocator> RawVecInner<A> {                 self.grow_exact(len, additional, elem_layout)?;             }         }-        // ignore-tidy-undocumented-unsafe+        // SAFETY: If we've already grown, we will not need to again immediately after.         unsafe {             // Inform the optimizer that the reservation has succeeded or wasn't needed             hint::assert_unchecked(!self.needs_to_grow(len, additional, elem_layout));@@ -838,7 +846,8 @@ impl<A: Allocator> RawVecInner<A> {     /// big for LLVM to be willing to inline.     ///     /// # Safety-    /// `cap <= self.capacity()`+    /// - `cap <= self.capacity()`+    /// - `elem_layout` must be valid for `self`.     unsafe fn shrink_unchecked(         &mut self,         cap: usize,@@ -853,17 +862,20 @@ impl<A: Allocator> RawVecInner<A> {         // for the T::IS_ZST case since current_memory() will have returned         // None.         if cap == 0 {-            // ignore-tidy-undocumented-unsafe+            // SAFETY: T isn't a ZST if we're here and `ptr` is our pointer that `current_memory`+            // ensures was allocated with `layout`.             unsafe { self.alloc.deallocate(ptr, layout) };             self.ptr =-                // ignore-tidy-undocumented-unsafe+                // SAFETY: Alignment is guaranteed to be nonzero.                 unsafe { Unique::new_unchecked(ptr::without_provenance_mut(elem_layout.align())) };             self.cap = ZERO_CAP;         } else {-            // ignore-tidy-undocumented-unsafe+            // SAFETY: `cap` is less than the previous capacity, which must have fit in an+            // isize already for the non-ZST case. `shrink` is also sound to call since+            // `current_memory` ensures `ptr` and `layout` are correct for the old allocation,+            // while `new_layout` is computed with a smaller size than the old one per the+            // requirement we instate on our callers.             let ptr = unsafe {-                // Layout cannot overflow here because it would have-                // overflowed earlier when capacity was larger.                 let new_size = elem_layout.size().unchecked_mul(cap);                 let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());                 self.alloc
library/alloc/src/slice.rs23 + / 6
@@ -481,7 +481,10 @@ impl<T> [T] {     pub const fn into_vec<A: Allocator>(self: Box<Self, A>) -> Vec<T, A> {         let len = self.len();         let (b, alloc) = Box::into_raw_with_allocator(self);-        // ignore-tidy-undocumented-unsafe+        // SAFETY: `b` is currently allocated with `alloc` and was allocated with the+        // matching layout for an array of `T * len`, the length is equal to the capacity,+        // and the existence of a `Box<[T]>` is proof that the first `len` elements are+        // valid `T`s.         unsafe { Vec::from_raw_parts_in(b as *mut T, len, len, alloc) }     } @@ -530,17 +533,24 @@ impl<T> [T] {             // If `m > 0`, there are remaining bits up to the leftmost '1'.             while m > 0 {                 // `buf.extend(buf)`:-                // ignore-tidy-undocumented-unsafe+                // SAFETY: We're copying `len` elements after offsetting by `len`,+                // with the previous call to `extend` ensuring that the first `len`+                // elements are valid `T`s and the call to `with_capacity` ensuring+                // we have `len * n` space to write the new elements.+                // Each iteration of this loop doubles the number of initialised elements,+                // which is tracked via `m` - when `m == 0`, we've written `most_significant_bit(n)`+                // elements to the buffer.                 unsafe {                     ptr::copy_nonoverlapping::<T>(                         buf.as_ptr(),                         (buf.as_mut_ptr()).add(buf.len()),                         buf.len(),                     );-                    // `buf` has capacity of `self.len() * n`.-                    let buf_len = buf.len();-                    buf.set_len(buf_len * 2);                 }+                // `buf` has capacity of `self.len() * n`.+                let buf_len = buf.len();+                // SAFETY: We initialised another `buf_len` elements above.+                unsafe { buf.set_len(buf_len * 2) };                  m >>= 1;             }@@ -551,7 +561,14 @@ impl<T> [T] {         let rem_len = capacity - buf.len(); // `self.len() * rem`         if rem_len > 0 {             // `buf.extend(buf[0 .. rem_len])`:-            // ignore-tidy-undocumented-unsafe+            // SAFETY: We're copying `rem_len` elements after offsetting by `len`. The previous+            // looping `copy_nonoverlapping` always doubled the number of instantiated elements,+            // and so if `rem_len` was greater than `len` it would have allowed for another such+            // doubling, until such time that `rem_len < len`. Thus, the space for these remaining+            // `rem_len` elements must be preceded by more than `rem_len` previously-copied+            // elements.+            // Setting the length is correct since we've initialised the whole `capacity`-length+            // space with copies of the previous `len` elements.             unsafe {                 // This is non-overlapping since `2^expn > rem`.                 ptr::copy_nonoverlapping::<T>(