1
Fork 0

Add regression test for Vec::extend_from_within leak

This commit is contained in:
Waffle 2021-03-04 12:19:52 +03:00
parent 84e9608596
commit 1f031d95de
2 changed files with 47 additions and 3 deletions

View file

@ -1944,14 +1944,16 @@ impl<T, A: Allocator> Vec<T, A> {
pub fn split_at_spare_mut(&mut self) -> (&mut [T], &mut [MaybeUninit<T>]) {
// SAFETY:
// - len is ignored and so never changed
let (init, spare, _) = unsafe{ self.split_at_spare_mut_with_len() };
let (init, spare, _) = unsafe { self.split_at_spare_mut_with_len() };
(init, spare)
}
/// Safety: changing returned .2 (&mut usize) is considered the same as calling `.set_len(_)`.
///
/// This method is used to have unique access to all vec parts at once in `extend_from_within`.
unsafe fn split_at_spare_mut_with_len(&mut self) -> (&mut [T], &mut [MaybeUninit<T>], &mut usize) {
unsafe fn split_at_spare_mut_with_len(
&mut self,
) -> (&mut [T], &mut [MaybeUninit<T>], &mut usize) {
let Range { start: ptr, end: spare_ptr } = self.as_mut_ptr_range();
let spare_ptr = spare_ptr.cast::<MaybeUninit<T>>();
let spare_len = self.buf.capacity() - self.len;
@ -1965,7 +1967,7 @@ impl<T, A: Allocator> Vec<T, A> {
(initialized, spare, &mut self.len)
}
}
}
}
impl<T: Clone, A: Allocator> Vec<T, A> {

View file

@ -7,6 +7,7 @@ use std::mem::{size_of, swap};
use std::ops::Bound::*;
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::rc::Rc;
use std::sync::atomic::{AtomicU32, Ordering};
use std::vec::{Drain, IntoIter};
struct DropCounter<'a> {
@ -2100,3 +2101,44 @@ fn test_extend_from_within() {
assert_eq!(v, ["a", "b", "c", "b", "c", "a", "b"]);
}
// Regression test for issue #82533
#[test]
fn test_extend_from_within_panicing_clone() {
struct Panic<'dc> {
drop_count: &'dc AtomicU32,
aaaaa: bool,
}
impl Clone for Panic<'_> {
fn clone(&self) -> Self {
if self.aaaaa {
panic!("panic! at the clone");
}
Self { ..*self }
}
}
impl Drop for Panic<'_> {
fn drop(&mut self) {
self.drop_count.fetch_add(1, Ordering::SeqCst);
}
}
let count = core::sync::atomic::AtomicU32::new(0);
let mut vec = vec![
Panic { drop_count: &count, aaaaa: false },
Panic { drop_count: &count, aaaaa: true },
Panic { drop_count: &count, aaaaa: false },
];
// This should clone&append one Panic{..} at the end, and then panic while
// cloning second Panic{..}. This means that `Panic::drop` should be called
// 4 times (3 for items already in vector, 1 for just appended).
//
// Previously just appended item was leaked, making drop_count = 3, instead of 4.
std::panic::catch_unwind(move || vec.extend_from_within(..)).unwrap_err();
assert_eq!(count.load(Ordering::SeqCst), 4);
}