about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Markeffsky <@>2024-10-27 17:47:00 +0100
committerLukas Markeffsky <@>2024-10-27 18:32:36 +0100
commit8a9f40043fb6177a8a6d720103bac4e8889e0732 (patch)
tree7fdd000b9a74318bc5b4948366dee089ce93c9b3
parent5f5c243ca020e45cb6ee48a1f635a929eb611fd2 (diff)
downloadrust-8a9f40043fb6177a8a6d720103bac4e8889e0732.tar.gz
rust-8a9f40043fb6177a8a6d720103bac4e8889e0732.zip
add test for panicking drop in Box/Rc/Arc
-rw-r--r--library/alloc/tests/arc.rs40
-rw-r--r--library/alloc/tests/boxed.rs38
-rw-r--r--library/alloc/tests/rc.rs38
3 files changed, 113 insertions, 3 deletions
diff --git a/library/alloc/tests/arc.rs b/library/alloc/tests/arc.rs
index dc27c578b57..a259c0131ec 100644
--- a/library/alloc/tests/arc.rs
+++ b/library/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<T> = Arc<T>;
+type Rc<T, A = std::alloc::Global> = Arc<T, A>;
 
 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<i32>);
+    unsafe impl Allocator for AllocCount {
+        fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
+            self.0.set(self.0.get() + 1);
+            Global.allocate(layout)
+        }
+        unsafe fn deallocate(&self, ptr: NonNull<u8>, 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/library/alloc/tests/boxed.rs b/library/alloc/tests/boxed.rs
index bfc31a626fa..2d172fc2137 100644
--- a/library/alloc/tests/boxed.rs
+++ b/library/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<i32>);
+    unsafe impl Allocator for AllocCount {
+        fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
+            self.0.set(self.0.get() + 1);
+            Global.allocate(layout)
+        }
+        unsafe fn deallocate(&self, ptr: NonNull<u8>, 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/library/alloc/tests/rc.rs b/library/alloc/tests/rc.rs
index 29dbdcf225e..451765d7242 100644
--- a/library/alloc/tests/rc.rs
+++ b/library/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<i32>);
+    unsafe impl Allocator for AllocCount {
+        fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
+            self.0.set(self.0.get() + 1);
+            Global.allocate(layout)
+        }
+        unsafe fn deallocate(&self, ptr: NonNull<u8>, 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};