Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

rust: upgrade to Rust 1.77.1

This is the next upgrade to the Rust toolchain, from 1.76.0 to 1.77.1
(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

The `offset_of` feature (single-field `offset_of!`) that we were using
got stabilized in Rust 1.77.0 [3].

Therefore, now the only unstable features allowed to be used outside the
`kernel` crate is `new_uninit`, though other code to be upstreamed may
increase the list.

Please see [4] for details.

# Required changes

Rust 1.77.0 merged the `unused_tuple_struct_fields` lint into `dead_code`,
thus upgrading it from `allow` to `warn` [5]. In turn, this made `rustc`
complain about the `ThisModule`'s pointer field being never read, but
the previous patch adds the `as_ptr` method to it, needed by Binder [6],
so that we do not need to locally `allow` it.

# Other changes

Rust 1.77.0 introduces the `--check-cfg` feature [7], for which there
is a Call for Testing going on [8]. We were requested to test it and
we found it useful [9] -- we will likely enable it in the future.

# `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-1770-2024-03-21 [1]
Link: https://rust-for-linux.com/rust-version-policy [2]
Link: https://github.com/rust-lang/rust/pull/118799 [3]
Link: https://github.com/Rust-for-Linux/linux/issues/2 [4]
Link: https://github.com/rust-lang/rust/pull/118297 [5]
Link: https://lore.kernel.org/rust-for-linux/20231101-rust-binder-v1-2-08ba9197f637@google.com/#Z31rust:kernel:lib.rs [6]
Link: https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html [7]
Link: https://github.com/rust-lang/rfcs/pull/3013#issuecomment-1936648479 [8]
Link: https://github.com/rust-lang/rust/issues/82450#issuecomment-1947462977 [9]
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Tested-by: Boqun Feng <boqun.feng@gmail.com>
Link: https://lore.kernel.org/r/20240217002717.57507-1-ojeda@kernel.org
[ Upgraded to 1.77.1. Removed `allow(dead_code)` thanks to the previous
patch. Reworded accordingly. No changes to `alloc` during the beta. ]
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>

+158 -88
+1 -1
Documentation/process/changes.rst
··· 31 31 ====================== =============== ======================================== 32 32 GNU C 5.1 gcc --version 33 33 Clang/LLVM (optional) 13.0.1 clang --version 34 - Rust (optional) 1.76.0 rustc --version 34 + Rust (optional) 1.77.1 rustc --version 35 35 bindgen (optional) 0.65.1 bindgen --version 36 36 GNU make 3.82 make --version 37 37 bash 4.2 bash --version
+3 -3
rust/alloc/alloc.rs
··· 5 5 #![stable(feature = "alloc_module", since = "1.28.0")] 6 6 7 7 #[cfg(not(test))] 8 - use core::intrinsics; 8 + use core::hint; 9 9 10 10 #[cfg(not(test))] 11 11 use core::ptr::{self, NonNull}; ··· 210 210 let new_size = new_layout.size(); 211 211 212 212 // `realloc` probably checks for `new_size >= old_layout.size()` or something similar. 213 - intrinsics::assume(new_size >= old_layout.size()); 213 + hint::assert_unchecked(new_size >= old_layout.size()); 214 214 215 215 let raw_ptr = realloc(ptr.as_ptr(), old_layout, new_size); 216 216 let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; ··· 301 301 // SAFETY: `new_size` is non-zero. Other conditions must be upheld by the caller 302 302 new_size if old_layout.align() == new_layout.align() => unsafe { 303 303 // `realloc` probably checks for `new_size <= old_layout.size()` or something similar. 304 - intrinsics::assume(new_size <= old_layout.size()); 304 + hint::assert_unchecked(new_size <= old_layout.size()); 305 305 306 306 let raw_ptr = realloc(ptr.as_ptr(), old_layout, new_size); 307 307 let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
+2 -2
rust/alloc/boxed.rs
··· 26 26 //! Creating a recursive data structure: 27 27 //! 28 28 //! ``` 29 + //! ##[allow(dead_code)] 29 30 //! #[derive(Debug)] 30 31 //! enum List<T> { 31 32 //! Cons(T, Box<List<T>>), ··· 195 194 #[fundamental] 196 195 #[stable(feature = "rust1", since = "1.0.0")] 197 196 // The declaration of the `Box` struct must be kept in sync with the 198 - // `alloc::alloc::box_free` function or ICEs will happen. See the comment 199 - // on `box_free` for more details. 197 + // compiler or ICEs will happen. 200 198 pub struct Box< 201 199 T: ?Sized, 202 200 #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global,
+4 -3
rust/alloc/lib.rs
··· 105 105 #![feature(allocator_api)] 106 106 #![feature(array_chunks)] 107 107 #![feature(array_into_iter_constructors)] 108 - #![feature(array_methods)] 109 108 #![feature(array_windows)] 110 109 #![feature(ascii_char)] 111 110 #![feature(assert_matches)] ··· 121 122 #![feature(const_size_of_val)] 122 123 #![feature(const_waker)] 123 124 #![feature(core_intrinsics)] 124 - #![feature(core_panic)] 125 125 #![feature(deprecated_suggestion)] 126 126 #![feature(dispatch_from_dyn)] 127 127 #![feature(error_generic_member_access)] ··· 130 132 #![feature(fmt_internals)] 131 133 #![feature(fn_traits)] 132 134 #![feature(hasher_prefixfree_extras)] 135 + #![feature(hint_assert_unchecked)] 133 136 #![feature(inline_const)] 134 137 #![feature(inplace_iteration)] 135 138 #![feature(iter_advance_by)] ··· 140 141 #![feature(maybe_uninit_slice)] 141 142 #![feature(maybe_uninit_uninit_array)] 142 143 #![feature(maybe_uninit_uninit_array_transpose)] 144 + #![feature(non_null_convenience)] 145 + #![feature(panic_internals)] 143 146 #![feature(pattern)] 144 147 #![feature(ptr_internals)] 145 148 #![feature(ptr_metadata)] ··· 150 149 #![feature(set_ptr_value)] 151 150 #![feature(sized_type_properties)] 152 151 #![feature(slice_from_ptr_range)] 153 - #![feature(slice_group_by)] 154 152 #![feature(slice_ptr_get)] 155 153 #![feature(slice_ptr_len)] 156 154 #![feature(slice_range)] ··· 182 182 #![feature(const_ptr_write)] 183 183 #![feature(const_trait_impl)] 184 184 #![feature(const_try)] 185 + #![feature(decl_macro)] 185 186 #![feature(dropck_eyepatch)] 186 187 #![feature(exclusive_range_pattern)] 187 188 #![feature(fundamental)]
+6 -7
rust/alloc/raw_vec.rs
··· 4 4 5 5 use core::alloc::LayoutError; 6 6 use core::cmp; 7 - use core::intrinsics; 7 + use core::hint; 8 8 use core::mem::{self, ManuallyDrop, MaybeUninit, SizedTypeProperties}; 9 9 use core::ptr::{self, NonNull, Unique}; 10 10 use core::slice; ··· 317 317 /// 318 318 /// # Panics 319 319 /// 320 - /// Panics if the new capacity exceeds `isize::MAX` bytes. 320 + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. 321 321 /// 322 322 /// # Aborts 323 323 /// ··· 358 358 } 359 359 unsafe { 360 360 // Inform the optimizer that the reservation has succeeded or wasn't needed 361 - core::intrinsics::assume(!self.needs_to_grow(len, additional)); 361 + hint::assert_unchecked(!self.needs_to_grow(len, additional)); 362 362 } 363 363 Ok(()) 364 364 } ··· 381 381 /// 382 382 /// # Panics 383 383 /// 384 - /// Panics if the new capacity exceeds `isize::MAX` bytes. 384 + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. 385 385 /// 386 386 /// # Aborts 387 387 /// ··· 402 402 } 403 403 unsafe { 404 404 // Inform the optimizer that the reservation has succeeded or wasn't needed 405 - core::intrinsics::assume(!self.needs_to_grow(len, additional)); 405 + hint::assert_unchecked(!self.needs_to_grow(len, additional)); 406 406 } 407 407 Ok(()) 408 408 } ··· 553 553 debug_assert_eq!(old_layout.align(), new_layout.align()); 554 554 unsafe { 555 555 // The allocator checks for alignment equality 556 - intrinsics::assume(old_layout.align() == new_layout.align()); 556 + hint::assert_unchecked(old_layout.align() == new_layout.align()); 557 557 alloc.grow(ptr, old_layout, new_layout) 558 558 } 559 559 } else { ··· 591 591 // `> isize::MAX` bytes will surely fail. On 32-bit and 16-bit we need to add 592 592 // an extra guard for this in case we're running on a platform which can use 593 593 // all 4GB in user-space, e.g., PAE or x32. 594 - 595 594 #[inline] 596 595 fn alloc_guard(alloc_size: usize) -> Result<(), TryReserveError> { 597 596 if usize::BITS < 64 && alloc_size > isize::MAX as usize {
+2 -2
rust/alloc/slice.rs
··· 53 53 pub use core::slice::{from_mut_ptr_range, from_ptr_range}; 54 54 #[stable(feature = "rust1", since = "1.0.0")] 55 55 pub use core::slice::{from_raw_parts, from_raw_parts_mut}; 56 + #[stable(feature = "slice_group_by", since = "1.77.0")] 57 + pub use core::slice::{ChunkBy, ChunkByMut}; 56 58 #[stable(feature = "rust1", since = "1.0.0")] 57 59 pub use core::slice::{Chunks, Windows}; 58 60 #[stable(feature = "chunks_exact", since = "1.31.0")] 59 61 pub use core::slice::{ChunksExact, ChunksExactMut}; 60 62 #[stable(feature = "rust1", since = "1.0.0")] 61 63 pub use core::slice::{ChunksMut, Split, SplitMut}; 62 - #[unstable(feature = "slice_group_by", issue = "80552")] 63 - pub use core::slice::{GroupBy, GroupByMut}; 64 64 #[stable(feature = "rust1", since = "1.0.0")] 65 65 pub use core::slice::{Iter, IterMut}; 66 66 #[stable(feature = "rchunks", since = "1.31.0")]
+67 -37
rust/alloc/vec/into_iter.rs
··· 20 20 use core::ptr::{self, NonNull}; 21 21 use core::slice::{self}; 22 22 23 + macro non_null { 24 + (mut $place:expr, $t:ident) => {{ 25 + #![allow(unused_unsafe)] // we're sometimes used within an unsafe block 26 + unsafe { &mut *(ptr::addr_of_mut!($place) as *mut NonNull<$t>) } 27 + }}, 28 + ($place:expr, $t:ident) => {{ 29 + #![allow(unused_unsafe)] // we're sometimes used within an unsafe block 30 + unsafe { *(ptr::addr_of!($place) as *const NonNull<$t>) } 31 + }}, 32 + } 33 + 23 34 /// An iterator that moves out of a vector. 24 35 /// 25 36 /// This `struct` is created by the `into_iter` method on [`Vec`](super::Vec) ··· 54 43 // the drop impl reconstructs a RawVec from buf, cap and alloc 55 44 // to avoid dropping the allocator twice we need to wrap it into ManuallyDrop 56 45 pub(super) alloc: ManuallyDrop<A>, 57 - pub(super) ptr: *const T, 58 - pub(super) end: *const T, // If T is a ZST, this is actually ptr+len. This encoding is picked so that 59 - // ptr == end is a quick test for the Iterator being empty, that works 60 - // for both ZST and non-ZST. 46 + pub(super) ptr: NonNull<T>, 47 + /// If T is a ZST, this is actually ptr+len. This encoding is picked so that 48 + /// ptr == end is a quick test for the Iterator being empty, that works 49 + /// for both ZST and non-ZST. 50 + /// For non-ZSTs the pointer is treated as `NonNull<T>` 51 + pub(super) end: *const T, 61 52 } 62 53 63 54 #[stable(feature = "vec_intoiter_debug", since = "1.13.0")] ··· 83 70 /// ``` 84 71 #[stable(feature = "vec_into_iter_as_slice", since = "1.15.0")] 85 72 pub fn as_slice(&self) -> &[T] { 86 - unsafe { slice::from_raw_parts(self.ptr, self.len()) } 73 + unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len()) } 87 74 } 88 75 89 76 /// Returns the remaining items of this iterator as a mutable slice. ··· 112 99 } 113 100 114 101 fn as_raw_mut_slice(&mut self) -> *mut [T] { 115 - ptr::slice_from_raw_parts_mut(self.ptr as *mut T, self.len()) 102 + ptr::slice_from_raw_parts_mut(self.ptr.as_ptr(), self.len()) 116 103 } 117 104 118 105 /// Drops remaining elements and relinquishes the backing allocation. ··· 139 126 // this creates less assembly 140 127 self.cap = 0; 141 128 self.buf = unsafe { NonNull::new_unchecked(RawVec::NEW.ptr()) }; 142 - self.ptr = self.buf.as_ptr(); 129 + self.ptr = self.buf; 143 130 self.end = self.buf.as_ptr(); 144 131 145 132 // Dropping the remaining elements can panic, so this needs to be ··· 151 138 152 139 /// Forgets to Drop the remaining elements while still allowing the backing allocation to be freed. 153 140 pub(crate) fn forget_remaining_elements(&mut self) { 154 - // For th ZST case, it is crucial that we mutate `end` here, not `ptr`. 141 + // For the ZST case, it is crucial that we mutate `end` here, not `ptr`. 155 142 // `ptr` must stay aligned, while `end` may be unaligned. 156 - self.end = self.ptr; 143 + self.end = self.ptr.as_ptr(); 157 144 } 158 145 159 146 #[cfg(not(no_global_oom_handling))] ··· 175 162 // say that they're all at the beginning of the "allocation". 176 163 0..this.len() 177 164 } else { 178 - this.ptr.sub_ptr(buf)..this.end.sub_ptr(buf) 165 + this.ptr.sub_ptr(this.buf)..this.end.sub_ptr(buf) 179 166 }; 180 167 let cap = this.cap; 181 168 let alloc = ManuallyDrop::take(&mut this.alloc); ··· 202 189 203 190 #[inline] 204 191 fn next(&mut self) -> Option<T> { 205 - if self.ptr == self.end { 206 - None 207 - } else if T::IS_ZST { 208 - // `ptr` has to stay where it is to remain aligned, so we reduce the length by 1 by 209 - // reducing the `end`. 210 - self.end = self.end.wrapping_byte_sub(1); 192 + if T::IS_ZST { 193 + if self.ptr.as_ptr() == self.end as *mut _ { 194 + None 195 + } else { 196 + // `ptr` has to stay where it is to remain aligned, so we reduce the length by 1 by 197 + // reducing the `end`. 198 + self.end = self.end.wrapping_byte_sub(1); 211 199 212 - // Make up a value of this ZST. 213 - Some(unsafe { mem::zeroed() }) 200 + // Make up a value of this ZST. 201 + Some(unsafe { mem::zeroed() }) 202 + } 214 203 } else { 215 - let old = self.ptr; 216 - self.ptr = unsafe { self.ptr.add(1) }; 204 + if self.ptr == non_null!(self.end, T) { 205 + None 206 + } else { 207 + let old = self.ptr; 208 + self.ptr = unsafe { old.add(1) }; 217 209 218 - Some(unsafe { ptr::read(old) }) 210 + Some(unsafe { ptr::read(old.as_ptr()) }) 211 + } 219 212 } 220 213 } 221 214 222 215 #[inline] 223 216 fn size_hint(&self) -> (usize, Option<usize>) { 224 217 let exact = if T::IS_ZST { 225 - self.end.addr().wrapping_sub(self.ptr.addr()) 218 + self.end.addr().wrapping_sub(self.ptr.as_ptr().addr()) 226 219 } else { 227 - unsafe { self.end.sub_ptr(self.ptr) } 220 + unsafe { non_null!(self.end, T).sub_ptr(self.ptr) } 228 221 }; 229 222 (exact, Some(exact)) 230 223 } ··· 238 219 #[inline] 239 220 fn advance_by(&mut self, n: usize) -> Result<(), NonZeroUsize> { 240 221 let step_size = self.len().min(n); 241 - let to_drop = ptr::slice_from_raw_parts_mut(self.ptr as *mut T, step_size); 222 + let to_drop = ptr::slice_from_raw_parts_mut(self.ptr.as_ptr(), step_size); 242 223 if T::IS_ZST { 243 224 // See `next` for why we sub `end` here. 244 225 self.end = self.end.wrapping_byte_sub(step_size); ··· 280 261 // Safety: `len` indicates that this many elements are available and we just checked that 281 262 // it fits into the array. 282 263 unsafe { 283 - ptr::copy_nonoverlapping(self.ptr, raw_ary.as_mut_ptr() as *mut T, len); 264 + ptr::copy_nonoverlapping(self.ptr.as_ptr(), raw_ary.as_mut_ptr() as *mut T, len); 284 265 self.forget_remaining_elements(); 285 266 return Err(array::IntoIter::new_unchecked(raw_ary, 0..len)); 286 267 } ··· 289 270 // Safety: `len` is larger than the array size. Copy a fixed amount here to fully initialize 290 271 // the array. 291 272 return unsafe { 292 - ptr::copy_nonoverlapping(self.ptr, raw_ary.as_mut_ptr() as *mut T, N); 273 + ptr::copy_nonoverlapping(self.ptr.as_ptr(), raw_ary.as_mut_ptr() as *mut T, N); 293 274 self.ptr = self.ptr.add(N); 294 275 Ok(raw_ary.transpose().assume_init()) 295 276 }; ··· 307 288 // Also note the implementation of `Self: TrustedRandomAccess` requires 308 289 // that `T: Copy` so reading elements from the buffer doesn't invalidate 309 290 // them for `Drop`. 310 - unsafe { if T::IS_ZST { mem::zeroed() } else { ptr::read(self.ptr.add(i)) } } 291 + unsafe { if T::IS_ZST { mem::zeroed() } else { self.ptr.add(i).read() } } 311 292 } 312 293 } 313 294 ··· 315 296 impl<T, A: Allocator> DoubleEndedIterator for IntoIter<T, A> { 316 297 #[inline] 317 298 fn next_back(&mut self) -> Option<T> { 318 - if self.end == self.ptr { 319 - None 320 - } else if T::IS_ZST { 321 - // See above for why 'ptr.offset' isn't used 322 - self.end = self.end.wrapping_byte_sub(1); 299 + if T::IS_ZST { 300 + if self.end as *mut _ == self.ptr.as_ptr() { 301 + None 302 + } else { 303 + // See above for why 'ptr.offset' isn't used 304 + self.end = self.end.wrapping_byte_sub(1); 323 305 324 - // Make up a value of this ZST. 325 - Some(unsafe { mem::zeroed() }) 306 + // Make up a value of this ZST. 307 + Some(unsafe { mem::zeroed() }) 308 + } 326 309 } else { 327 - self.end = unsafe { self.end.sub(1) }; 310 + if non_null!(self.end, T) == self.ptr { 311 + None 312 + } else { 313 + let new_end = unsafe { non_null!(self.end, T).sub(1) }; 314 + *non_null!(mut self.end, T) = new_end; 328 315 329 - Some(unsafe { ptr::read(self.end) }) 316 + Some(unsafe { ptr::read(new_end.as_ptr()) }) 317 + } 330 318 } 331 319 } 332 320 ··· 359 333 #[stable(feature = "rust1", since = "1.0.0")] 360 334 impl<T, A: Allocator> ExactSizeIterator for IntoIter<T, A> { 361 335 fn is_empty(&self) -> bool { 362 - self.ptr == self.end 336 + if T::IS_ZST { 337 + self.ptr.as_ptr() == self.end as *mut _ 338 + } else { 339 + self.ptr == non_null!(self.end, T) 340 + } 363 341 } 364 342 } 365 343
+71 -30
rust/alloc/vec/mod.rs
··· 360 360 /// 361 361 /// `vec![x; n]`, `vec![a, b, c, d]`, and 362 362 /// [`Vec::with_capacity(n)`][`Vec::with_capacity`], will all produce a `Vec` 363 - /// with exactly the requested capacity. If <code>[len] == [capacity]</code>, 363 + /// with at least the requested capacity. If <code>[len] == [capacity]</code>, 364 364 /// (as is the case for the [`vec!`] macro), then a `Vec<T>` can be converted to 365 365 /// and from a [`Box<[T]>`][owned slice] without reallocating or moving the elements. 366 366 /// ··· 447 447 /// 448 448 /// # Panics 449 449 /// 450 - /// Panics if the new capacity exceeds `isize::MAX` bytes. 450 + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. 451 451 /// 452 452 /// # Examples 453 453 /// ··· 690 690 /// 691 691 /// # Panics 692 692 /// 693 - /// Panics if the new capacity exceeds `isize::MAX` bytes. 693 + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. 694 694 /// 695 695 /// # Examples 696 696 /// ··· 1013 1013 /// 1014 1014 /// # Panics 1015 1015 /// 1016 - /// Panics if the new capacity exceeds `isize::MAX` bytes. 1016 + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. 1017 1017 /// 1018 1018 /// # Examples 1019 1019 /// ··· 1043 1043 /// 1044 1044 /// # Panics 1045 1045 /// 1046 - /// Panics if the new capacity exceeds `isize::MAX` bytes. 1046 + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. 1047 1047 /// 1048 1048 /// # Examples 1049 1049 /// ··· 1140 1140 1141 1141 /// Shrinks the capacity of the vector as much as possible. 1142 1142 /// 1143 - /// It will drop down as close as possible to the length but the allocator 1144 - /// may still inform the vector that there is space for a few more elements. 1143 + /// The behavior of this method depends on the allocator, which may either shrink the vector 1144 + /// in-place or reallocate. The resulting vector might still have some excess capacity, just as 1145 + /// is the case for [`with_capacity`]. See [`Allocator::shrink`] for more details. 1146 + /// 1147 + /// [`with_capacity`]: Vec::with_capacity 1145 1148 /// 1146 1149 /// # Examples 1147 1150 /// ··· 1194 1191 1195 1192 /// Converts the vector into [`Box<[T]>`][owned slice]. 1196 1193 /// 1197 - /// If the vector has excess capacity, its items will be moved into a 1198 - /// newly-allocated buffer with exactly the right capacity. 1194 + /// Before doing the conversion, this method discards excess capacity like [`shrink_to_fit`]. 1199 1195 /// 1200 1196 /// [owned slice]: Box 1197 + /// [`shrink_to_fit`]: Vec::shrink_to_fit 1201 1198 /// 1202 1199 /// # Examples 1203 1200 /// ··· 2020 2017 /// 2021 2018 /// # Panics 2022 2019 /// 2023 - /// Panics if the new capacity exceeds `isize::MAX` bytes. 2020 + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. 2024 2021 /// 2025 2022 /// # Examples 2026 2023 /// ··· 2136 2133 } else { 2137 2134 unsafe { 2138 2135 self.len -= 1; 2139 - core::intrinsics::assume(self.len < self.capacity()); 2136 + core::hint::assert_unchecked(self.len < self.capacity()); 2140 2137 Some(ptr::read(self.as_ptr().add(self.len()))) 2141 2138 } 2142 2139 } ··· 2146 2143 /// 2147 2144 /// # Panics 2148 2145 /// 2149 - /// Panics if the new capacity exceeds `isize::MAX` bytes. 2146 + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. 2150 2147 /// 2151 2148 /// # Examples 2152 2149 /// ··· 2318 2315 /// `[at, len)`. After the call, the original vector will be left containing 2319 2316 /// the elements `[0, at)` with its previous capacity unchanged. 2320 2317 /// 2318 + /// - If you want to take ownership of the entire contents and capacity of 2319 + /// the vector, see [`mem::take`] or [`mem::replace`]. 2320 + /// - If you don't need the returned vector at all, see [`Vec::truncate`]. 2321 + /// - If you want to take ownership of an arbitrary subslice, or you don't 2322 + /// necessarily want to store the removed items in a vector, see [`Vec::drain`]. 2323 + /// 2321 2324 /// # Panics 2322 2325 /// 2323 2326 /// Panics if `at > len`. ··· 2353 2344 2354 2345 if at > self.len() { 2355 2346 assert_failed(at, self.len()); 2356 - } 2357 - 2358 - if at == 0 { 2359 - // the new vector can take over the original buffer and avoid the copy 2360 - return mem::replace( 2361 - self, 2362 - Vec::with_capacity_in(self.capacity(), self.allocator().clone()), 2363 - ); 2364 2347 } 2365 2348 2366 2349 let other_len = self.len - at; ··· 3028 3027 } 3029 3028 } 3030 3029 3030 + /// Collects an iterator into a Vec, commonly called via [`Iterator::collect()`] 3031 + /// 3032 + /// # Allocation behavior 3033 + /// 3034 + /// In general `Vec` does not guarantee any particular growth or allocation strategy. 3035 + /// That also applies to this trait impl. 3036 + /// 3037 + /// **Note:** This section covers implementation details and is therefore exempt from 3038 + /// stability guarantees. 3039 + /// 3040 + /// Vec may use any or none of the following strategies, 3041 + /// depending on the supplied iterator: 3042 + /// 3043 + /// * preallocate based on [`Iterator::size_hint()`] 3044 + /// * and panic if the number of items is outside the provided lower/upper bounds 3045 + /// * use an amortized growth strategy similar to `pushing` one item at a time 3046 + /// * perform the iteration in-place on the original allocation backing the iterator 3047 + /// 3048 + /// The last case warrants some attention. It is an optimization that in many cases reduces peak memory 3049 + /// consumption and improves cache locality. But when big, short-lived allocations are created, 3050 + /// only a small fraction of their items get collected, no further use is made of the spare capacity 3051 + /// and the resulting `Vec` is moved into a longer-lived structure, then this can lead to the large 3052 + /// allocations having their lifetimes unnecessarily extended which can result in increased memory 3053 + /// footprint. 3054 + /// 3055 + /// In cases where this is an issue, the excess capacity can be discarded with [`Vec::shrink_to()`], 3056 + /// [`Vec::shrink_to_fit()`] or by collecting into [`Box<[T]>`][owned slice] instead, which additionally reduces 3057 + /// the size of the long-lived struct. 3058 + /// 3059 + /// [owned slice]: Box 3060 + /// 3061 + /// ```rust 3062 + /// # use std::sync::Mutex; 3063 + /// static LONG_LIVED: Mutex<Vec<Vec<u16>>> = Mutex::new(Vec::new()); 3064 + /// 3065 + /// for i in 0..10 { 3066 + /// let big_temporary: Vec<u16> = (0..1024).collect(); 3067 + /// // discard most items 3068 + /// let mut result: Vec<_> = big_temporary.into_iter().filter(|i| i % 100 == 0).collect(); 3069 + /// // without this a lot of unused capacity might be moved into the global 3070 + /// result.shrink_to_fit(); 3071 + /// LONG_LIVED.lock().unwrap().push(result); 3072 + /// } 3073 + /// ``` 3031 3074 #[cfg(not(no_global_oom_handling))] 3032 3075 #[stable(feature = "rust1", since = "1.0.0")] 3033 3076 impl<T> FromIterator<T> for Vec<T> { ··· 3114 3069 begin.add(me.len()) as *const T 3115 3070 }; 3116 3071 let cap = me.buf.capacity(); 3117 - IntoIter { 3118 - buf: NonNull::new_unchecked(begin), 3119 - phantom: PhantomData, 3120 - cap, 3121 - alloc, 3122 - ptr: begin, 3123 - end, 3124 - } 3072 + let buf = NonNull::new_unchecked(begin); 3073 + IntoIter { buf, phantom: PhantomData, cap, alloc, ptr: buf, end } 3125 3074 } 3126 3075 } 3127 3076 } ··· 3637 3598 impl<T, A: Allocator> From<Vec<T, A>> for Box<[T], A> { 3638 3599 /// Convert a vector into a boxed slice. 3639 3600 /// 3640 - /// If `v` has excess capacity, its items will be moved into a 3641 - /// newly-allocated buffer with exactly the right capacity. 3601 + /// Before doing the conversion, this method discards excess capacity like [`Vec::shrink_to_fit`]. 3602 + /// 3603 + /// [owned slice]: Box 3604 + /// [`Vec::shrink_to_fit`]: Vec::shrink_to_fit 3642 3605 /// 3643 3606 /// # Examples 3644 3607 ///
-1
rust/kernel/lib.rs
··· 16 16 #![feature(coerce_unsized)] 17 17 #![feature(dispatch_from_dyn)] 18 18 #![feature(new_uninit)] 19 - #![feature(offset_of)] 20 19 #![feature(receiver_trait)] 21 20 #![feature(unsize)] 22 21
+1 -1
scripts/Makefile.build
··· 263 263 # Compile Rust sources (.rs) 264 264 # --------------------------------------------------------------------------- 265 265 266 - rust_allowed_features := new_uninit,offset_of 266 + rust_allowed_features := new_uninit 267 267 268 268 # `--out-dir` is required to avoid temporaries being created by `rustc` in the 269 269 # current working directory, which may be not accessible in the out-of-tree
+1 -1
scripts/min-tool-version.sh
··· 33 33 fi 34 34 ;; 35 35 rustc) 36 - echo 1.76.0 36 + echo 1.77.1 37 37 ;; 38 38 bindgen) 39 39 echo 0.65.1