Skip to content

Commit d6c959c

Browse files
authored
Rollup merge of #95298 - jhorstmann:fix-double-drop-of-allocator-in-vec-into-iter, r=oli-obk
Fix double drop of allocator in IntoIter impl of Vec Fixes #95269 The `drop` impl of `IntoIter` reconstructs a `RawVec` from `buf`, `cap` and `alloc`, when that `RawVec` is dropped it also drops the allocator. To avoid dropping the allocator twice we wrap it in `ManuallyDrop` in the `InttoIter` struct. Note this is my first contribution to the standard library, so I might be missing some details or a better way to solve this.
2 parents 86388f6 + d9a438d commit d6c959c

File tree

3 files changed

+38
-7
lines changed

3 files changed

+38
-7
lines changed

library/alloc/src/vec/into_iter.rs

+9-6
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ use core::iter::{
88
FusedIterator, InPlaceIterable, SourceIter, TrustedLen, TrustedRandomAccessNoCoerce,
99
};
1010
use core::marker::PhantomData;
11-
use core::mem::{self};
11+
use core::mem::{self, ManuallyDrop};
12+
use core::ops::Deref;
1213
use core::ptr::{self, NonNull};
1314
use core::slice::{self};
1415

@@ -32,7 +33,9 @@ pub struct IntoIter<
3233
pub(super) buf: NonNull<T>,
3334
pub(super) phantom: PhantomData<T>,
3435
pub(super) cap: usize,
35-
pub(super) alloc: A,
36+
// the drop impl reconstructs a RawVec from buf, cap and alloc
37+
// to avoid dropping the allocator twice we need to wrap it into ManuallyDrop
38+
pub(super) alloc: ManuallyDrop<A>,
3639
pub(super) ptr: *const T,
3740
pub(super) end: *const T,
3841
}
@@ -295,11 +298,11 @@ where
295298
impl<T: Clone, A: Allocator + Clone> Clone for IntoIter<T, A> {
296299
#[cfg(not(test))]
297300
fn clone(&self) -> Self {
298-
self.as_slice().to_vec_in(self.alloc.clone()).into_iter()
301+
self.as_slice().to_vec_in(self.alloc.deref().clone()).into_iter()
299302
}
300303
#[cfg(test)]
301304
fn clone(&self) -> Self {
302-
crate::slice::to_vec(self.as_slice(), self.alloc.clone()).into_iter()
305+
crate::slice::to_vec(self.as_slice(), self.alloc.deref().clone()).into_iter()
303306
}
304307
}
305308

@@ -311,8 +314,8 @@ unsafe impl<#[may_dangle] T, A: Allocator> Drop for IntoIter<T, A> {
311314
impl<T, A: Allocator> Drop for DropGuard<'_, T, A> {
312315
fn drop(&mut self) {
313316
unsafe {
314-
// `IntoIter::alloc` is not used anymore after this
315-
let alloc = ptr::read(&self.0.alloc);
317+
// `IntoIter::alloc` is not used anymore after this and will be dropped by RawVec
318+
let alloc = ManuallyDrop::take(&mut self.0.alloc);
316319
// RawVec handles deallocation
317320
let _ = RawVec::from_raw_parts_in(self.0.buf.as_ptr(), self.0.cap, alloc);
318321
}

library/alloc/src/vec/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2579,7 +2579,7 @@ impl<T, A: Allocator> IntoIterator for Vec<T, A> {
25792579
fn into_iter(self) -> IntoIter<T, A> {
25802580
unsafe {
25812581
let mut me = ManuallyDrop::new(self);
2582-
let alloc = ptr::read(me.allocator());
2582+
let alloc = ManuallyDrop::new(ptr::read(me.allocator()));
25832583
let begin = me.as_mut_ptr();
25842584
let end = if mem::size_of::<T>() == 0 {
25852585
arith_offset(begin as *const i8, me.len() as isize) as *const T

library/alloc/tests/vec.rs

+28
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
use core::alloc::{Allocator, Layout};
2+
use core::ptr::NonNull;
3+
use std::alloc::System;
14
use std::assert_matches::assert_matches;
25
use std::borrow::Cow;
36
use std::cell::Cell;
@@ -991,6 +994,31 @@ fn test_into_iter_advance_by() {
991994
assert_eq!(i.len(), 0);
992995
}
993996

997+
#[test]
998+
fn test_into_iter_drop_allocator() {
999+
struct ReferenceCountedAllocator<'a>(DropCounter<'a>);
1000+
1001+
unsafe impl Allocator for ReferenceCountedAllocator<'_> {
1002+
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, core::alloc::AllocError> {
1003+
System.allocate(layout)
1004+
}
1005+
1006+
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
1007+
System.deallocate(ptr, layout)
1008+
}
1009+
}
1010+
1011+
let mut drop_count = 0;
1012+
1013+
let allocator = ReferenceCountedAllocator(DropCounter { count: &mut drop_count });
1014+
let _ = Vec::<u32, _>::new_in(allocator);
1015+
assert_eq!(drop_count, 1);
1016+
1017+
let allocator = ReferenceCountedAllocator(DropCounter { count: &mut drop_count });
1018+
let _ = Vec::<u32, _>::new_in(allocator).into_iter();
1019+
assert_eq!(drop_count, 2);
1020+
}
1021+
9941022
#[test]
9951023
fn test_from_iter_specialization() {
9961024
let src: Vec<usize> = vec![0usize; 1];

0 commit comments

Comments
 (0)