Skip to content

Commit fe9f22a

Browse files
authored
move heap traits to separate file (#229)
1 parent dc28860 commit fe9f22a

File tree

3 files changed

+310
-300
lines changed

3 files changed

+310
-300
lines changed

crates/monty/src/heap.rs

Lines changed: 4 additions & 299 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@ use std::{
44
collections::hash_map::DefaultHasher,
55
fmt::Write,
66
hash::{Hash, Hasher},
7-
mem::{ManuallyDrop, size_of},
8-
ptr::addr_of,
7+
mem::size_of,
98
vec,
109
};
1110

1211
use ahash::AHashSet;
1312
use num_integer::Integer;
1413
use smallvec::SmallVec;
1514

15+
// Re-export items moved to `heap_traits` so that `crate::heap::HeapGuard` etc. continue
16+
// to resolve (used by the `defer_drop!` macros and throughout the codebase).
17+
pub(crate) use crate::heap_traits::{ContainsHeap, DropWithHeap, HeapGuard, ImmutableHeapGuard};
1618
use crate::{
1719
args::ArgValues,
1820
asyncio::{Coroutine, GatherFuture, GatherItem},
@@ -1725,300 +1727,3 @@ impl<T: ResourceTracker> Drop for Heap<T> {
17251727
}
17261728
}
17271729
}
1728-
1729-
/// This trait represents types that contain a `Heap`; it allows for more complex structures
1730-
/// to participate in the `HeapGuard` pattern.
1731-
pub(crate) trait ContainsHeap {
1732-
type ResourceTracker: ResourceTracker;
1733-
fn heap(&self) -> &Heap<Self::ResourceTracker>;
1734-
fn heap_mut(&mut self) -> &mut Heap<Self::ResourceTracker>;
1735-
}
1736-
1737-
impl<T: ResourceTracker> ContainsHeap for Heap<T> {
1738-
type ResourceTracker = T;
1739-
fn heap(&self) -> &Self {
1740-
self
1741-
}
1742-
#[inline]
1743-
fn heap_mut(&mut self) -> &mut Self {
1744-
self
1745-
}
1746-
}
1747-
1748-
/// Trait for types that require heap access for proper cleanup.
1749-
///
1750-
/// Rust's standard `Drop` trait cannot decrement heap reference counts because it has no
1751-
/// access to the `Heap`. This trait provides an explicit drop-with-heap method so that
1752-
/// ref-counted values (and containers of them) can properly decrement their counts when
1753-
/// they are no longer needed.
1754-
///
1755-
/// **All types implementing this trait must be cleaned up on every code path** — not just
1756-
/// the happy path, but also early returns, conditional branches, `continue`, etc. A missed
1757-
/// call on any branch leaks reference counts. Prefer [`defer_drop!`] or [`HeapGuard`] to
1758-
/// guarantee cleanup automatically rather than inserting manual calls in every branch.
1759-
///
1760-
/// Implemented for `Value`, `Option<V>`, `Vec<Value>`, `ArgValues`, iterators, and other
1761-
/// types that hold heap references.
1762-
pub(crate) trait DropWithHeap: Sized {
1763-
/// Consume `self` and decrement reference counts for any heap-allocated values contained within.
1764-
fn drop_with_heap<H: ContainsHeap>(self, heap: &mut H);
1765-
}
1766-
1767-
impl DropWithHeap for Value {
1768-
#[inline]
1769-
fn drop_with_heap<H: ContainsHeap>(self, heap: &mut H) {
1770-
Self::drop_with_heap(self, heap);
1771-
}
1772-
}
1773-
1774-
impl<U: DropWithHeap> DropWithHeap for Option<U> {
1775-
#[inline]
1776-
fn drop_with_heap<H: ContainsHeap>(self, heap: &mut H) {
1777-
if let Some(value) = self {
1778-
value.drop_with_heap(heap);
1779-
}
1780-
}
1781-
}
1782-
1783-
impl<U: DropWithHeap> DropWithHeap for Vec<U> {
1784-
fn drop_with_heap<H: ContainsHeap>(self, heap: &mut H) {
1785-
for value in self {
1786-
value.drop_with_heap(heap);
1787-
}
1788-
}
1789-
}
1790-
1791-
impl<U: DropWithHeap> DropWithHeap for vec::IntoIter<U> {
1792-
fn drop_with_heap<H: ContainsHeap>(self, heap: &mut H) {
1793-
for value in self {
1794-
value.drop_with_heap(heap);
1795-
}
1796-
}
1797-
}
1798-
1799-
impl<const N: usize> DropWithHeap for [Value; N] {
1800-
fn drop_with_heap<H: ContainsHeap>(self, heap: &mut H) {
1801-
for value in self {
1802-
value.drop_with_heap(heap);
1803-
}
1804-
}
1805-
}
1806-
1807-
impl<U: DropWithHeap, V: DropWithHeap> DropWithHeap for (U, V) {
1808-
fn drop_with_heap<H: ContainsHeap>(self, heap: &mut H) {
1809-
let (left, right) = self;
1810-
left.drop_with_heap(heap);
1811-
right.drop_with_heap(heap);
1812-
}
1813-
}
1814-
1815-
/// Trait for types that require only an immutable heap reference for cleanup.
1816-
///
1817-
/// Unlike [`DropWithHeap`], which requires `&mut Heap`, this trait works with `&Heap`.
1818-
/// This is needed for cleanup in contexts that only have shared access to the heap,
1819-
/// such as `py_repr_fmt` and `py_str` formatting methods.
1820-
///
1821-
/// Currently implemented for [`RecursionToken`], which decrements the recursion depth
1822-
/// counter via interior mutability (`Cell`).
1823-
pub(crate) trait DropWithImmutableHeap {
1824-
/// Consume `self` and perform cleanup using an immutable heap reference.
1825-
fn drop_with_immutable_heap<T: ResourceTracker>(self, heap: &Heap<T>);
1826-
}
1827-
1828-
impl DropWithImmutableHeap for RecursionToken {
1829-
#[inline]
1830-
fn drop_with_immutable_heap<T: ResourceTracker>(self, heap: &Heap<T>) {
1831-
heap.decr_recursion_depth();
1832-
}
1833-
}
1834-
1835-
/// RAII guard that ensures a [`DropWithImmutableHeap`] value is cleaned up on every code path.
1836-
///
1837-
/// Like [`HeapGuard`], but holds an immutable `&Heap<T>` instead of requiring `&mut` access
1838-
/// via [`ContainsHeap`]. This is useful in contexts that only have shared access to the heap,
1839-
/// such as `py_repr_fmt` formatting methods.
1840-
///
1841-
/// On the normal path, the guarded value can be borrowed via [`as_parts`](Self::as_parts).
1842-
/// The guard's `Drop` impl calls [`DropWithImmutableHeap::drop_with_immutable_heap`]
1843-
/// automatically, so cleanup happens on all exit paths.
1844-
pub(crate) struct ImmutableHeapGuard<'a, T: ResourceTracker, V: DropWithImmutableHeap> {
1845-
value: ManuallyDrop<V>,
1846-
heap: &'a Heap<T>,
1847-
}
1848-
1849-
impl<'a, T: ResourceTracker, V: DropWithImmutableHeap> ImmutableHeapGuard<'a, T, V> {
1850-
/// Creates a new `ImmutableHeapGuard` for the given value and immutable heap reference.
1851-
#[inline]
1852-
pub fn new(value: V, heap: &'a Heap<T>) -> Self {
1853-
Self {
1854-
value: ManuallyDrop::new(value),
1855-
heap,
1856-
}
1857-
}
1858-
1859-
/// Borrows the value (immutably) and heap (immutably) out of the guard.
1860-
///
1861-
/// This is what [`defer_drop_immutable_heap!`] calls internally. The returned
1862-
/// references are tied to the guard's lifetime, so the value cannot escape.
1863-
#[inline]
1864-
pub fn as_parts(&self) -> (&V, &'a Heap<T>) {
1865-
(&self.value, self.heap)
1866-
}
1867-
}
1868-
1869-
impl<T: ResourceTracker, V: DropWithImmutableHeap> Drop for ImmutableHeapGuard<'_, T, V> {
1870-
fn drop(&mut self) {
1871-
// SAFETY: [DH] - value is never manually dropped until this point
1872-
unsafe { ManuallyDrop::take(&mut self.value) }.drop_with_immutable_heap(self.heap);
1873-
}
1874-
}
1875-
1876-
/// RAII guard that ensures a [`DropWithHeap`] value is cleaned up on every code path.
1877-
///
1878-
/// The guard's `Drop` impl calls [`DropWithHeap::drop_with_heap`] automatically, so
1879-
/// cleanup happens whether the scope exits normally, via `?`, `continue`, early return,
1880-
/// or any other branch. This eliminates the need to manually insert `drop_with_heap`
1881-
/// calls in every branch.
1882-
///
1883-
/// On the normal path, the guarded value can be borrowed via [`as_parts`](Self::as_parts) /
1884-
/// [`as_parts_mut`](Self::as_parts_mut), or reclaimed via [`into_inner`](Self::into_inner) /
1885-
/// [`into_parts`](Self::into_parts) (which consume the guard without dropping the value).
1886-
///
1887-
/// Prefer the [`defer_drop!`] macro for the common case where you just need to ensure a
1888-
/// value is dropped at scope exit. Use `HeapGuard` directly when you need to conditionally
1889-
/// reclaim the value (e.g. push it back onto the stack on success) or need mutable access
1890-
/// to both the value and heap through [`as_parts_mut`](Self::as_parts_mut).
1891-
pub(crate) struct HeapGuard<'a, H: ContainsHeap, V: DropWithHeap> {
1892-
// manually dropped because it needs to be dropped by move.
1893-
value: ManuallyDrop<V>,
1894-
heap: &'a mut H,
1895-
}
1896-
1897-
impl<'a, H: ContainsHeap, V: DropWithHeap> HeapGuard<'a, H, V> {
1898-
/// Creates a new `HeapGuard` for the given value and heap.
1899-
#[inline]
1900-
pub fn new(value: V, heap: &'a mut H) -> Self {
1901-
Self {
1902-
value: ManuallyDrop::new(value),
1903-
heap,
1904-
}
1905-
}
1906-
1907-
/// Consumes the guard and returns the contained value without dropping it.
1908-
///
1909-
/// Use this when the value should survive beyond the guard's scope (e.g. returning
1910-
/// a computed result from a function that used the guard for error-path safety).
1911-
#[inline]
1912-
pub fn into_inner(self) -> V {
1913-
let mut this = ManuallyDrop::new(self);
1914-
// SAFETY: [DH] - `ManuallyDrop::new(self)` prevents `Drop` on self, so we can take the value out
1915-
unsafe { ManuallyDrop::take(&mut this.value) }
1916-
}
1917-
1918-
/// Borrows the value (immutably) and heap (mutably) out of the guard.
1919-
///
1920-
/// This is what [`defer_drop!`] calls internally. The returned references are tied
1921-
/// to the guard's lifetime, so the value cannot escape.
1922-
#[inline]
1923-
pub fn as_parts(&mut self) -> (&V, &mut H) {
1924-
(&self.value, self.heap)
1925-
}
1926-
1927-
/// Borrows the value (mutably) and heap (mutably) out of the guard.
1928-
///
1929-
/// This is what [`defer_drop_mut!`] calls internally. Use this when the value needs
1930-
/// to be mutated in place (e.g. advancing an iterator, swapping during min/max).
1931-
#[inline]
1932-
pub fn as_parts_mut(&mut self) -> (&mut V, &mut H) {
1933-
(&mut self.value, self.heap)
1934-
}
1935-
1936-
/// Consumes the guard and returns the value and heap separately, without dropping.
1937-
///
1938-
/// Use this when you need to reclaim both the value *and* the heap reference — for
1939-
/// example, to push the value back onto the VM stack via the heap owner.
1940-
#[inline]
1941-
pub fn into_parts(self) -> (V, &'a mut H) {
1942-
let mut this = ManuallyDrop::new(self);
1943-
// SAFETY: [DH] - `ManuallyDrop` prevents `Drop` on self, so we can recover the parts
1944-
unsafe { (ManuallyDrop::take(&mut this.value), addr_of!(this.heap).read()) }
1945-
}
1946-
1947-
/// Borrows just the heap out of the guard
1948-
#[inline]
1949-
pub fn heap(&mut self) -> &mut H {
1950-
self.heap
1951-
}
1952-
}
1953-
1954-
impl<H: ContainsHeap, V: DropWithHeap> Drop for HeapGuard<'_, H, V> {
1955-
fn drop(&mut self) {
1956-
// SAFETY: [DH] - value is never manually dropped until this point
1957-
unsafe { ManuallyDrop::take(&mut self.value) }.drop_with_heap(self.heap.heap_mut());
1958-
}
1959-
}
1960-
1961-
/// The preferred way to ensure a [`DropWithHeap`] value is cleaned up on every code path.
1962-
///
1963-
/// Creates a [`HeapGuard`] and immediately rebinds `$value` as `&V` and `$heap` as
1964-
/// `&mut H` via [`HeapGuard::as_parts`]. The original owned value is moved into the
1965-
/// guard, which will call [`DropWithHeap::drop_with_heap`] when scope exits — whether
1966-
/// that's normal completion, early return via `?`, `continue`, or any other branch.
1967-
///
1968-
/// Beyond safety, this is often much more concise than inserting `drop_with_heap` calls
1969-
/// in every branch of complex control flow. For mutable access to the value, use
1970-
/// [`defer_drop_mut!`].
1971-
///
1972-
/// # Limitation
1973-
///
1974-
/// The macro rebinds `$heap` as a new `let` binding, so it cannot be used when `$heap`
1975-
/// is `self`. In `&mut self` methods, first assign `let this = self;` and pass `this`.
1976-
#[macro_export]
1977-
macro_rules! defer_drop {
1978-
($value:ident, $heap:ident) => {
1979-
let mut _guard = $crate::heap::HeapGuard::new($value, $heap);
1980-
#[allow(
1981-
clippy::allow_attributes,
1982-
reason = "the reborrowed parts may not both be used in every case, so allow unused vars to avoid warnings"
1983-
)]
1984-
#[allow(unused_variables)]
1985-
let ($value, $heap) = _guard.as_parts();
1986-
};
1987-
}
1988-
1989-
/// Like [`defer_drop!`], but rebinds `$value` as `&mut V` via [`HeapGuard::as_parts_mut`].
1990-
///
1991-
/// Use this when the value needs to be mutated in place — for example, advancing an
1992-
/// iterator with `for_next()`, or swapping values during a min/max comparison.
1993-
#[macro_export]
1994-
macro_rules! defer_drop_mut {
1995-
($value:ident, $heap:ident) => {
1996-
let mut _guard = $crate::heap::HeapGuard::new($value, $heap);
1997-
#[allow(
1998-
clippy::allow_attributes,
1999-
reason = "the reborrowed parts may not both be used in every case, so allow unused vars to avoid warnings"
2000-
)]
2001-
#[allow(unused_variables)]
2002-
let ($value, $heap) = _guard.as_parts_mut();
2003-
};
2004-
}
2005-
2006-
/// Like [`defer_drop!`], but for [`DropWithImmutableHeap`] values that only need `&Heap`
2007-
/// for cleanup.
2008-
///
2009-
/// Creates an [`ImmutableHeapGuard`] and immediately rebinds `$value` as `&V` and `$heap`
2010-
/// as `&Heap<T>`. The guard will call [`DropWithImmutableHeap::drop_with_immutable_heap`]
2011-
/// when scope exits. Use this for values like [`RecursionToken`] in contexts that only have
2012-
/// shared access to the heap (e.g., `py_repr_fmt` formatting methods).
2013-
#[macro_export]
2014-
macro_rules! defer_drop_immutable_heap {
2015-
($value:ident, $heap:ident) => {
2016-
let _guard = $crate::heap::ImmutableHeapGuard::new($value, $heap);
2017-
#[allow(
2018-
clippy::allow_attributes,
2019-
reason = "the reborrowed parts may not both be used in every case, so allow unused vars to avoid warnings"
2020-
)]
2021-
#[allow(unused_variables)]
2022-
let ($value, $heap) = _guard.as_parts();
2023-
};
2024-
}

0 commit comments

Comments
 (0)