diff options
Diffstat (limited to 'src/liballoc')
| -rw-r--r-- | src/liballoc/tests/vec.rs | 39 | ||||
| -rw-r--r-- | src/liballoc/vec.rs | 43 |
2 files changed, 69 insertions, 13 deletions
diff --git a/src/liballoc/tests/vec.rs b/src/liballoc/tests/vec.rs index 2a9bfefc713..80acba0a3a1 100644 --- a/src/liballoc/tests/vec.rs +++ b/src/liballoc/tests/vec.rs @@ -1,6 +1,7 @@ use std::borrow::Cow; use std::collections::TryReserveError::*; use std::mem::size_of; +use std::panic::{catch_unwind, AssertUnwindSafe}; use std::vec::{Drain, IntoIter}; use std::{isize, usize}; @@ -586,6 +587,44 @@ fn test_drain_inclusive_out_of_bounds() { } #[test] +fn test_drain_leak() { + static mut DROPS: i32 = 0; + + #[derive(Debug, PartialEq)] + struct D(u32, bool); + + impl Drop for D { + fn drop(&mut self) { + unsafe { + DROPS += 1; + } + + if self.1 { + panic!("panic in `drop`"); + } + } + } + + let mut v = vec![ + D(0, false), + D(1, false), + D(2, false), + D(3, false), + D(4, true), + D(5, false), + D(6, false), + ]; + + catch_unwind(AssertUnwindSafe(|| { + v.drain(2..=5); + })) + .ok(); + + assert_eq!(unsafe { DROPS }, 4); + assert_eq!(v, vec![D(0, false), D(1, false), D(6, false),]); +} + +#[test] fn test_splice() { let mut v = vec![1, 2, 3, 4, 5]; let a = [10, 11, 12]; diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index e9cbf627846..ba71e42090c 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -2700,23 +2700,40 @@ impl<T> DoubleEndedIterator for Drain<'_, T> { #[stable(feature = "drain", since = "1.6.0")] impl<T> Drop for Drain<'_, T> { fn drop(&mut self) { - // exhaust self first - self.for_each(drop); + /// Continues dropping the remaining elements when a destructor unwinds. + struct DropGuard<'r, 'a, T>(&'r mut Drain<'a, T>); - if self.tail_len > 0 { - unsafe { - let source_vec = self.vec.as_mut(); - // memmove back untouched tail, update to new length - let start = source_vec.len(); - let tail = self.tail_start; - if tail != start { - let src = source_vec.as_ptr().add(tail); - let dst = source_vec.as_mut_ptr().add(start); - ptr::copy(src, dst, self.tail_len); + impl<'r, 'a, T> Drop for DropGuard<'r, 'a, T> { + fn drop(&mut self) { + // Continue the same loop we do below. This only runs when a destructor has + // panicked. If another one panics this will abort. + self.0.for_each(drop); + + if self.0.tail_len > 0 { + unsafe { + let source_vec = self.0.vec.as_mut(); + // memmove back untouched tail, update to new length + let start = source_vec.len(); + let tail = self.0.tail_start; + if tail != start { + let src = source_vec.as_ptr().add(tail); + let dst = source_vec.as_mut_ptr().add(start); + ptr::copy(src, dst, self.0.tail_len); + } + source_vec.set_len(start + self.0.tail_len); + } } - source_vec.set_len(start + self.tail_len); } } + + // exhaust self first + while let Some(item) = self.next() { + let guard = DropGuard(self); + drop(item); + mem::forget(guard); + } + + DropGuard(self); } } |
