Skip to content

Commit 1633e2a

Browse files
committed
Auto merge of #120352 - cuviper:beta-next, r=cuviper
[beta] backports - Remove alignment-changing in-place collect #120116 - fix: Drop guard was deallocating with the incorrect size #120145 r? ghost
2 parents a27ad23 + 73b36b1 commit 1633e2a

File tree

4 files changed

+40
-25
lines changed

4 files changed

+40
-25
lines changed

library/alloc/src/vec/in_place_collect.rs

+14-9
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
//! This is handled by the [`InPlaceDrop`] guard for sink items (`U`) and by
7373
//! [`vec::IntoIter::forget_allocation_drop_remaining()`] for remaining source items (`T`).
7474
//!
75-
//! If dropping any remaining source item (`T`) panics then [`InPlaceDstBufDrop`] will handle dropping
75+
//! If dropping any remaining source item (`T`) panics then [`InPlaceDstDataSrcBufDrop`] will handle dropping
7676
//! the already collected sink items (`U`) and freeing the allocation.
7777
//!
7878
//! [`vec::IntoIter::forget_allocation_drop_remaining()`]: super::IntoIter::forget_allocation_drop_remaining()
@@ -158,17 +158,20 @@ use crate::alloc::{handle_alloc_error, Global};
158158
use core::alloc::Allocator;
159159
use core::alloc::Layout;
160160
use core::iter::{InPlaceIterable, SourceIter, TrustedRandomAccessNoCoerce};
161+
use core::marker::PhantomData;
161162
use core::mem::{self, ManuallyDrop, SizedTypeProperties};
162163
use core::num::NonZeroUsize;
163164
use core::ptr::{self, NonNull};
164165

165-
use super::{InPlaceDrop, InPlaceDstBufDrop, SpecFromIter, SpecFromIterNested, Vec};
166+
use super::{InPlaceDrop, InPlaceDstDataSrcBufDrop, SpecFromIter, SpecFromIterNested, Vec};
166167

167168
const fn in_place_collectible<DEST, SRC>(
168169
step_merge: Option<NonZeroUsize>,
169170
step_expand: Option<NonZeroUsize>,
170171
) -> bool {
171-
if const { SRC::IS_ZST || DEST::IS_ZST || mem::align_of::<SRC>() < mem::align_of::<DEST>() } {
172+
// Require matching alignments because an alignment-changing realloc is inefficient on many
173+
// system allocators and better implementations would require the unstable Allocator trait.
174+
if const { SRC::IS_ZST || DEST::IS_ZST || mem::align_of::<SRC>() != mem::align_of::<DEST>() } {
172175
return false;
173176
}
174177

@@ -188,7 +191,8 @@ const fn in_place_collectible<DEST, SRC>(
188191

189192
const fn needs_realloc<SRC, DEST>(src_cap: usize, dst_cap: usize) -> bool {
190193
if const { mem::align_of::<SRC>() != mem::align_of::<DEST>() } {
191-
return src_cap > 0;
194+
// FIXME: use unreachable! once that works in const
195+
panic!("in_place_collectible() prevents this");
192196
}
193197

194198
// If src type size is an integer multiple of the destination type size then
@@ -262,7 +266,7 @@ where
262266
);
263267
}
264268

265-
// The ownership of the allocation and the new `T` values is temporarily moved into `dst_guard`.
269+
// The ownership of the source allocation and the new `T` values is temporarily moved into `dst_guard`.
266270
// This is safe because
267271
// * `forget_allocation_drop_remaining` immediately forgets the allocation
268272
// before any panic can occur in order to avoid any double free, and then proceeds to drop
@@ -273,11 +277,12 @@ where
273277
// Note: This access to the source wouldn't be allowed by the TrustedRandomIteratorNoCoerce
274278
// contract (used by SpecInPlaceCollect below). But see the "O(1) collect" section in the
275279
// module documentation why this is ok anyway.
276-
let dst_guard = InPlaceDstBufDrop { ptr: dst_buf, len, cap: dst_cap };
280+
let dst_guard =
281+
InPlaceDstDataSrcBufDrop { ptr: dst_buf, len, src_cap, src: PhantomData::<I::Src> };
277282
src.forget_allocation_drop_remaining();
278283

279-
// Adjust the allocation if the alignment didn't match or the source had a capacity in bytes
280-
// that wasn't a multiple of the destination type size.
284+
// Adjust the allocation if the source had a capacity in bytes that wasn't a multiple
285+
// of the destination type size.
281286
// Since the discrepancy should generally be small this should only result in some
282287
// bookkeeping updates and no memmove.
283288
if needs_realloc::<I::Src, T>(src_cap, dst_cap) {
@@ -290,7 +295,7 @@ where
290295
let src_size = mem::size_of::<I::Src>().unchecked_mul(src_cap);
291296
let old_layout = Layout::from_size_align_unchecked(src_size, src_align);
292297

293-
// The must be equal or smaller for in-place iteration to be possible
298+
// The allocation must be equal or smaller for in-place iteration to be possible
294299
// therefore the new layout must be ≤ the old one and therefore valid.
295300
let dst_align = mem::align_of::<T>();
296301
let dst_size = mem::size_of::<T>().unchecked_mul(dst_cap);

library/alloc/src/vec/in_place_drop.rs

+18-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1-
use core::ptr::{self};
1+
use core::marker::PhantomData;
2+
use core::ptr::{self, drop_in_place};
23
use core::slice::{self};
34

5+
use crate::alloc::Global;
6+
use crate::raw_vec::RawVec;
7+
48
// A helper struct for in-place iteration that drops the destination slice of iteration,
59
// i.e. the head. The source slice (the tail) is dropped by IntoIter.
610
pub(super) struct InPlaceDrop<T> {
@@ -23,17 +27,23 @@ impl<T> Drop for InPlaceDrop<T> {
2327
}
2428
}
2529

26-
// A helper struct for in-place collection that drops the destination allocation and elements,
27-
// to avoid leaking them if some other destructor panics.
28-
pub(super) struct InPlaceDstBufDrop<T> {
29-
pub(super) ptr: *mut T,
30+
// A helper struct for in-place collection that drops the destination items together with
31+
// the source allocation - i.e. before the reallocation happened - to avoid leaking them
32+
// if some other destructor panics.
33+
pub(super) struct InPlaceDstDataSrcBufDrop<Src, Dest> {
34+
pub(super) ptr: *mut Dest,
3035
pub(super) len: usize,
31-
pub(super) cap: usize,
36+
pub(super) src_cap: usize,
37+
pub(super) src: PhantomData<Src>,
3238
}
3339

34-
impl<T> Drop for InPlaceDstBufDrop<T> {
40+
impl<Src, Dest> Drop for InPlaceDstDataSrcBufDrop<Src, Dest> {
3541
#[inline]
3642
fn drop(&mut self) {
37-
unsafe { super::Vec::from_raw_parts(self.ptr, self.len, self.cap) };
43+
unsafe {
44+
let _drop_allocation =
45+
RawVec::<Src>::from_raw_parts_in(self.ptr.cast::<Src>(), self.src_cap, Global);
46+
drop_in_place(core::ptr::slice_from_raw_parts_mut::<Dest>(self.ptr, self.len));
47+
};
3848
}
3949
}

library/alloc/src/vec/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ use self::set_len_on_drop::SetLenOnDrop;
123123
mod set_len_on_drop;
124124

125125
#[cfg(not(no_global_oom_handling))]
126-
use self::in_place_drop::{InPlaceDrop, InPlaceDstBufDrop};
126+
use self::in_place_drop::{InPlaceDrop, InPlaceDstDataSrcBufDrop};
127127

128128
#[cfg(not(no_global_oom_handling))]
129129
mod in_place_drop;

library/alloc/src/vec/spec_from_iter.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ use super::{IntoIter, SpecExtend, SpecFromIterNested, Vec};
1313
/// +-+-----------+
1414
/// |
1515
/// v
16-
/// +-+-------------------------------+ +---------------------+
17-
/// |SpecFromIter +---->+SpecFromIterNested |
18-
/// |where I: | | |where I: |
19-
/// | Iterator (default)----------+ | | Iterator (default) |
20-
/// | vec::IntoIter | | | TrustedLen |
21-
/// | SourceIterMarker---fallback-+ | +---------------------+
22-
/// +---------------------------------+
16+
/// +-+---------------------------------+ +---------------------+
17+
/// |SpecFromIter +---->+SpecFromIterNested |
18+
/// |where I: | | |where I: |
19+
/// | Iterator (default)------------+ | | Iterator (default) |
20+
/// | vec::IntoIter | | | TrustedLen |
21+
/// | InPlaceCollect--(fallback to)-+ | +---------------------+
22+
/// +-----------------------------------+
2323
/// ```
2424
pub(super) trait SpecFromIter<T, I> {
2525
fn from_iter(iter: I) -> Self;

0 commit comments

Comments
 (0)