diff --git a/alloc/tests/arc.rs b/alloc/tests/arc.rs index dc27c578b57ef..a259c0131ecdf 100644 --- a/alloc/tests/arc.rs +++ b/alloc/tests/arc.rs @@ -1,5 +1,5 @@ use std::any::Any; -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; use std::iter::TrustedLen; use std::mem; use std::sync::{Arc, Weak}; @@ -89,7 +89,7 @@ fn eq() { // The test code below is identical to that in `rc.rs`. // For better maintainability we therefore define this type alias. -type Rc = Arc; +type Rc = Arc; const SHARED_ITER_MAX: u16 = 100; @@ -210,6 +210,42 @@ fn weak_may_dangle() { // borrow might be used here, when `val` is dropped and runs the `Drop` code for type `std::sync::Weak` } +/// Test that a panic from a destructor does not leak the allocation. +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn panic_no_leak() { + use std::alloc::{AllocError, Allocator, Global, Layout}; + use std::panic::{AssertUnwindSafe, catch_unwind}; + use std::ptr::NonNull; + + struct AllocCount(Cell); + unsafe impl Allocator for AllocCount { + fn allocate(&self, layout: Layout) -> Result, AllocError> { + self.0.set(self.0.get() + 1); + Global.allocate(layout) + } + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + self.0.set(self.0.get() - 1); + unsafe { Global.deallocate(ptr, layout) } + } + } + + struct PanicOnDrop; + impl Drop for PanicOnDrop { + fn drop(&mut self) { + panic!("PanicOnDrop"); + } + } + + let alloc = AllocCount(Cell::new(0)); + let rc = Rc::new_in(PanicOnDrop, &alloc); + assert_eq!(alloc.0.get(), 1); + + let panic_message = catch_unwind(AssertUnwindSafe(|| drop(rc))).unwrap_err(); + assert_eq!(*panic_message.downcast_ref::<&'static str>().unwrap(), "PanicOnDrop"); + assert_eq!(alloc.0.get(), 0); +} + /// This is similar to the doc-test for `Arc::make_mut()`, but on an unsized type (slice). #[test] fn make_mut_unsized() { diff --git a/alloc/tests/boxed.rs b/alloc/tests/boxed.rs index bfc31a626fadd..2d172fc213799 100644 --- a/alloc/tests/boxed.rs +++ b/alloc/tests/boxed.rs @@ -59,6 +59,44 @@ fn box_deref_lval() { assert_eq!(x.get(), 1000); } +/// Test that a panic from a destructor does not leak the allocation. +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn panic_no_leak() { + use std::alloc::{AllocError, Allocator, Global, Layout}; + use std::panic::{AssertUnwindSafe, catch_unwind}; + use std::ptr::NonNull; + + struct AllocCount(Cell); + unsafe impl Allocator for AllocCount { + fn allocate(&self, layout: Layout) -> Result, AllocError> { + self.0.set(self.0.get() + 1); + Global.allocate(layout) + } + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + self.0.set(self.0.get() - 1); + unsafe { Global.deallocate(ptr, layout) } + } + } + + struct PanicOnDrop { + _data: u8, + } + impl Drop for PanicOnDrop { + fn drop(&mut self) { + panic!("PanicOnDrop"); + } + } + + let alloc = AllocCount(Cell::new(0)); + let b = Box::new_in(PanicOnDrop { _data: 42 }, &alloc); + assert_eq!(alloc.0.get(), 1); + + let panic_message = catch_unwind(AssertUnwindSafe(|| drop(b))).unwrap_err(); + assert_eq!(*panic_message.downcast_ref::<&'static str>().unwrap(), "PanicOnDrop"); + assert_eq!(alloc.0.get(), 0); +} + #[allow(unused)] pub struct ConstAllocator; diff --git a/alloc/tests/rc.rs b/alloc/tests/rc.rs index 29dbdcf225eb5..451765d724283 100644 --- a/alloc/tests/rc.rs +++ b/alloc/tests/rc.rs @@ -1,5 +1,5 @@ use std::any::Any; -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; use std::iter::TrustedLen; use std::mem; use std::rc::{Rc, Weak}; @@ -206,6 +206,42 @@ fn weak_may_dangle() { // borrow might be used here, when `val` is dropped and runs the `Drop` code for type `std::rc::Weak` } +/// Test that a panic from a destructor does not leak the allocation. +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn panic_no_leak() { + use std::alloc::{AllocError, Allocator, Global, Layout}; + use std::panic::{AssertUnwindSafe, catch_unwind}; + use std::ptr::NonNull; + + struct AllocCount(Cell); + unsafe impl Allocator for AllocCount { + fn allocate(&self, layout: Layout) -> Result, AllocError> { + self.0.set(self.0.get() + 1); + Global.allocate(layout) + } + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + self.0.set(self.0.get() - 1); + unsafe { Global.deallocate(ptr, layout) } + } + } + + struct PanicOnDrop; + impl Drop for PanicOnDrop { + fn drop(&mut self) { + panic!("PanicOnDrop"); + } + } + + let alloc = AllocCount(Cell::new(0)); + let rc = Rc::new_in(PanicOnDrop, &alloc); + assert_eq!(alloc.0.get(), 1); + + let panic_message = catch_unwind(AssertUnwindSafe(|| drop(rc))).unwrap_err(); + assert_eq!(*panic_message.downcast_ref::<&'static str>().unwrap(), "PanicOnDrop"); + assert_eq!(alloc.0.get(), 0); +} + #[allow(unused)] mod pin_coerce_unsized { use alloc::rc::{Rc, UniqueRc};