[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20230823184107.494aa56e.gary@garyguo.net>
Date: Wed, 23 Aug 2023 18:41:07 +0100
From: Gary Guo <gary@...yguo.net>
To: Miguel Ojeda <ojeda@...nel.org>
Cc: Wedson Almeida Filho <wedsonaf@...il.com>,
Alex Gaynor <alex.gaynor@...il.com>,
Boqun Feng <boqun.feng@...il.com>,
Björn Roy Baron <bjorn3_gh@...tonmail.com>,
Benno Lossin <benno.lossin@...ton.me>,
Andreas Hindborg <a.hindborg@...sung.com>,
Alice Ryhl <aliceryhl@...gle.com>,
rust-for-linux@...r.kernel.org, linux-kernel@...r.kernel.org,
patches@...ts.linux.dev, Jonathan Corbet <corbet@....net>,
workflows@...r.kernel.org, linux-doc@...r.kernel.org
Subject: Re: [PATCH 2/2] rust: upgrade to Rust 1.72.0
On Wed, 23 Aug 2023 18:02:43 +0200
Miguel Ojeda <ojeda@...nel.org> wrote:
> This is the second upgrade to the Rust toolchain, from 1.71.1 to 1.72.0
> (i.e. the latest) [1].
>
> See the upgrade policy [2] and the comments on the first upgrade in
> commit 3ed03f4da06e ("rust: upgrade to Rust 1.68.2").
>
> # Unstable features
>
> No unstable features (that we use) were stabilized.
>
> Therefore, the only unstable feature allowed to be used outside
> the `kernel` crate is still `new_uninit`, though other code to be
> upstreamed may increase the list.
>
> Please see [3] for details.
>
> # Other improvements
>
> Previously, the compiler could incorrectly generate a `.eh_frame`
> section under `-Cpanic=abort`. We were hitting this bug in our
> old `rust` branch [4]. Gary fixed the issue in Rust 1.72.0 [5].
>
> # Required changes
>
> For the upgrade, the following changes are required:
>
> - A call to `Box::from_raw` in `rust/kernel/sync/arc.rs` now requires
> an explicit `drop()` call. See previous patch for details.
>
> # `alloc` upgrade and reviewing
>
> The vast majority of changes are due to our `alloc` fork being upgraded
> at once.
>
> There are two kinds of changes to be aware of: the ones coming from
> upstream, which we should follow as closely as possible, and the updates
> needed in our added fallible APIs to keep them matching the newer
> infallible APIs coming from upstream.
>
> Instead of taking a look at the diff of this patch, an alternative
> approach is reviewing a diff of the changes between upstream `alloc` and
> the kernel's. This allows to easily inspect the kernel additions only,
> especially to check if the fallible methods we already have still match
> the infallible ones in the new version coming from upstream.
>
> Another approach is reviewing the changes introduced in the additions in
> the kernel fork between the two versions. This is useful to spot
> potentially unintended changes to our additions.
>
> To apply these approaches, one may follow steps similar to the following
> to generate a pair of patches that show the differences between upstream
> Rust and the kernel (for the subset of `alloc` we use) before and after
> applying this patch:
>
> # Get the difference with respect to the old version.
> git -C rust checkout $(linux/scripts/min-tool-version.sh rustc)
> git -C linux ls-tree -r --name-only HEAD -- rust/alloc |
> cut -d/ -f3- |
> grep -Fv README.md |
> xargs -IPATH cp rust/library/alloc/src/PATH linux/rust/alloc/PATH
> git -C linux diff --patch-with-stat --summary -R > old.patch
> git -C linux restore rust/alloc
>
> # Apply this patch.
> git -C linux am rust-upgrade.patch
>
> # Get the difference with respect to the new version.
> git -C rust checkout $(linux/scripts/min-tool-version.sh rustc)
> git -C linux ls-tree -r --name-only HEAD -- rust/alloc |
> cut -d/ -f3- |
> grep -Fv README.md |
> xargs -IPATH cp rust/library/alloc/src/PATH linux/rust/alloc/PATH
> git -C linux diff --patch-with-stat --summary -R > new.patch
> git -C linux restore rust/alloc
>
> Now one may check the `new.patch` to take a look at the additions (first
> approach) or at the difference between those two patches (second
> approach). For the latter, a side-by-side tool is recommended.
>
> Link: https://github.com/rust-lang/rust/blob/stable/RELEASES.md#version-1720-2023-08-24 [1]
> Link: https://rust-for-linux.com/rust-version-policy [2]
> Link: https://github.com/Rust-for-Linux/linux/issues/2 [3]
> Closes: https://github.com/Rust-for-Linux/linux/issues/1012 [4]
> Link: https://github.com/rust-lang/rust/pull/112403 [5]
> Signed-off-by: Miguel Ojeda <ojeda@...nel.org>
Reviewed-by: Gary Guo <gary@...yguo.net>
> ---
> Documentation/process/changes.rst | 2 +-
> rust/alloc/alloc.rs | 9 +-
> rust/alloc/boxed.rs | 10 +-
> rust/alloc/lib.rs | 10 +-
> rust/alloc/vec/drain_filter.rs | 199 ------------------------------
> rust/alloc/vec/extract_if.rs | 115 +++++++++++++++++
> rust/alloc/vec/mod.rs | 106 ++++++++--------
> scripts/min-tool-version.sh | 2 +-
> 8 files changed, 187 insertions(+), 266 deletions(-)
> delete mode 100644 rust/alloc/vec/drain_filter.rs
> create mode 100644 rust/alloc/vec/extract_if.rs
>
> diff --git a/Documentation/process/changes.rst b/Documentation/process/changes.rst
> index 0bbd040f6a55..1ea95fb5af62 100644
> --- a/Documentation/process/changes.rst
> +++ b/Documentation/process/changes.rst
> @@ -31,7 +31,7 @@ you probably needn't concern yourself with pcmciautils.
> ====================== =============== ========================================
> GNU C 5.1 gcc --version
> Clang/LLVM (optional) 11.0.0 clang --version
> -Rust (optional) 1.71.1 rustc --version
> +Rust (optional) 1.72.0 rustc --version
> bindgen (optional) 0.65.1 bindgen --version
> GNU make 3.82 make --version
> bash 4.2 bash --version
> diff --git a/rust/alloc/alloc.rs b/rust/alloc/alloc.rs
> index 0b6bf5b6da43..51821feb20b1 100644
> --- a/rust/alloc/alloc.rs
> +++ b/rust/alloc/alloc.rs
> @@ -6,8 +6,10 @@
>
> #[cfg(not(test))]
> use core::intrinsics;
> +#[cfg(all(bootstrap, not(test)))]
> use core::intrinsics::{min_align_of_val, size_of_val};
>
> +#[cfg(all(bootstrap, not(test)))]
> use core::ptr::Unique;
> #[cfg(not(test))]
> use core::ptr::{self, NonNull};
> @@ -40,7 +42,6 @@
> #[rustc_nounwind]
> fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8;
>
> - #[cfg(not(bootstrap))]
> static __rust_no_alloc_shim_is_unstable: u8;
> }
>
> @@ -98,7 +99,6 @@ pub unsafe fn alloc(layout: Layout) -> *mut u8 {
> unsafe {
> // Make sure we don't accidentally allow omitting the allocator shim in
> // stable code until it is actually stabilized.
> - #[cfg(not(bootstrap))]
> core::ptr::read_volatile(&__rust_no_alloc_shim_is_unstable);
>
> __rust_alloc(layout.size(), layout.align())
> @@ -339,14 +339,15 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 {
> }
> }
>
> -#[cfg_attr(not(test), lang = "box_free")]
> +#[cfg(all(bootstrap, not(test)))]
> +#[lang = "box_free"]
> #[inline]
> // This signature has to be the same as `Box`, otherwise an ICE will happen.
> // When an additional parameter to `Box` is added (like `A: Allocator`), this has to be added here as
> // well.
> // For example if `Box` is changed to `struct Box<T: ?Sized, A: Allocator>(Unique<T>, A)`,
> // this function has to be changed to `fn box_free<T: ?Sized, A: Allocator>(Unique<T>, A)` as well.
> -pub(crate) unsafe fn box_free<T: ?Sized, A: Allocator>(ptr: Unique<T>, alloc: A) {
> +unsafe fn box_free<T: ?Sized, A: Allocator>(ptr: Unique<T>, alloc: A) {
> unsafe {
> let size = size_of_val(ptr.as_ref());
> let align = min_align_of_val(ptr.as_ref());
> diff --git a/rust/alloc/boxed.rs b/rust/alloc/boxed.rs
> index c8173cea8317..bdab710f7737 100644
> --- a/rust/alloc/boxed.rs
> +++ b/rust/alloc/boxed.rs
> @@ -1215,8 +1215,16 @@ pub const fn into_pin(boxed: Self) -> Pin<Self>
>
> #[stable(feature = "rust1", since = "1.0.0")]
> unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Box<T, A> {
> + #[inline]
> fn drop(&mut self) {
> - // FIXME: Do nothing, drop is currently performed by compiler.
> + // the T in the Box is dropped by the compiler before the destructor is run
> +
> + let ptr = self.0;
> +
> + unsafe {
> + let layout = Layout::for_value_raw(ptr.as_ptr());
> + self.1.deallocate(From::from(ptr.cast()), layout)
> + }
> }
> }
>
> diff --git a/rust/alloc/lib.rs b/rust/alloc/lib.rs
> index 85e91356ecb3..115fcb053e73 100644
> --- a/rust/alloc/lib.rs
> +++ b/rust/alloc/lib.rs
> @@ -58,6 +58,11 @@
> //! [`Rc`]: rc
> //! [`RefCell`]: core::cell
>
> +// To run alloc tests without x.py without ending up with two copies of alloc, Miri needs to be
> +// able to "empty" this crate. See <https://github.com/rust-lang/miri-test-libstd/issues/4>.
> +// rustc itself never sets the feature, so this line has no affect there.
> +#![cfg(any(not(feature = "miri-test-libstd"), test, doctest))]
> +//
> #![allow(unused_attributes)]
> #![stable(feature = "alloc", since = "1.36.0")]
> #![doc(
> @@ -77,11 +82,6 @@
> ))]
> #![no_std]
> #![needs_allocator]
> -// To run alloc tests without x.py without ending up with two copies of alloc, Miri needs to be
> -// able to "empty" this crate. See <https://github.com/rust-lang/miri-test-libstd/issues/4>.
> -// rustc itself never sets the feature, so this line has no affect there.
> -#![cfg(any(not(feature = "miri-test-libstd"), test, doctest))]
> -//
> // Lints:
> #![deny(unsafe_op_in_unsafe_fn)]
> #![deny(fuzzy_provenance_casts)]
> diff --git a/rust/alloc/vec/drain_filter.rs b/rust/alloc/vec/drain_filter.rs
> deleted file mode 100644
> index 09efff090e42..000000000000
> --- a/rust/alloc/vec/drain_filter.rs
> +++ /dev/null
> @@ -1,199 +0,0 @@
> -// SPDX-License-Identifier: Apache-2.0 OR MIT
> -
> -use crate::alloc::{Allocator, Global};
> -use core::mem::{ManuallyDrop, SizedTypeProperties};
> -use core::ptr;
> -use core::slice;
> -
> -use super::Vec;
> -
> -/// An iterator which uses a closure to determine if an element should be removed.
> -///
> -/// This struct is created by [`Vec::drain_filter`].
> -/// See its documentation for more.
> -///
> -/// # Example
> -///
> -/// ```
> -/// #![feature(drain_filter)]
> -///
> -/// let mut v = vec![0, 1, 2];
> -/// let iter: std::vec::DrainFilter<'_, _, _> = v.drain_filter(|x| *x % 2 == 0);
> -/// ```
> -#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")]
> -#[derive(Debug)]
> -pub struct DrainFilter<
> - 'a,
> - T,
> - F,
> - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global,
> -> where
> - F: FnMut(&mut T) -> bool,
> -{
> - pub(super) vec: &'a mut Vec<T, A>,
> - /// The index of the item that will be inspected by the next call to `next`.
> - pub(super) idx: usize,
> - /// The number of items that have been drained (removed) thus far.
> - pub(super) del: usize,
> - /// The original length of `vec` prior to draining.
> - pub(super) old_len: usize,
> - /// The filter test predicate.
> - pub(super) pred: F,
> - /// A flag that indicates a panic has occurred in the filter test predicate.
> - /// This is used as a hint in the drop implementation to prevent consumption
> - /// of the remainder of the `DrainFilter`. Any unprocessed items will be
> - /// backshifted in the `vec`, but no further items will be dropped or
> - /// tested by the filter predicate.
> - pub(super) panic_flag: bool,
> -}
> -
> -impl<T, F, A: Allocator> DrainFilter<'_, T, F, A>
> -where
> - F: FnMut(&mut T) -> bool,
> -{
> - /// Returns a reference to the underlying allocator.
> - #[unstable(feature = "allocator_api", issue = "32838")]
> - #[inline]
> - pub fn allocator(&self) -> &A {
> - self.vec.allocator()
> - }
> -
> - /// Keep unyielded elements in the source `Vec`.
> - ///
> - /// # Examples
> - ///
> - /// ```
> - /// #![feature(drain_filter)]
> - /// #![feature(drain_keep_rest)]
> - ///
> - /// let mut vec = vec!['a', 'b', 'c'];
> - /// let mut drain = vec.drain_filter(|_| true);
> - ///
> - /// assert_eq!(drain.next().unwrap(), 'a');
> - ///
> - /// // This call keeps 'b' and 'c' in the vec.
> - /// drain.keep_rest();
> - ///
> - /// // If we wouldn't call `keep_rest()`,
> - /// // `vec` would be empty.
> - /// assert_eq!(vec, ['b', 'c']);
> - /// ```
> - #[unstable(feature = "drain_keep_rest", issue = "101122")]
> - pub fn keep_rest(self) {
> - // At this moment layout looks like this:
> - //
> - // _____________________/-- old_len
> - // / \
> - // [kept] [yielded] [tail]
> - // \_______/ ^-- idx
> - // \-- del
> - //
> - // Normally `Drop` impl would drop [tail] (via .for_each(drop), ie still calling `pred`)
> - //
> - // 1. Move [tail] after [kept]
> - // 2. Update length of the original vec to `old_len - del`
> - // a. In case of ZST, this is the only thing we want to do
> - // 3. Do *not* drop self, as everything is put in a consistent state already, there is nothing to do
> - let mut this = ManuallyDrop::new(self);
> -
> - unsafe {
> - // ZSTs have no identity, so we don't need to move them around.
> - if !T::IS_ZST && this.idx < this.old_len && this.del > 0 {
> - let ptr = this.vec.as_mut_ptr();
> - let src = ptr.add(this.idx);
> - let dst = src.sub(this.del);
> - let tail_len = this.old_len - this.idx;
> - src.copy_to(dst, tail_len);
> - }
> -
> - let new_len = this.old_len - this.del;
> - this.vec.set_len(new_len);
> - }
> - }
> -}
> -
> -#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")]
> -impl<T, F, A: Allocator> Iterator for DrainFilter<'_, T, F, A>
> -where
> - F: FnMut(&mut T) -> bool,
> -{
> - type Item = T;
> -
> - fn next(&mut self) -> Option<T> {
> - unsafe {
> - while self.idx < self.old_len {
> - let i = self.idx;
> - let v = slice::from_raw_parts_mut(self.vec.as_mut_ptr(), self.old_len);
> - self.panic_flag = true;
> - let drained = (self.pred)(&mut v[i]);
> - self.panic_flag = false;
> - // Update the index *after* the predicate is called. If the index
> - // is updated prior and the predicate panics, the element at this
> - // index would be leaked.
> - self.idx += 1;
> - if drained {
> - self.del += 1;
> - return Some(ptr::read(&v[i]));
> - } else if self.del > 0 {
> - let del = self.del;
> - let src: *const T = &v[i];
> - let dst: *mut T = &mut v[i - del];
> - ptr::copy_nonoverlapping(src, dst, 1);
> - }
> - }
> - None
> - }
> - }
> -
> - fn size_hint(&self) -> (usize, Option<usize>) {
> - (0, Some(self.old_len - self.idx))
> - }
> -}
> -
> -#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")]
> -impl<T, F, A: Allocator> Drop for DrainFilter<'_, T, F, A>
> -where
> - F: FnMut(&mut T) -> bool,
> -{
> - fn drop(&mut self) {
> - struct BackshiftOnDrop<'a, 'b, T, F, A: Allocator>
> - where
> - F: FnMut(&mut T) -> bool,
> - {
> - drain: &'b mut DrainFilter<'a, T, F, A>,
> - }
> -
> - impl<'a, 'b, T, F, A: Allocator> Drop for BackshiftOnDrop<'a, 'b, T, F, A>
> - where
> - F: FnMut(&mut T) -> bool,
> - {
> - fn drop(&mut self) {
> - unsafe {
> - if self.drain.idx < self.drain.old_len && self.drain.del > 0 {
> - // This is a pretty messed up state, and there isn't really an
> - // obviously right thing to do. We don't want to keep trying
> - // to execute `pred`, so we just backshift all the unprocessed
> - // elements and tell the vec that they still exist. The backshift
> - // is required to prevent a double-drop of the last successfully
> - // drained item prior to a panic in the predicate.
> - let ptr = self.drain.vec.as_mut_ptr();
> - let src = ptr.add(self.drain.idx);
> - let dst = src.sub(self.drain.del);
> - let tail_len = self.drain.old_len - self.drain.idx;
> - src.copy_to(dst, tail_len);
> - }
> - self.drain.vec.set_len(self.drain.old_len - self.drain.del);
> - }
> - }
> - }
> -
> - let backshift = BackshiftOnDrop { drain: self };
> -
> - // Attempt to consume any remaining elements if the filter predicate
> - // has not yet panicked. We'll backshift any remaining elements
> - // whether we've already panicked or if the consumption here panics.
> - if !backshift.drain.panic_flag {
> - backshift.drain.for_each(drop);
> - }
> - }
> -}
> diff --git a/rust/alloc/vec/extract_if.rs b/rust/alloc/vec/extract_if.rs
> new file mode 100644
> index 000000000000..f314a51d4d3d
> --- /dev/null
> +++ b/rust/alloc/vec/extract_if.rs
> @@ -0,0 +1,115 @@
> +// SPDX-License-Identifier: Apache-2.0 OR MIT
> +
> +use crate::alloc::{Allocator, Global};
> +use core::ptr;
> +use core::slice;
> +
> +use super::Vec;
> +
> +/// An iterator which uses a closure to determine if an element should be removed.
> +///
> +/// This struct is created by [`Vec::extract_if`].
> +/// See its documentation for more.
> +///
> +/// # Example
> +///
> +/// ```
> +/// #![feature(extract_if)]
> +///
> +/// let mut v = vec![0, 1, 2];
> +/// let iter: std::vec::ExtractIf<'_, _, _> = v.extract_if(|x| *x % 2 == 0);
> +/// ```
> +#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")]
> +#[derive(Debug)]
> +#[must_use = "iterators are lazy and do nothing unless consumed"]
> +pub struct ExtractIf<
> + 'a,
> + T,
> + F,
> + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global,
> +> where
> + F: FnMut(&mut T) -> bool,
> +{
> + pub(super) vec: &'a mut Vec<T, A>,
> + /// The index of the item that will be inspected by the next call to `next`.
> + pub(super) idx: usize,
> + /// The number of items that have been drained (removed) thus far.
> + pub(super) del: usize,
> + /// The original length of `vec` prior to draining.
> + pub(super) old_len: usize,
> + /// The filter test predicate.
> + pub(super) pred: F,
> +}
> +
> +impl<T, F, A: Allocator> ExtractIf<'_, T, F, A>
> +where
> + F: FnMut(&mut T) -> bool,
> +{
> + /// Returns a reference to the underlying allocator.
> + #[unstable(feature = "allocator_api", issue = "32838")]
> + #[inline]
> + pub fn allocator(&self) -> &A {
> + self.vec.allocator()
> + }
> +}
> +
> +#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")]
> +impl<T, F, A: Allocator> Iterator for ExtractIf<'_, T, F, A>
> +where
> + F: FnMut(&mut T) -> bool,
> +{
> + type Item = T;
> +
> + fn next(&mut self) -> Option<T> {
> + unsafe {
> + while self.idx < self.old_len {
> + let i = self.idx;
> + let v = slice::from_raw_parts_mut(self.vec.as_mut_ptr(), self.old_len);
> + let drained = (self.pred)(&mut v[i]);
> + // Update the index *after* the predicate is called. If the index
> + // is updated prior and the predicate panics, the element at this
> + // index would be leaked.
> + self.idx += 1;
> + if drained {
> + self.del += 1;
> + return Some(ptr::read(&v[i]));
> + } else if self.del > 0 {
> + let del = self.del;
> + let src: *const T = &v[i];
> + let dst: *mut T = &mut v[i - del];
> + ptr::copy_nonoverlapping(src, dst, 1);
> + }
> + }
> + None
> + }
> + }
> +
> + fn size_hint(&self) -> (usize, Option<usize>) {
> + (0, Some(self.old_len - self.idx))
> + }
> +}
> +
> +#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")]
> +impl<T, F, A: Allocator> Drop for ExtractIf<'_, T, F, A>
> +where
> + F: FnMut(&mut T) -> bool,
> +{
> + fn drop(&mut self) {
> + unsafe {
> + if self.idx < self.old_len && self.del > 0 {
> + // This is a pretty messed up state, and there isn't really an
> + // obviously right thing to do. We don't want to keep trying
> + // to execute `pred`, so we just backshift all the unprocessed
> + // elements and tell the vec that they still exist. The backshift
> + // is required to prevent a double-drop of the last successfully
> + // drained item prior to a panic in the predicate.
> + let ptr = self.vec.as_mut_ptr();
> + let src = ptr.add(self.idx);
> + let dst = src.sub(self.del);
> + let tail_len = self.old_len - self.idx;
> + src.copy_to(dst, tail_len);
> + }
> + self.vec.set_len(self.old_len - self.del);
> + }
> + }
> +}
> diff --git a/rust/alloc/vec/mod.rs b/rust/alloc/vec/mod.rs
> index 05c70de0227e..a4e9a5002a6d 100644
> --- a/rust/alloc/vec/mod.rs
> +++ b/rust/alloc/vec/mod.rs
> @@ -74,10 +74,10 @@
> use crate::collections::{TryReserveError, TryReserveErrorKind};
> use crate::raw_vec::RawVec;
>
> -#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")]
> -pub use self::drain_filter::DrainFilter;
> +#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")]
> +pub use self::extract_if::ExtractIf;
>
> -mod drain_filter;
> +mod extract_if;
>
> #[cfg(not(no_global_oom_handling))]
> #[stable(feature = "vec_splice", since = "1.21.0")]
> @@ -618,22 +618,20 @@ pub fn try_with_capacity(capacity: usize) -> Result<Self, TryReserveError> {
> /// Using memory that was allocated elsewhere:
> ///
> /// ```rust
> - /// #![feature(allocator_api)]
> - ///
> - /// use std::alloc::{AllocError, Allocator, Global, Layout};
> + /// use std::alloc::{alloc, Layout};
> ///
> /// fn main() {
> /// let layout = Layout::array::<u32>(16).expect("overflow cannot happen");
> ///
> /// let vec = unsafe {
> - /// let mem = match Global.allocate(layout) {
> - /// Ok(mem) => mem.cast::<u32>().as_ptr(),
> - /// Err(AllocError) => return,
> - /// };
> + /// let mem = alloc(layout).cast::<u32>();
> + /// if mem.is_null() {
> + /// return;
> + /// }
> ///
> /// mem.write(1_000_000);
> ///
> - /// Vec::from_raw_parts_in(mem, 1, 16, Global)
> + /// Vec::from_raw_parts(mem, 1, 16)
> /// };
> ///
> /// assert_eq!(vec, &[1_000_000]);
> @@ -876,19 +874,22 @@ pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result<Self, TryReserv
> /// Using memory that was allocated elsewhere:
> ///
> /// ```rust
> - /// use std::alloc::{alloc, Layout};
> + /// #![feature(allocator_api)]
> + ///
> + /// use std::alloc::{AllocError, Allocator, Global, Layout};
> ///
> /// fn main() {
> /// let layout = Layout::array::<u32>(16).expect("overflow cannot happen");
> + ///
> /// let vec = unsafe {
> - /// let mem = alloc(layout).cast::<u32>();
> - /// if mem.is_null() {
> - /// return;
> - /// }
> + /// let mem = match Global.allocate(layout) {
> + /// Ok(mem) => mem.cast::<u32>().as_ptr(),
> + /// Err(AllocError) => return,
> + /// };
> ///
> /// mem.write(1_000_000);
> ///
> - /// Vec::from_raw_parts(mem, 1, 16)
> + /// Vec::from_raw_parts_in(mem, 1, 16, Global)
> /// };
> ///
> /// assert_eq!(vec, &[1_000_000]);
> @@ -2507,7 +2508,7 @@ pub fn resize(&mut self, new_len: usize, value: T) {
> let len = self.len();
>
> if new_len > len {
> - self.extend_with(new_len - len, ExtendElement(value))
> + self.extend_with(new_len - len, value)
> } else {
> self.truncate(new_len);
> }
> @@ -2545,7 +2546,7 @@ pub fn try_resize(&mut self, new_len: usize, value: T) -> Result<(), TryReserveE
> let len = self.len();
>
> if new_len > len {
> - self.try_extend_with(new_len - len, ExtendElement(value))
> + self.try_extend_with(new_len - len, value)
> } else {
> self.truncate(new_len);
> Ok(())
> @@ -2684,26 +2685,10 @@ pub fn into_flattened(self) -> Vec<T, A> {
> }
> }
>
> -// This code generalizes `extend_with_{element,default}`.
> -trait ExtendWith<T> {
> - fn next(&mut self) -> T;
> - fn last(self) -> T;
> -}
> -
> -struct ExtendElement<T>(T);
> -impl<T: Clone> ExtendWith<T> for ExtendElement<T> {
> - fn next(&mut self) -> T {
> - self.0.clone()
> - }
> - fn last(self) -> T {
> - self.0
> - }
> -}
> -
> -impl<T, A: Allocator> Vec<T, A> {
> +impl<T: Clone, A: Allocator> Vec<T, A> {
> #[cfg(not(no_global_oom_handling))]
> - /// Extend the vector by `n` values, using the given generator.
> - fn extend_with<E: ExtendWith<T>>(&mut self, n: usize, mut value: E) {
> + /// Extend the vector by `n` clones of value.
> + fn extend_with(&mut self, n: usize, value: T) {
> self.reserve(n);
>
> unsafe {
> @@ -2715,15 +2700,15 @@ fn extend_with<E: ExtendWith<T>>(&mut self, n: usize, mut value: E) {
>
> // Write all elements except the last one
> for _ in 1..n {
> - ptr::write(ptr, value.next());
> + ptr::write(ptr, value.clone());
> ptr = ptr.add(1);
> - // Increment the length in every step in case next() panics
> + // Increment the length in every step in case clone() panics
> local_len.increment_len(1);
> }
>
> if n > 0 {
> // We can write the last element directly without cloning needlessly
> - ptr::write(ptr, value.last());
> + ptr::write(ptr, value);
> local_len.increment_len(1);
> }
>
> @@ -2731,8 +2716,8 @@ fn extend_with<E: ExtendWith<T>>(&mut self, n: usize, mut value: E) {
> }
> }
>
> - /// Try to extend the vector by `n` values, using the given generator.
> - fn try_extend_with<E: ExtendWith<T>>(&mut self, n: usize, mut value: E) -> Result<(), TryReserveError> {
> + /// Try to extend the vector by `n` clones of value.
> + fn try_extend_with(&mut self, n: usize, value: T) -> Result<(), TryReserveError> {
> self.try_reserve(n)?;
>
> unsafe {
> @@ -2744,15 +2729,15 @@ fn try_extend_with<E: ExtendWith<T>>(&mut self, n: usize, mut value: E) -> Resul
>
> // Write all elements except the last one
> for _ in 1..n {
> - ptr::write(ptr, value.next());
> + ptr::write(ptr, value.clone());
> ptr = ptr.add(1);
> - // Increment the length in every step in case next() panics
> + // Increment the length in every step in case clone() panics
> local_len.increment_len(1);
> }
>
> if n > 0 {
> // We can write the last element directly without cloning needlessly
> - ptr::write(ptr, value.last());
> + ptr::write(ptr, value);
> local_len.increment_len(1);
> }
>
> @@ -3210,6 +3195,12 @@ pub fn splice<R, I>(&mut self, range: R, replace_with: I) -> Splice<'_, I::IntoI
> /// If the closure returns false, the element will remain in the vector and will not be yielded
> /// by the iterator.
> ///
> + /// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating
> + /// or the iteration short-circuits, then the remaining elements will be retained.
> + /// Use [`retain`] with a negated predicate if you do not need the returned iterator.
> + ///
> + /// [`retain`]: Vec::retain
> + ///
> /// Using this method is equivalent to the following code:
> ///
> /// ```
> @@ -3228,10 +3219,10 @@ pub fn splice<R, I>(&mut self, range: R, replace_with: I) -> Splice<'_, I::IntoI
> /// # assert_eq!(vec, vec![1, 4, 5]);
> /// ```
> ///
> - /// But `drain_filter` is easier to use. `drain_filter` is also more efficient,
> + /// But `extract_if` is easier to use. `extract_if` is also more efficient,
> /// because it can backshift the elements of the array in bulk.
> ///
> - /// Note that `drain_filter` also lets you mutate every element in the filter closure,
> + /// Note that `extract_if` also lets you mutate every element in the filter closure,
> /// regardless of whether you choose to keep or remove it.
> ///
> /// # Examples
> @@ -3239,17 +3230,17 @@ pub fn splice<R, I>(&mut self, range: R, replace_with: I) -> Splice<'_, I::IntoI
> /// Splitting an array into evens and odds, reusing the original allocation:
> ///
> /// ```
> - /// #![feature(drain_filter)]
> + /// #![feature(extract_if)]
> /// let mut numbers = vec![1, 2, 3, 4, 5, 6, 8, 9, 11, 13, 14, 15];
> ///
> - /// let evens = numbers.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
> + /// let evens = numbers.extract_if(|x| *x % 2 == 0).collect::<Vec<_>>();
> /// let odds = numbers;
> ///
> /// assert_eq!(evens, vec![2, 4, 6, 8, 14]);
> /// assert_eq!(odds, vec![1, 3, 5, 9, 11, 13, 15]);
> /// ```
> - #[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")]
> - pub fn drain_filter<F>(&mut self, filter: F) -> DrainFilter<'_, T, F, A>
> + #[unstable(feature = "extract_if", reason = "recently added", issue = "43244")]
> + pub fn extract_if<F>(&mut self, filter: F) -> ExtractIf<'_, T, F, A>
> where
> F: FnMut(&mut T) -> bool,
> {
> @@ -3260,7 +3251,7 @@ pub fn drain_filter<F>(&mut self, filter: F) -> DrainFilter<'_, T, F, A>
> self.set_len(0);
> }
>
> - DrainFilter { vec: self, idx: 0, del: 0, old_len, pred: filter, panic_flag: false }
> + ExtractIf { vec: self, idx: 0, del: 0, old_len, pred: filter }
> }
> }
>
> @@ -3290,9 +3281,14 @@ fn extend_reserve(&mut self, additional: usize) {
>
> /// Implements comparison of vectors, [lexicographically](Ord#lexicographical-comparison).
> #[stable(feature = "rust1", since = "1.0.0")]
> -impl<T: PartialOrd, A: Allocator> PartialOrd for Vec<T, A> {
> +impl<T, A1, A2> PartialOrd<Vec<T, A2>> for Vec<T, A1>
> +where
> + T: PartialOrd,
> + A1: Allocator,
> + A2: Allocator,
> +{
> #[inline]
> - fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
> + fn partial_cmp(&self, other: &Vec<T, A2>) -> Option<Ordering> {
> PartialOrd::partial_cmp(&**self, &**other)
> }
> }
> diff --git a/scripts/min-tool-version.sh b/scripts/min-tool-version.sh
> index d65ab8bfeaf4..9520612dd398 100755
> --- a/scripts/min-tool-version.sh
> +++ b/scripts/min-tool-version.sh
> @@ -31,7 +31,7 @@ llvm)
> fi
> ;;
> rustc)
> - echo 1.71.1
> + echo 1.72.0
> ;;
> bindgen)
> echo 0.65.1
Powered by blists - more mailing lists