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
This commit is contained in:
commit
1633e2ae06
4 changed files with 40 additions and 25 deletions
|
|
@ -72,7 +72,7 @@
|
|||
//! This is handled by the [`InPlaceDrop`] guard for sink items (`U`) and by
|
||||
//! [`vec::IntoIter::forget_allocation_drop_remaining()`] for remaining source items (`T`).
|
||||
//!
|
||||
//! If dropping any remaining source item (`T`) panics then [`InPlaceDstBufDrop`] will handle dropping
|
||||
//! If dropping any remaining source item (`T`) panics then [`InPlaceDstDataSrcBufDrop`] will handle dropping
|
||||
//! the already collected sink items (`U`) and freeing the allocation.
|
||||
//!
|
||||
//! [`vec::IntoIter::forget_allocation_drop_remaining()`]: super::IntoIter::forget_allocation_drop_remaining()
|
||||
|
|
@ -158,17 +158,20 @@ use crate::alloc::{handle_alloc_error, Global};
|
|||
use core::alloc::Allocator;
|
||||
use core::alloc::Layout;
|
||||
use core::iter::{InPlaceIterable, SourceIter, TrustedRandomAccessNoCoerce};
|
||||
use core::marker::PhantomData;
|
||||
use core::mem::{self, ManuallyDrop, SizedTypeProperties};
|
||||
use core::num::NonZeroUsize;
|
||||
use core::ptr::{self, NonNull};
|
||||
|
||||
use super::{InPlaceDrop, InPlaceDstBufDrop, SpecFromIter, SpecFromIterNested, Vec};
|
||||
use super::{InPlaceDrop, InPlaceDstDataSrcBufDrop, SpecFromIter, SpecFromIterNested, Vec};
|
||||
|
||||
const fn in_place_collectible<DEST, SRC>(
|
||||
step_merge: Option<NonZeroUsize>,
|
||||
step_expand: Option<NonZeroUsize>,
|
||||
) -> bool {
|
||||
if const { SRC::IS_ZST || DEST::IS_ZST || mem::align_of::<SRC>() < mem::align_of::<DEST>() } {
|
||||
// Require matching alignments because an alignment-changing realloc is inefficient on many
|
||||
// system allocators and better implementations would require the unstable Allocator trait.
|
||||
if const { SRC::IS_ZST || DEST::IS_ZST || mem::align_of::<SRC>() != mem::align_of::<DEST>() } {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -188,7 +191,8 @@ const fn in_place_collectible<DEST, SRC>(
|
|||
|
||||
const fn needs_realloc<SRC, DEST>(src_cap: usize, dst_cap: usize) -> bool {
|
||||
if const { mem::align_of::<SRC>() != mem::align_of::<DEST>() } {
|
||||
return src_cap > 0;
|
||||
// FIXME: use unreachable! once that works in const
|
||||
panic!("in_place_collectible() prevents this");
|
||||
}
|
||||
|
||||
// If src type size is an integer multiple of the destination type size then
|
||||
|
|
@ -262,7 +266,7 @@ where
|
|||
);
|
||||
}
|
||||
|
||||
// The ownership of the allocation and the new `T` values is temporarily moved into `dst_guard`.
|
||||
// The ownership of the source allocation and the new `T` values is temporarily moved into `dst_guard`.
|
||||
// This is safe because
|
||||
// * `forget_allocation_drop_remaining` immediately forgets the allocation
|
||||
// before any panic can occur in order to avoid any double free, and then proceeds to drop
|
||||
|
|
@ -273,11 +277,12 @@ where
|
|||
// Note: This access to the source wouldn't be allowed by the TrustedRandomIteratorNoCoerce
|
||||
// contract (used by SpecInPlaceCollect below). But see the "O(1) collect" section in the
|
||||
// module documentation why this is ok anyway.
|
||||
let dst_guard = InPlaceDstBufDrop { ptr: dst_buf, len, cap: dst_cap };
|
||||
let dst_guard =
|
||||
InPlaceDstDataSrcBufDrop { ptr: dst_buf, len, src_cap, src: PhantomData::<I::Src> };
|
||||
src.forget_allocation_drop_remaining();
|
||||
|
||||
// Adjust the allocation if the alignment didn't match or the source had a capacity in bytes
|
||||
// that wasn't a multiple of the destination type size.
|
||||
// Adjust the allocation if the source had a capacity in bytes that wasn't a multiple
|
||||
// of the destination type size.
|
||||
// Since the discrepancy should generally be small this should only result in some
|
||||
// bookkeeping updates and no memmove.
|
||||
if needs_realloc::<I::Src, T>(src_cap, dst_cap) {
|
||||
|
|
@ -290,7 +295,7 @@ where
|
|||
let src_size = mem::size_of::<I::Src>().unchecked_mul(src_cap);
|
||||
let old_layout = Layout::from_size_align_unchecked(src_size, src_align);
|
||||
|
||||
// The must be equal or smaller for in-place iteration to be possible
|
||||
// The allocation must be equal or smaller for in-place iteration to be possible
|
||||
// therefore the new layout must be ≤ the old one and therefore valid.
|
||||
let dst_align = mem::align_of::<T>();
|
||||
let dst_size = mem::size_of::<T>().unchecked_mul(dst_cap);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
use core::ptr::{self};
|
||||
use core::marker::PhantomData;
|
||||
use core::ptr::{self, drop_in_place};
|
||||
use core::slice::{self};
|
||||
|
||||
use crate::alloc::Global;
|
||||
use crate::raw_vec::RawVec;
|
||||
|
||||
// A helper struct for in-place iteration that drops the destination slice of iteration,
|
||||
// i.e. the head. The source slice (the tail) is dropped by IntoIter.
|
||||
pub(super) struct InPlaceDrop<T> {
|
||||
|
|
@ -23,17 +27,23 @@ impl<T> Drop for InPlaceDrop<T> {
|
|||
}
|
||||
}
|
||||
|
||||
// A helper struct for in-place collection that drops the destination allocation and elements,
|
||||
// to avoid leaking them if some other destructor panics.
|
||||
pub(super) struct InPlaceDstBufDrop<T> {
|
||||
pub(super) ptr: *mut T,
|
||||
// A helper struct for in-place collection that drops the destination items together with
|
||||
// the source allocation - i.e. before the reallocation happened - to avoid leaking them
|
||||
// if some other destructor panics.
|
||||
pub(super) struct InPlaceDstDataSrcBufDrop<Src, Dest> {
|
||||
pub(super) ptr: *mut Dest,
|
||||
pub(super) len: usize,
|
||||
pub(super) cap: usize,
|
||||
pub(super) src_cap: usize,
|
||||
pub(super) src: PhantomData<Src>,
|
||||
}
|
||||
|
||||
impl<T> Drop for InPlaceDstBufDrop<T> {
|
||||
impl<Src, Dest> Drop for InPlaceDstDataSrcBufDrop<Src, Dest> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe { super::Vec::from_raw_parts(self.ptr, self.len, self.cap) };
|
||||
unsafe {
|
||||
let _drop_allocation =
|
||||
RawVec::<Src>::from_raw_parts_in(self.ptr.cast::<Src>(), self.src_cap, Global);
|
||||
drop_in_place(core::ptr::slice_from_raw_parts_mut::<Dest>(self.ptr, self.len));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ use self::set_len_on_drop::SetLenOnDrop;
|
|||
mod set_len_on_drop;
|
||||
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
use self::in_place_drop::{InPlaceDrop, InPlaceDstBufDrop};
|
||||
use self::in_place_drop::{InPlaceDrop, InPlaceDstDataSrcBufDrop};
|
||||
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
mod in_place_drop;
|
||||
|
|
|
|||
|
|
@ -13,13 +13,13 @@ use super::{IntoIter, SpecExtend, SpecFromIterNested, Vec};
|
|||
/// +-+-----------+
|
||||
/// |
|
||||
/// v
|
||||
/// +-+-------------------------------+ +---------------------+
|
||||
/// |SpecFromIter +---->+SpecFromIterNested |
|
||||
/// |where I: | | |where I: |
|
||||
/// | Iterator (default)----------+ | | Iterator (default) |
|
||||
/// | vec::IntoIter | | | TrustedLen |
|
||||
/// | SourceIterMarker---fallback-+ | +---------------------+
|
||||
/// +---------------------------------+
|
||||
/// +-+---------------------------------+ +---------------------+
|
||||
/// |SpecFromIter +---->+SpecFromIterNested |
|
||||
/// |where I: | | |where I: |
|
||||
/// | Iterator (default)------------+ | | Iterator (default) |
|
||||
/// | vec::IntoIter | | | TrustedLen |
|
||||
/// | InPlaceCollect--(fallback to)-+ | +---------------------+
|
||||
/// +-----------------------------------+
|
||||
/// ```
|
||||
pub(super) trait SpecFromIter<T, I> {
|
||||
fn from_iter(iter: I) -> Self;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue