Skip to content

Commit b0b63ec

Browse files
authored
Rollup merge of #82564 - WaffleLapkin:revert_spare_mut, r=RalfJung
Revert `Vec::spare_capacity_mut` impl to prevent pointers invalidation The implementation was changed in #79015. Later it was [pointed out](#81944 (comment)) that the implementation invalidates pointers to the buffer (initialized elements) by creating a unique reference to the buffer. This PR reverts the implementation. r? `@RalfJung`
2 parents ae2482e + 950f121 commit b0b63ec

File tree

3 files changed

+19
-10
lines changed

3 files changed

+19
-10
lines changed

library/alloc/src/vec/mod.rs

+14-10
Original file line numberDiff line numberDiff line change
@@ -1877,7 +1877,15 @@ impl<T, A: Allocator> Vec<T, A> {
18771877
#[unstable(feature = "vec_spare_capacity", issue = "75017")]
18781878
#[inline]
18791879
pub fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit<T>] {
1880-
self.split_at_spare_mut().1
1880+
// Note:
1881+
// This method is not implemented in terms of `split_at_spare_mut`,
1882+
// to prevent invalidation of pointers to the buffer.
1883+
unsafe {
1884+
slice::from_raw_parts_mut(
1885+
self.as_mut_ptr().add(self.len) as *mut MaybeUninit<T>,
1886+
self.buf.capacity() - self.len,
1887+
)
1888+
}
18811889
}
18821890

18831891
/// Returns vector content as a slice of `T`, along with the remaining spare
@@ -1934,20 +1942,16 @@ impl<T, A: Allocator> Vec<T, A> {
19341942
#[unstable(feature = "vec_split_at_spare", issue = "81944")]
19351943
#[inline]
19361944
pub fn split_at_spare_mut(&mut self) -> (&mut [T], &mut [MaybeUninit<T>]) {
1937-
let ptr = self.as_mut_ptr();
1938-
1939-
// SAFETY:
1940-
// - `ptr` is guaranteed to be in bounds for `capacity` elements
1941-
// - `len` is guaranteed to less or equal to `capacity`
1942-
// - `MaybeUninit<T>` has the same layout as `T`
1943-
let spare_ptr = unsafe { ptr.cast::<MaybeUninit<T>>().add(self.len) };
1945+
let Range { start: ptr, end: spare_ptr } = self.as_mut_ptr_range();
1946+
let spare_ptr = spare_ptr.cast::<MaybeUninit<T>>();
1947+
let spare_len = self.buf.capacity() - self.len;
19441948

19451949
// SAFETY:
19461950
// - `ptr` is guaranteed to be valid for `len` elements
1947-
// - `spare_ptr` is offseted from `ptr` by `len`, so it doesn't overlap `initialized` slice
1951+
// - `spare_ptr` is pointing one element past the buffer, so it doesn't overlap with `initialized`
19481952
unsafe {
19491953
let initialized = slice::from_raw_parts_mut(ptr, self.len);
1950-
let spare = slice::from_raw_parts_mut(spare_ptr, self.buf.capacity() - self.len);
1954+
let spare = slice::from_raw_parts_mut(spare_ptr, spare_len);
19511955

19521956
(initialized, spare)
19531957
}

library/alloc/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#![feature(vecdeque_binary_search)]
2121
#![feature(slice_group_by)]
2222
#![feature(vec_extend_from_within)]
23+
#![feature(vec_spare_capacity)]
2324

2425
use std::collections::hash_map::DefaultHasher;
2526
use std::hash::{Hash, Hasher};

library/alloc/tests/vec.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1691,6 +1691,10 @@ fn test_stable_pointers() {
16911691
next_then_drop(v.splice(5..6, vec![1; 10].into_iter().filter(|_| true))); // lower bound not exact
16921692
assert_eq!(*v0, 13);
16931693

1694+
// spare_capacity_mut
1695+
v.spare_capacity_mut();
1696+
assert_eq!(*v0, 13);
1697+
16941698
// Smoke test that would fire even outside Miri if an actual relocation happened.
16951699
*v0 -= 13;
16961700
assert_eq!(v[0], 0);

0 commit comments

Comments
 (0)