From dfdbf6343ae1ea97b504b36ec1933e181ec7198e Mon Sep 17 00:00:00 2001 From: binarycat Date: Fri, 6 Sep 2024 16:28:22 -0400 Subject: [PATCH 01/10] properly handle EOF in BufReader::peek previously this would cause an infinite loop due to it being unable to read `n` bytes. --- library/std/src/io/buffered/bufreader.rs | 12 ++++++++++-- library/std/src/io/buffered/bufreader/buffer.rs | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index cf226bd28d005..b69d7edb5a714 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -99,7 +99,10 @@ impl BufReader { impl BufReader { /// Attempt to look ahead `n` bytes. /// - /// `n` must be less than `capacity`. + /// `n` must be less than or equal to `capacity`. + /// + /// the returned slice may be less than `n` bytes long if + /// end of file is reached. /// /// ## Examples /// @@ -117,6 +120,7 @@ impl BufReader { /// let mut s = String::new(); /// rdr.read_to_string(&mut s).unwrap(); /// assert_eq!(&s, "hello"); + /// assert_eq!(rdr.peek(1).unwrap().len(), 0); /// ``` #[unstable(feature = "bufreader_peek", issue = "128405")] pub fn peek(&mut self, n: usize) -> io::Result<&[u8]> { @@ -125,7 +129,11 @@ impl BufReader { if self.buf.pos() > 0 { self.buf.backshift(); } - self.buf.read_more(&mut self.inner)?; + let new = self.buf.read_more(&mut self.inner)?; + if new == 0 { + // end of file, no more bytes to read + return Ok(&self.buf.buffer()[..]); + } debug_assert_eq!(self.buf.pos(), 0); } Ok(&self.buf.buffer()[..n]) diff --git a/library/std/src/io/buffered/bufreader/buffer.rs b/library/std/src/io/buffered/bufreader/buffer.rs index ccd67fafb45b4..1bf84d8bef312 100644 --- a/library/std/src/io/buffered/bufreader/buffer.rs +++ b/library/std/src/io/buffered/bufreader/buffer.rs @@ -98,7 +98,7 @@ impl Buffer { } /// Read more bytes into the buffer without discarding any of its contents - pub fn read_more(&mut self, mut reader: impl Read) -> io::Result<()> { + pub fn read_more(&mut self, mut reader: impl Read) -> io::Result { let mut buf = BorrowedBuf::from(&mut self.buf[self.pos..]); let old_init = self.initialized - self.pos; unsafe { @@ -107,7 +107,7 @@ impl Buffer { reader.read_buf(buf.unfilled())?; self.filled += buf.len(); self.initialized += buf.init_len() - old_init; - Ok(()) + Ok(buf.len()) } /// Remove bytes that have already been read from the buffer. From 8e3e20ac2f278d79bc7ee72e5c43eb561e6d751e Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas) Chirananthavat" Date: Fri, 6 Sep 2024 22:55:36 +0700 Subject: [PATCH 02/10] Add `NonNull` convenience methods to `Box` --- library/alloc/src/boxed.rs | 223 +++++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index a924feaf15f2a..f265b7b6f26ec 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1059,6 +1059,59 @@ impl Box { pub unsafe fn from_raw(raw: *mut T) -> Self { unsafe { Self::from_raw_in(raw, Global) } } + + /// Constructs a box from a `NonNull` pointer. + /// + /// After calling this function, the `NonNull` pointer is owned by + /// the resulting `Box`. Specifically, the `Box` destructor will call + /// the destructor of `T` and free the allocated memory. For this + /// to be safe, the memory must have been allocated in accordance + /// with the [memory layout] used by `Box` . + /// + /// # Safety + /// + /// This function is unsafe because improper use may lead to + /// memory problems. For example, a double-free may occur if the + /// function is called twice on the same `NonNull` pointer. + /// + /// The safety conditions are described in the [memory layout] section. + /// + /// # Examples + /// + /// Recreate a `Box` which was previously converted to a `NonNull` + /// pointer using [`Box::into_non_null`]: + /// ``` + /// #![feature(box_vec_non_null)] + /// + /// let x = Box::new(5); + /// let non_null = Box::into_non_null(x); + /// let x = unsafe { Box::from_non_null(non_null) }; + /// ``` + /// Manually create a `Box` from scratch by using the global allocator: + /// ``` + /// #![feature(box_vec_non_null)] + /// + /// use std::alloc::{alloc, Layout}; + /// use std::ptr::NonNull; + /// + /// unsafe { + /// let non_null = NonNull::new(alloc(Layout::new::()).cast::()) + /// .expect("allocation failed"); + /// // In general .write is required to avoid attempting to destruct + /// // the (uninitialized) previous contents of `non_null`. + /// non_null.write(5); + /// let x = Box::from_non_null(non_null); + /// } + /// ``` + /// + /// [memory layout]: self#memory-layout + /// [`Layout`]: crate::Layout + #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "none")] + #[inline] + #[must_use = "call `drop(Box::from_non_null(ptr))` if you intend to drop the `Box`"] + pub unsafe fn from_non_null(ptr: NonNull) -> Self { + unsafe { Self::from_raw(ptr.as_ptr()) } + } } impl Box { @@ -1116,6 +1169,61 @@ impl Box { Box(unsafe { Unique::new_unchecked(raw) }, alloc) } + /// Constructs a box from a `NonNull` pointer in the given allocator. + /// + /// After calling this function, the `NonNull` pointer is owned by + /// the resulting `Box`. Specifically, the `Box` destructor will call + /// the destructor of `T` and free the allocated memory. For this + /// to be safe, the memory must have been allocated in accordance + /// with the [memory layout] used by `Box` . + /// + /// # Safety + /// + /// This function is unsafe because improper use may lead to + /// memory problems. For example, a double-free may occur if the + /// function is called twice on the same raw pointer. + /// + /// + /// # Examples + /// + /// Recreate a `Box` which was previously converted to a `NonNull` pointer + /// using [`Box::into_non_null_with_allocator`]: + /// ``` + /// #![feature(allocator_api, box_vec_non_null)] + /// + /// use std::alloc::System; + /// + /// let x = Box::new_in(5, System); + /// let (non_null, alloc) = Box::into_non_null_with_allocator(x); + /// let x = unsafe { Box::from_non_null_in(non_null, alloc) }; + /// ``` + /// Manually create a `Box` from scratch by using the system allocator: + /// ``` + /// #![feature(allocator_api, box_vec_non_null, slice_ptr_get)] + /// + /// use std::alloc::{Allocator, Layout, System}; + /// + /// unsafe { + /// let non_null = System.allocate(Layout::new::())?.cast::(); + /// // In general .write is required to avoid attempting to destruct + /// // the (uninitialized) previous contents of `non_null`. + /// non_null.write(5); + /// let x = Box::from_non_null_in(non_null, System); + /// } + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + /// + /// [memory layout]: self#memory-layout + /// [`Layout`]: crate::Layout + #[unstable(feature = "allocator_api", issue = "32838")] + // #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "none")] + #[rustc_const_unstable(feature = "const_box", issue = "92521")] + #[inline] + pub const unsafe fn from_non_null_in(raw: NonNull, alloc: A) -> Self { + // SAFETY: guaranteed by the caller. + unsafe { Box::from_raw_in(raw.as_ptr(), alloc) } + } + /// Consumes the `Box`, returning a wrapped raw pointer. /// /// The pointer will be properly aligned and non-null. @@ -1171,6 +1279,66 @@ impl Box { unsafe { addr_of_mut!(*&mut *Self::into_raw_with_allocator(b).0) } } + /// Consumes the `Box`, returning a wrapped `NonNull` pointer. + /// + /// The pointer will be properly aligned. + /// + /// After calling this function, the caller is responsible for the + /// memory previously managed by the `Box`. In particular, the + /// caller should properly destroy `T` and release the memory, taking + /// into account the [memory layout] used by `Box`. The easiest way to + /// do this is to convert the `NonNull` pointer back into a `Box` with the + /// [`Box::from_non_null`] function, allowing the `Box` destructor to + /// perform the cleanup. + /// + /// Note: this is an associated function, which means that you have + /// to call it as `Box::into_non_null(b)` instead of `b.into_non_null()`. + /// This is so that there is no conflict with a method on the inner type. + /// + /// # Examples + /// Converting the `NonNull` pointer back into a `Box` with [`Box::from_non_null`] + /// for automatic cleanup: + /// ``` + /// #![feature(box_vec_non_null)] + /// + /// let x = Box::new(String::from("Hello")); + /// let non_null = Box::into_non_null(x); + /// let x = unsafe { Box::from_non_null(non_null) }; + /// ``` + /// Manual cleanup by explicitly running the destructor and deallocating + /// the memory: + /// ``` + /// #![feature(box_vec_non_null)] + /// + /// use std::alloc::{dealloc, Layout}; + /// + /// let x = Box::new(String::from("Hello")); + /// let non_null = Box::into_non_null(x); + /// unsafe { + /// non_null.drop_in_place(); + /// dealloc(non_null.as_ptr().cast::(), Layout::new::()); + /// } + /// ``` + /// Note: This is equivalent to the following: + /// ``` + /// #![feature(box_vec_non_null)] + /// + /// let x = Box::new(String::from("Hello")); + /// let non_null = Box::into_non_null(x); + /// unsafe { + /// drop(Box::from_non_null(non_null)); + /// } + /// ``` + /// + /// [memory layout]: self#memory-layout + #[must_use = "losing the pointer will leak memory"] + #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "none")] + #[inline] + pub fn into_non_null(b: Self) -> NonNull { + // SAFETY: `Box` is guaranteed to be non-null. + unsafe { NonNull::new_unchecked(Self::into_raw(b)) } + } + /// Consumes the `Box`, returning a wrapped raw pointer and the allocator. /// /// The pointer will be properly aligned and non-null. @@ -1232,6 +1400,61 @@ impl Box { (ptr, alloc) } + /// Consumes the `Box`, returning a wrapped `NonNull` pointer and the allocator. + /// + /// The pointer will be properly aligned. + /// + /// After calling this function, the caller is responsible for the + /// memory previously managed by the `Box`. In particular, the + /// caller should properly destroy `T` and release the memory, taking + /// into account the [memory layout] used by `Box`. The easiest way to + /// do this is to convert the `NonNull` pointer back into a `Box` with the + /// [`Box::from_non_null_in`] function, allowing the `Box` destructor to + /// perform the cleanup. + /// + /// Note: this is an associated function, which means that you have + /// to call it as `Box::into_non_null_with_allocator(b)` instead of + /// `b.into_non_null_with_allocator()`. This is so that there is no + /// conflict with a method on the inner type. + /// + /// # Examples + /// Converting the `NonNull` pointer back into a `Box` with + /// [`Box::from_non_null_in`] for automatic cleanup: + /// ``` + /// #![feature(allocator_api, box_vec_non_null)] + /// + /// use std::alloc::System; + /// + /// let x = Box::new_in(String::from("Hello"), System); + /// let (non_null, alloc) = Box::into_non_null_with_allocator(x); + /// let x = unsafe { Box::from_non_null_in(non_null, alloc) }; + /// ``` + /// Manual cleanup by explicitly running the destructor and deallocating + /// the memory: + /// ``` + /// #![feature(allocator_api, box_vec_non_null)] + /// + /// use std::alloc::{Allocator, Layout, System}; + /// + /// let x = Box::new_in(String::from("Hello"), System); + /// let (non_null, alloc) = Box::into_non_null_with_allocator(x); + /// unsafe { + /// non_null.drop_in_place(); + /// alloc.deallocate(non_null.cast::(), Layout::new::()); + /// } + /// ``` + /// + /// [memory layout]: self#memory-layout + #[must_use = "losing the pointer will leak memory"] + #[unstable(feature = "allocator_api", issue = "32838")] + // #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "none")] + #[inline] + pub fn into_non_null_with_allocator(b: Self) -> (NonNull, A) { + let (ptr, alloc) = Box::into_raw_with_allocator(b); + // SAFETY: `Box` is guaranteed to be non-null. + unsafe { (NonNull::new_unchecked(ptr), alloc) } + } + #[unstable( feature = "ptr_internals", issue = "none", From 8230a90c494dcc7d8013a0a15719cc9f2f9a8263 Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas) Chirananthavat" Date: Sat, 7 Sep 2024 14:43:23 +0700 Subject: [PATCH 03/10] Add `NonNull` convenience methods to `Vec` --- library/alloc/src/vec/in_place_collect.rs | 2 +- library/alloc/src/vec/mod.rs | 320 +++++++++++++++++++++- library/alloc/src/vec/spec_from_iter.rs | 2 +- 3 files changed, 308 insertions(+), 16 deletions(-) diff --git a/library/alloc/src/vec/in_place_collect.rs b/library/alloc/src/vec/in_place_collect.rs index d119e6ca397c5..7322d3f0742e3 100644 --- a/library/alloc/src/vec/in_place_collect.rs +++ b/library/alloc/src/vec/in_place_collect.rs @@ -328,7 +328,7 @@ where mem::forget(dst_guard); - let vec = unsafe { Vec::from_nonnull(dst_buf, len, dst_cap) }; + let vec = unsafe { Vec::from_parts(dst_buf, len, dst_cap) }; vec } diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 162791ba59d03..aa23028cd2934 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -603,15 +603,116 @@ impl Vec { unsafe { Self::from_raw_parts_in(ptr, length, capacity, Global) } } - /// A convenience method for hoisting the non-null precondition out of [`Vec::from_raw_parts`]. + #[doc(alias = "from_non_null_parts")] + /// Creates a `Vec` directly from a `NonNull` pointer, a length, and a capacity. /// /// # Safety /// - /// See [`Vec::from_raw_parts`]. + /// This is highly unsafe, due to the number of invariants that aren't + /// checked: + /// + /// * `ptr` must have been allocated using the global allocator, such as via + /// the [`alloc::alloc`] function. + /// * `T` needs to have the same alignment as what `ptr` was allocated with. + /// (`T` having a less strict alignment is not sufficient, the alignment really + /// needs to be equal to satisfy the [`dealloc`] requirement that memory must be + /// allocated and deallocated with the same layout.) + /// * The size of `T` times the `capacity` (ie. the allocated size in bytes) needs + /// to be the same size as the pointer was allocated with. (Because similar to + /// alignment, [`dealloc`] must be called with the same layout `size`.) + /// * `length` needs to be less than or equal to `capacity`. + /// * The first `length` values must be properly initialized values of type `T`. + /// * `capacity` needs to be the capacity that the pointer was allocated with. + /// * The allocated size in bytes must be no larger than `isize::MAX`. + /// See the safety documentation of [`pointer::offset`]. + /// + /// These requirements are always upheld by any `ptr` that has been allocated + /// via `Vec`. Other allocation sources are allowed if the invariants are + /// upheld. + /// + /// Violating these may cause problems like corrupting the allocator's + /// internal data structures. For example it is normally **not** safe + /// to build a `Vec` from a pointer to a C `char` array with length + /// `size_t`, doing so is only safe if the array was initially allocated by + /// a `Vec` or `String`. + /// It's also not safe to build one from a `Vec` and its length, because + /// the allocator cares about the alignment, and these two types have different + /// alignments. The buffer was allocated with alignment 2 (for `u16`), but after + /// turning it into a `Vec` it'll be deallocated with alignment 1. To avoid + /// these issues, it is often preferable to do casting/transmuting using + /// [`NonNull::slice_from_raw_parts`] instead. + /// + /// The ownership of `ptr` is effectively transferred to the + /// `Vec` which may then deallocate, reallocate or change the + /// contents of memory pointed to by the pointer at will. Ensure + /// that nothing else uses the pointer after calling this + /// function. + /// + /// [`String`]: crate::string::String + /// [`alloc::alloc`]: crate::alloc::alloc + /// [`dealloc`]: crate::alloc::GlobalAlloc::dealloc + /// + /// # Examples + /// + /// ``` + /// #![feature(box_vec_non_null)] + /// + /// use std::ptr::NonNull; + /// use std::mem; + /// + /// let v = vec![1, 2, 3]; + /// + // FIXME Update this when vec_into_raw_parts is stabilized + /// // Prevent running `v`'s destructor so we are in complete control + /// // of the allocation. + /// let mut v = mem::ManuallyDrop::new(v); + /// + /// // Pull out the various important pieces of information about `v` + /// let p = unsafe { NonNull::new_unchecked(v.as_mut_ptr()) }; + /// let len = v.len(); + /// let cap = v.capacity(); + /// + /// unsafe { + /// // Overwrite memory with 4, 5, 6 + /// for i in 0..len { + /// p.add(i).write(4 + i); + /// } + /// + /// // Put everything back together into a Vec + /// let rebuilt = Vec::from_parts(p, len, cap); + /// assert_eq!(rebuilt, [4, 5, 6]); + /// } + /// ``` + /// + /// Using memory that was allocated elsewhere: + /// + /// ```rust + /// #![feature(box_vec_non_null)] + /// + /// use std::alloc::{alloc, Layout}; + /// use std::ptr::NonNull; + /// + /// fn main() { + /// let layout = Layout::array::(16).expect("overflow cannot happen"); + /// + /// let vec = unsafe { + /// let Some(mem) = NonNull::new(alloc(layout).cast::()) else { + /// return; + /// }; + /// + /// mem.write(1_000_000); + /// + /// Vec::from_parts(mem, 1, 16) + /// }; + /// + /// assert_eq!(vec, &[1_000_000]); + /// assert_eq!(vec.capacity(), 16); + /// } + /// ``` #[inline] - #[cfg(not(no_global_oom_handling))] // required by tests/run-make/alloc-no-oom-handling - pub(crate) unsafe fn from_nonnull(ptr: NonNull, length: usize, capacity: usize) -> Self { - unsafe { Self::from_nonnull_in(ptr, length, capacity, Global) } + #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "none")] + pub unsafe fn from_parts(ptr: NonNull, length: usize, capacity: usize) -> Self { + unsafe { Self::from_parts_in(ptr, length, capacity, Global) } } } @@ -830,19 +931,119 @@ impl Vec { unsafe { Vec { buf: RawVec::from_raw_parts_in(ptr, capacity, alloc), len: length } } } - /// A convenience method for hoisting the non-null precondition out of [`Vec::from_raw_parts_in`]. + #[doc(alias = "from_non_null_parts_in")] + /// Creates a `Vec` directly from a `NonNull` pointer, a length, a capacity, + /// and an allocator. /// /// # Safety /// - /// See [`Vec::from_raw_parts_in`]. + /// This is highly unsafe, due to the number of invariants that aren't + /// checked: + /// + /// * `ptr` must be [*currently allocated*] via the given allocator `alloc`. + /// * `T` needs to have the same alignment as what `ptr` was allocated with. + /// (`T` having a less strict alignment is not sufficient, the alignment really + /// needs to be equal to satisfy the [`dealloc`] requirement that memory must be + /// allocated and deallocated with the same layout.) + /// * The size of `T` times the `capacity` (ie. the allocated size in bytes) needs + /// to be the same size as the pointer was allocated with. (Because similar to + /// alignment, [`dealloc`] must be called with the same layout `size`.) + /// * `length` needs to be less than or equal to `capacity`. + /// * The first `length` values must be properly initialized values of type `T`. + /// * `capacity` needs to [*fit*] the layout size that the pointer was allocated with. + /// * The allocated size in bytes must be no larger than `isize::MAX`. + /// See the safety documentation of [`pointer::offset`]. + /// + /// These requirements are always upheld by any `ptr` that has been allocated + /// via `Vec`. Other allocation sources are allowed if the invariants are + /// upheld. + /// + /// Violating these may cause problems like corrupting the allocator's + /// internal data structures. For example it is **not** safe + /// to build a `Vec` from a pointer to a C `char` array with length `size_t`. + /// It's also not safe to build one from a `Vec` and its length, because + /// the allocator cares about the alignment, and these two types have different + /// alignments. The buffer was allocated with alignment 2 (for `u16`), but after + /// turning it into a `Vec` it'll be deallocated with alignment 1. + /// + /// The ownership of `ptr` is effectively transferred to the + /// `Vec` which may then deallocate, reallocate or change the + /// contents of memory pointed to by the pointer at will. Ensure + /// that nothing else uses the pointer after calling this + /// function. + /// + /// [`String`]: crate::string::String + /// [`dealloc`]: crate::alloc::GlobalAlloc::dealloc + /// [*currently allocated*]: crate::alloc::Allocator#currently-allocated-memory + /// [*fit*]: crate::alloc::Allocator#memory-fitting + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api, box_vec_non_null)] + /// + /// use std::alloc::System; + /// + /// use std::ptr::NonNull; + /// use std::mem; + /// + /// let mut v = Vec::with_capacity_in(3, System); + /// v.push(1); + /// v.push(2); + /// v.push(3); + /// + // FIXME Update this when vec_into_raw_parts is stabilized + /// // Prevent running `v`'s destructor so we are in complete control + /// // of the allocation. + /// let mut v = mem::ManuallyDrop::new(v); + /// + /// // Pull out the various important pieces of information about `v` + /// let p = unsafe { NonNull::new_unchecked(v.as_mut_ptr()) }; + /// let len = v.len(); + /// let cap = v.capacity(); + /// let alloc = v.allocator(); + /// + /// unsafe { + /// // Overwrite memory with 4, 5, 6 + /// for i in 0..len { + /// p.add(i).write(4 + i); + /// } + /// + /// // Put everything back together into a Vec + /// let rebuilt = Vec::from_parts_in(p, len, cap, alloc.clone()); + /// assert_eq!(rebuilt, [4, 5, 6]); + /// } + /// ``` + /// + /// Using memory that was allocated elsewhere: + /// + /// ```rust + /// #![feature(allocator_api, box_vec_non_null)] + /// + /// use std::alloc::{AllocError, Allocator, Global, Layout}; + /// + /// fn main() { + /// let layout = Layout::array::(16).expect("overflow cannot happen"); + /// + /// let vec = unsafe { + /// let mem = match Global.allocate(layout) { + /// Ok(mem) => mem.cast::(), + /// Err(AllocError) => return, + /// }; + /// + /// mem.write(1_000_000); + /// + /// Vec::from_parts_in(mem, 1, 16, Global) + /// }; + /// + /// assert_eq!(vec, &[1_000_000]); + /// assert_eq!(vec.capacity(), 16); + /// } + /// ``` #[inline] - #[cfg(not(no_global_oom_handling))] // required by tests/run-make/alloc-no-oom-handling - pub(crate) unsafe fn from_nonnull_in( - ptr: NonNull, - length: usize, - capacity: usize, - alloc: A, - ) -> Self { + #[unstable(feature = "allocator_api", reason = "new API", issue = "32838")] + // #[unstable(feature = "box_vec_non_null", issue = "none")] + pub unsafe fn from_parts_in(ptr: NonNull, length: usize, capacity: usize, alloc: A) -> Self { unsafe { Vec { buf: RawVec::from_nonnull_in(ptr, capacity, alloc), len: length } } } @@ -885,6 +1086,49 @@ impl Vec { (me.as_mut_ptr(), me.len(), me.capacity()) } + #[doc(alias = "into_non_null_parts")] + /// Decomposes a `Vec` into its raw components: `(NonNull pointer, length, capacity)`. + /// + /// Returns the `NonNull` pointer to the underlying data, the length of + /// the vector (in elements), and the allocated capacity of the + /// data (in elements). These are the same arguments in the same + /// order as the arguments to [`from_parts`]. + /// + /// After calling this function, the caller is responsible for the + /// memory previously managed by the `Vec`. The only way to do + /// this is to convert the `NonNull` pointer, length, and capacity back + /// into a `Vec` with the [`from_parts`] function, allowing + /// the destructor to perform the cleanup. + /// + /// [`from_parts`]: Vec::from_parts + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_into_raw_parts, box_vec_non_null)] + /// + /// let v: Vec = vec![-1, 0, 1]; + /// + /// let (ptr, len, cap) = v.into_parts(); + /// + /// let rebuilt = unsafe { + /// // We can now make changes to the components, such as + /// // transmuting the raw pointer to a compatible type. + /// let ptr = ptr.cast::(); + /// + /// Vec::from_parts(ptr, len, cap) + /// }; + /// assert_eq!(rebuilt, [4294967295, 0, 1]); + /// ``` + #[must_use = "losing the pointer will leak memory"] + #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "none")] + // #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")] + pub fn into_parts(self) -> (NonNull, usize, usize) { + let (ptr, len, capacity) = self.into_raw_parts(); + // SAFETY: A `Vec` always has a non-null pointer. + (unsafe { NonNull::new_unchecked(ptr) }, len, capacity) + } + /// Decomposes a `Vec` into its raw components: `(pointer, length, capacity, allocator)`. /// /// Returns the raw pointer to the underlying data, the length of the vector (in elements), @@ -934,6 +1178,54 @@ impl Vec { (ptr, len, capacity, alloc) } + #[doc(alias = "into_non_null_parts_with_alloc")] + /// Decomposes a `Vec` into its raw components: `(NonNull pointer, length, capacity, allocator)`. + /// + /// Returns the `NonNull` pointer to the underlying data, the length of the vector (in elements), + /// the allocated capacity of the data (in elements), and the allocator. These are the same + /// arguments in the same order as the arguments to [`from_parts_in`]. + /// + /// After calling this function, the caller is responsible for the + /// memory previously managed by the `Vec`. The only way to do + /// this is to convert the `NonNull` pointer, length, and capacity back + /// into a `Vec` with the [`from_parts_in`] function, allowing + /// the destructor to perform the cleanup. + /// + /// [`from_parts_in`]: Vec::from_parts_in + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api, vec_into_raw_parts, box_vec_non_null)] + /// + /// use std::alloc::System; + /// + /// let mut v: Vec = Vec::new_in(System); + /// v.push(-1); + /// v.push(0); + /// v.push(1); + /// + /// let (ptr, len, cap, alloc) = v.into_parts_with_alloc(); + /// + /// let rebuilt = unsafe { + /// // We can now make changes to the components, such as + /// // transmuting the raw pointer to a compatible type. + /// let ptr = ptr.cast::(); + /// + /// Vec::from_parts_in(ptr, len, cap, alloc) + /// }; + /// assert_eq!(rebuilt, [4294967295, 0, 1]); + /// ``` + #[must_use = "losing the pointer will leak memory"] + #[unstable(feature = "allocator_api", issue = "32838")] + // #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "none")] + // #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")] + pub fn into_parts_with_alloc(self) -> (NonNull, usize, usize, A) { + let (ptr, len, capacity, alloc) = self.into_raw_parts_with_alloc(); + // SAFETY: A `Vec` always has a non-null pointer. + (unsafe { NonNull::new_unchecked(ptr) }, len, capacity, alloc) + } + /// Returns the total number of elements the vector can hold without /// reallocating. /// diff --git a/library/alloc/src/vec/spec_from_iter.rs b/library/alloc/src/vec/spec_from_iter.rs index 6646ae7bccb7a..e1f0b639bdfd6 100644 --- a/library/alloc/src/vec/spec_from_iter.rs +++ b/library/alloc/src/vec/spec_from_iter.rs @@ -51,7 +51,7 @@ impl SpecFromIter> for Vec { if has_advanced { ptr::copy(it.ptr.as_ptr(), it.buf.as_ptr(), it.len()); } - return Vec::from_nonnull(it.buf, it.len(), it.cap); + return Vec::from_parts(it.buf, it.len(), it.cap); } } From 79b87c57cbddc7ac4a6e9f7381616e77e3f443c5 Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas) Chirananthavat" Date: Sat, 7 Sep 2024 21:06:49 +0700 Subject: [PATCH 04/10] Bless tests due to new method suggestions. --- tests/ui/privacy/suggest-box-new.stderr | 4 ++-- tests/ui/suggestions/deref-path-method.stderr | 2 +- tests/ui/ufcs/bad-builder.stderr | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ui/privacy/suggest-box-new.stderr b/tests/ui/privacy/suggest-box-new.stderr index 8b01e8c3c10ef..1e28b9fbd86bd 100644 --- a/tests/ui/privacy/suggest-box-new.stderr +++ b/tests/ui/privacy/suggest-box-new.stderr @@ -44,7 +44,7 @@ LL | wtf: Some(Box::new_zeroed()), | ~~~~~~~~~~~~~~ LL | wtf: Some(Box::new_in(_, _)), | ~~~~~~~~~~~~~~ - and 10 other candidates + and 12 other candidates help: consider using the `Default` trait | LL | wtf: Some(::default()), @@ -89,7 +89,7 @@ LL | let _ = Box::new_zeroed(); | ~~~~~~~~~~~~~~ LL | let _ = Box::new_in(_, _); | ~~~~~~~~~~~~~~ - and 10 other candidates + and 12 other candidates help: consider using the `Default` trait | LL | let _ = ::default(); diff --git a/tests/ui/suggestions/deref-path-method.stderr b/tests/ui/suggestions/deref-path-method.stderr index b27d9aef06614..bfcc2307fd7fb 100644 --- a/tests/ui/suggestions/deref-path-method.stderr +++ b/tests/ui/suggestions/deref-path-method.stderr @@ -9,7 +9,7 @@ note: if you're trying to build a new `Vec<_, _>` consider using one of the foll Vec::::with_capacity Vec::::try_with_capacity Vec::::from_raw_parts - and 4 others + and 6 others --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL help: the function `contains` is implemented on `[_]` | diff --git a/tests/ui/ufcs/bad-builder.stderr b/tests/ui/ufcs/bad-builder.stderr index 9cfeb7a5d09d6..e466f94d0d842 100644 --- a/tests/ui/ufcs/bad-builder.stderr +++ b/tests/ui/ufcs/bad-builder.stderr @@ -9,7 +9,7 @@ note: if you're trying to build a new `Vec` consider using one of the followi Vec::::with_capacity Vec::::try_with_capacity Vec::::from_raw_parts - and 4 others + and 6 others --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL help: there is an associated function `new` with a similar name | From ed89d9b7a29124e2757a8cfba93046f44992e07b Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Tue, 10 Sep 2024 19:00:54 +0300 Subject: [PATCH 05/10] force ci-llvm by default on library and tools profiles It's very rare for developers to need to modify LLVM, so "if-unchanged" isn't a good default since it fetches the LLVM submodule to track changes. Signed-off-by: onur-ozkan --- src/bootstrap/defaults/config.library.toml | 3 ++- src/bootstrap/defaults/config.tools.toml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/defaults/config.library.toml b/src/bootstrap/defaults/config.library.toml index 087544397f5ea..5447565a4b04c 100644 --- a/src/bootstrap/defaults/config.library.toml +++ b/src/bootstrap/defaults/config.library.toml @@ -13,4 +13,5 @@ lto = "off" [llvm] # Will download LLVM from CI if available on your platform. -download-ci-llvm = "if-unchanged" +# If you intend to modify `src/llvm-project`, use `"if-unchanged"` or `false` instead. +download-ci-llvm = true diff --git a/src/bootstrap/defaults/config.tools.toml b/src/bootstrap/defaults/config.tools.toml index 75ad37ce33549..efb56996bcd45 100644 --- a/src/bootstrap/defaults/config.tools.toml +++ b/src/bootstrap/defaults/config.tools.toml @@ -17,4 +17,5 @@ compiler-docs = true [llvm] # Will download LLVM from CI if available on your platform. -download-ci-llvm = "if-unchanged" +# If you intend to modify `src/llvm-project`, use `"if-unchanged"` or `false` instead. +download-ci-llvm = true From b5d69ba9fb77ee2fa0bedc137660faf1d3d06f9b Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Tue, 10 Sep 2024 19:07:50 +0300 Subject: [PATCH 06/10] add change entry for "tools" and "library" profile update Signed-off-by: onur-ozkan --- src/bootstrap/src/utils/change_tracker.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 99bcc6e0787ff..bedaa15ccb9c5 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -255,4 +255,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Warning, summary: "`download-ci-llvm = true` now checks if CI llvm is available and has become the default for the compiler profile", }, + ChangeInfo { + change_id: 130202, + severity: ChangeSeverity::Info, + summary: "'tools' and 'library' profiles switched `download-ci-llvm` option from `if-unchanged` to `true`.", + }, ]; From d4ac759542ec9789ad5ba9a12abe112e7158ecab Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 11 Sep 2024 08:08:39 +0200 Subject: [PATCH 07/10] MaybeUninit::zeroed: mention that padding is not zeroed --- library/core/src/mem/maybe_uninit.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 4be2e5ef1eade..0154caa7c24dd 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -351,6 +351,9 @@ impl MaybeUninit { /// but `MaybeUninit<&'static i32>::zeroed()` is not because references must not /// be null. /// + /// Note that if `T` has padding bytes, those bytes are *not* preserved when the + /// `MaybeUninit` value is returned from this function, so those bytes will *not* be zeroed. + /// /// Note that dropping a `MaybeUninit` will never call `T`'s drop code. /// It is your responsibility to make sure `T` gets dropped if it got initialized. /// From 14ed979cdfda34c468e0abaad9e3c981205a7bac Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 14 Sep 2024 23:16:47 +1000 Subject: [PATCH 08/10] Make some lint doctests compatible with `--stage=0` --- compiler/rustc_lint/src/if_let_rescope.rs | 2 +- compiler/rustc_lint_defs/src/builtin.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs index 5b65541bea6e2..7138d40a48f64 100644 --- a/compiler/rustc_lint/src/if_let_rescope.rs +++ b/compiler/rustc_lint/src/if_let_rescope.rs @@ -24,7 +24,7 @@ declare_lint! { /// ### Example /// /// ```rust,edition2021 - /// #![feature(if_let_rescope)] + /// #![cfg_attr(not(bootstrap), feature(if_let_rescope))] // Simplify this in bootstrap bump. /// #![warn(if_let_rescope)] /// #![allow(unused_variables)] /// diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 25d33126754af..d7394b68b608c 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -1871,6 +1871,7 @@ declare_lint! { /// ### Example /// /// ```rust,compile_fail + /// # #[cfg_attr(bootstrap)] compile_error!(); // Remove this in bootstrap bump. /// #![deny(elided_named_lifetimes)] /// struct Foo; /// impl Foo { From 811ee38ff0e291cc0633753f1aac152dd063d1d8 Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas) Chirananthavat" Date: Sun, 15 Sep 2024 01:11:18 +0700 Subject: [PATCH 09/10] Add tracking issue number for `box_vec_non_null` --- library/alloc/src/boxed.rs | 8 ++++---- library/alloc/src/vec/mod.rs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index f265b7b6f26ec..22d950bf3baa2 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1106,7 +1106,7 @@ impl Box { /// /// [memory layout]: self#memory-layout /// [`Layout`]: crate::Layout - #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "none")] + #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] #[inline] #[must_use = "call `drop(Box::from_non_null(ptr))` if you intend to drop the `Box`"] pub unsafe fn from_non_null(ptr: NonNull) -> Self { @@ -1216,7 +1216,7 @@ impl Box { /// [memory layout]: self#memory-layout /// [`Layout`]: crate::Layout #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "none")] + // #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] #[rustc_const_unstable(feature = "const_box", issue = "92521")] #[inline] pub const unsafe fn from_non_null_in(raw: NonNull, alloc: A) -> Self { @@ -1332,7 +1332,7 @@ impl Box { /// /// [memory layout]: self#memory-layout #[must_use = "losing the pointer will leak memory"] - #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "none")] + #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] #[inline] pub fn into_non_null(b: Self) -> NonNull { // SAFETY: `Box` is guaranteed to be non-null. @@ -1447,7 +1447,7 @@ impl Box { /// [memory layout]: self#memory-layout #[must_use = "losing the pointer will leak memory"] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "none")] + // #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] #[inline] pub fn into_non_null_with_allocator(b: Self) -> (NonNull, A) { let (ptr, alloc) = Box::into_raw_with_allocator(b); diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index aa23028cd2934..0b04ac81c20ed 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -710,7 +710,7 @@ impl Vec { /// } /// ``` #[inline] - #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "none")] + #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] pub unsafe fn from_parts(ptr: NonNull, length: usize, capacity: usize) -> Self { unsafe { Self::from_parts_in(ptr, length, capacity, Global) } } @@ -1042,7 +1042,7 @@ impl Vec { /// ``` #[inline] #[unstable(feature = "allocator_api", reason = "new API", issue = "32838")] - // #[unstable(feature = "box_vec_non_null", issue = "none")] + // #[unstable(feature = "box_vec_non_null", issue = "130364")] pub unsafe fn from_parts_in(ptr: NonNull, length: usize, capacity: usize, alloc: A) -> Self { unsafe { Vec { buf: RawVec::from_nonnull_in(ptr, capacity, alloc), len: length } } } @@ -1121,7 +1121,7 @@ impl Vec { /// assert_eq!(rebuilt, [4294967295, 0, 1]); /// ``` #[must_use = "losing the pointer will leak memory"] - #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "none")] + #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] // #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")] pub fn into_parts(self) -> (NonNull, usize, usize) { let (ptr, len, capacity) = self.into_raw_parts(); @@ -1218,7 +1218,7 @@ impl Vec { /// ``` #[must_use = "losing the pointer will leak memory"] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "none")] + // #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] // #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")] pub fn into_parts_with_alloc(self) -> (NonNull, usize, usize, A) { let (ptr, len, capacity, alloc) = self.into_raw_parts_with_alloc(); From 29ccc0d3256f487225140e6d9e2823adbb460710 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Sat, 14 Sep 2024 14:46:29 -0700 Subject: [PATCH 10/10] `trait_upcasting` is not currently an incomplete feature. --- src/doc/unstable-book/src/language-features/trait-upcasting.md | 1 - 1 file changed, 1 deletion(-) diff --git a/src/doc/unstable-book/src/language-features/trait-upcasting.md b/src/doc/unstable-book/src/language-features/trait-upcasting.md index 3697ae38f9d81..a5f99cc86f270 100644 --- a/src/doc/unstable-book/src/language-features/trait-upcasting.md +++ b/src/doc/unstable-book/src/language-features/trait-upcasting.md @@ -12,7 +12,6 @@ so long as `Bar: Foo`. ```rust,edition2018 #![feature(trait_upcasting)] -#![allow(incomplete_features)] trait Foo {}