use std::{
mem::ManuallyDrop,
ptr::addr_of,
vec::{Drain, IntoIter},
};
use smallvec::SmallVec;
use crate::{
heap::{Heap, HeapId},
value::Value,
};
pub(crate) trait HeapItem {
fn py_dec_ref_ids(&mut self, stack: &mut Vec<HeapId>);
}
pub(crate) trait ContainsHeap {
fn heap(&self) -> &Heap;
fn heap_mut(&mut self) -> &mut Heap;
}
impl ContainsHeap for Heap {
fn heap(&self) -> &Self {
self
}
#[inline]
fn heap_mut(&mut self) -> &mut Self {
self
}
}
pub(crate) trait DropWithContext<C: ?Sized> {
fn drop_with(self, ctx: &mut C);
}
impl<C, U: DropWithContext<C>> DropWithContext<C> for Option<U> {
#[inline]
fn drop_with(self, ctx: &mut C) {
if let Some(value) = self {
value.drop_with(ctx);
}
}
}
impl<C, U: DropWithContext<C>> DropWithContext<C> for Vec<U> {
fn drop_with(self, ctx: &mut C) {
for value in self {
value.drop_with(ctx);
}
}
}
impl<C, A: smallvec::Array> DropWithContext<C> for SmallVec<A>
where
A::Item: DropWithContext<C>,
{
fn drop_with(self, ctx: &mut C) {
for value in self {
value.drop_with(ctx);
}
}
}
impl<C, U: DropWithContext<C>> DropWithContext<C> for IntoIter<U> {
fn drop_with(self, ctx: &mut C) {
for value in self {
value.drop_with(ctx);
}
}
}
impl<C, U: DropWithContext<C>> DropWithContext<C> for Drain<'_, U> {
fn drop_with(self, ctx: &mut C) {
for value in self {
value.drop_with(ctx);
}
}
}
impl<C, const N: usize> DropWithContext<C> for [Value; N]
where
Value: DropWithContext<C>,
{
fn drop_with(self, ctx: &mut C) {
for value in self {
DropWithContext::drop_with(value, ctx);
}
}
}
impl<C, U: DropWithContext<C>, V: DropWithContext<C>> DropWithContext<C> for (U, V) {
fn drop_with(self, ctx: &mut C) {
let (left, right) = self;
left.drop_with(ctx);
right.drop_with(ctx);
}
}
pub(crate) struct DropGuard<'a, C, V: DropWithContext<C>> {
value: ManuallyDrop<V>,
ctx: &'a mut C,
}
impl<'a, C, V: DropWithContext<C>> DropGuard<'a, C, V> {
#[inline]
pub fn new(value: V, ctx: &'a mut C) -> Self {
Self {
value: ManuallyDrop::new(value),
ctx,
}
}
#[inline]
pub fn into_inner(self) -> V {
let mut this = ManuallyDrop::new(self);
#[expect(unsafe_code)]
unsafe {
ManuallyDrop::take(&mut this.value)
}
}
#[inline]
pub fn as_parts(&mut self) -> (&V, &mut C) {
(&self.value, self.ctx)
}
#[inline]
pub fn as_parts_mut(&mut self) -> (&mut V, &mut C) {
(&mut self.value, self.ctx)
}
#[inline]
pub fn into_parts(self) -> (V, &'a mut C) {
let mut this = ManuallyDrop::new(self);
#[expect(unsafe_code)]
unsafe {
(ManuallyDrop::take(&mut this.value), addr_of!(this.ctx).read())
}
}
#[inline]
pub fn ctx(&mut self) -> &mut C {
self.ctx
}
}
impl<C, V: DropWithContext<C>> Drop for DropGuard<'_, C, V> {
fn drop(&mut self) {
#[expect(unsafe_code)]
unsafe { ManuallyDrop::take(&mut self.value) }.drop_with(self.ctx);
}
}
#[macro_export]
macro_rules! defer_drop {
($value:ident, $ctx:ident) => {
let mut _guard = $crate::heap::DropGuard::new($value, $ctx);
#[allow(
clippy::allow_attributes,
reason = "the reborrowed parts may not both be used in every case, so allow unused vars to avoid warnings"
)]
#[allow(unused_variables)]
let ($value, $ctx) = _guard.as_parts();
};
}
#[macro_export]
macro_rules! defer_drop_mut {
($value:ident, $ctx:ident) => {
let mut _guard = $crate::heap::DropGuard::new($value, $ctx);
#[allow(
clippy::allow_attributes,
reason = "the reborrowed parts may not both be used in every case, so allow unused vars to avoid warnings"
)]
#[allow(unused_variables)]
let ($value, $ctx) = _guard.as_parts_mut();
};
}