Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove unnecessary add in clone_from_impl #519

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 70 additions & 7 deletions src/raw/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3577,12 +3577,23 @@ impl<T: Clone, A: Allocator + Clone> RawTable<T, A> {
.ctrl(0)
.copy_to_nonoverlapping(self.table.ctrl(0), self.table.num_ctrl_bytes());

// The cloning of elements may panic, in which case we need
// to make sure we drop only the elements that have been
// cloned so far.
let mut guard = guard((0, &mut *self), |(index, self_)| {
if T::NEEDS_DROP {
for i in 0..*index {
// The cloning of elements may panic, in which case we need to make sure we drop only
// the elements that have been cloned so far.
//
// We use `usize::MAX` as the starting index instead of zero (0) to avoid the possibility
// of removing an uninitialized element at index `0` if the clone implementation of `T`
// panicked on the first call and there was an element at index zero in the original table.
// This is possible due to the fact that we first copy the control bytes, and only then
// populate the table itself.
//
// Using `usize::MAX` as the starting index makes perfect sense, because even for T is ZST
// (mem::size_of::<T>() == 0), the index cannot be larger than `isize::MAX - ( ctrl_align - 1)`
// (see TableLayout::calculate_layout_for). This is due to the fact that for any T we need
// one control byte; therefore, storing `usize::MAX` ZST elements requires the same amount
// of memory, which is physically impossible.
let mut guard = guard((usize::MAX, &mut *self), |(index, self_)| {
if T::NEEDS_DROP && *index != usize::MAX {
for i in 0..=*index {
if self_.is_bucket_full(i) {
self_.bucket(i).drop();
}
Expand All @@ -3596,7 +3607,7 @@ impl<T: Clone, A: Allocator + Clone> RawTable<T, A> {
to.write(from.as_ref().clone());

// Update the index in case we need to unwind.
guard.0 = index + 1;
guard.0 = index;
}

// Successfully cloned all items, no need to clean up.
Expand Down Expand Up @@ -4814,4 +4825,56 @@ mod test_map {
// All allocator clones should already be dropped.
assert_eq!(dropped.load(Ordering::SeqCst), 1);
}

#[test]
#[should_panic = "panic in clone_from"]
fn test_clone_from_panic_in_first_item() {
use ::alloc::vec::Vec;

struct CheckedCloneDrop {
hash: u64,
panic_in_clone: bool,
dropped: bool,
need_drop: Vec<u64>,
}

impl Clone for CheckedCloneDrop {
fn clone(&self) -> Self {
if self.panic_in_clone {
panic!("panic in clone_from")
}
Self {
hash: self.hash,
panic_in_clone: self.panic_in_clone,
dropped: self.dropped,
need_drop: self.need_drop.clone(),
}
}
}

impl Drop for CheckedCloneDrop {
fn drop(&mut self) {
if self.dropped {
panic!("double drop");
}
self.dropped = true;
}
}

let mut table = RawTable::new();

let data = CheckedCloneDrop {
hash: 0,
panic_in_clone: true,
dropped: false,
need_drop: vec![0],
};
table.insert(data.hash, data, |v| v.hash);

assert_eq!(table.len(), 1);

let mut table_two = RawTable::new();

table_two.clone_from(&table);
}
}
Loading