From b486e6c077b3104795d80ed625e796e94d5e57a8 Mon Sep 17 00:00:00 2001 From: Flamki <9833ayush@gmail.com> Date: Sun, 29 Mar 2026 22:04:07 +0530 Subject: [PATCH] mark_sweep: add cast_ref_unchecked parity API and tests --- .../src/collectors/mark_sweep/pointers/gc.rs | 11 ++++++++++ oscars/src/collectors/mark_sweep/tests.rs | 22 +++++++++++++++++++ .../mark_sweep_arena2/pointers/gc.rs | 11 ++++++++++ .../src/collectors/mark_sweep_arena2/tests.rs | 22 +++++++++++++++++++ 4 files changed, 66 insertions(+) diff --git a/oscars/src/collectors/mark_sweep/pointers/gc.rs b/oscars/src/collectors/mark_sweep/pointers/gc.rs index c7d6bfd..69459a8 100644 --- a/oscars/src/collectors/mark_sweep/pointers/gc.rs +++ b/oscars/src/collectors/mark_sweep/pointers/gc.rs @@ -93,6 +93,17 @@ impl Gc { marker: PhantomData, } } + + /// Cast a `&Gc` to `&Gc` without consuming the handle. + /// + /// # Safety + /// + /// Caller must ensure that `U` is the correct runtime type for `this`. + #[inline] + #[must_use] + pub unsafe fn cast_ref_unchecked(this: &Self) -> &Gc { + unsafe { &*(this as *const Self).cast::>() } + } } impl Gc { diff --git a/oscars/src/collectors/mark_sweep/tests.rs b/oscars/src/collectors/mark_sweep/tests.rs index dc3e5bf..50010d4 100644 --- a/oscars/src/collectors/mark_sweep/tests.rs +++ b/oscars/src/collectors/mark_sweep/tests.rs @@ -149,6 +149,28 @@ fn ptr_eq_distinguishes_equal_values() { ); } +#[test] +fn cast_ref_unchecked_preserves_identity_and_value() { + let collector = &mut MarkSweepGarbageCollector::default() + .with_page_size(256) + .with_heap_threshold(512); + + let typed = Gc::new_in(13u32, collector); + let erased_as_u64: Gc = unsafe { Gc::cast_unchecked(typed.clone()) }; + + // SAFETY: `erased_as_u64` points to an allocation that actually stores `u32`. + let recovered_ref: &Gc = unsafe { Gc::cast_ref_unchecked(&erased_as_u64) }; + + assert_eq!( + **recovered_ref, 13u32, + "cast_ref_unchecked recovered wrong value" + ); + assert!( + Gc::ptr_eq(&typed, recovered_ref), + "cast_ref_unchecked should preserve pointer identity" + ); +} + #[test] fn multi_gc() { let collector = &mut MarkSweepGarbageCollector::default() diff --git a/oscars/src/collectors/mark_sweep_arena2/pointers/gc.rs b/oscars/src/collectors/mark_sweep_arena2/pointers/gc.rs index cd88f28..460a7e6 100644 --- a/oscars/src/collectors/mark_sweep_arena2/pointers/gc.rs +++ b/oscars/src/collectors/mark_sweep_arena2/pointers/gc.rs @@ -97,6 +97,17 @@ impl Gc { marker: PhantomData, } } + + /// Cast a `&Gc` to `&Gc` without consuming the handle. + /// + /// # Safety + /// + /// Caller must ensure that `U` is the correct runtime type for `this`. + #[inline] + #[must_use] + pub unsafe fn cast_ref_unchecked(this: &Self) -> &Gc { + unsafe { &*(this as *const Self).cast::>() } + } } impl Gc { diff --git a/oscars/src/collectors/mark_sweep_arena2/tests.rs b/oscars/src/collectors/mark_sweep_arena2/tests.rs index 4cada03..d52cb96 100644 --- a/oscars/src/collectors/mark_sweep_arena2/tests.rs +++ b/oscars/src/collectors/mark_sweep_arena2/tests.rs @@ -160,6 +160,28 @@ fn ptr_eq_distinguishes_equal_values() { ); } +#[test] +fn cast_ref_unchecked_preserves_identity_and_value() { + let collector = &mut MarkSweepGarbageCollector::default() + .with_arena_size(256) + .with_heap_threshold(512); + + let typed = Gc::new_in(13u32, collector); + let erased_as_u64: Gc = unsafe { Gc::cast_unchecked(typed.clone()) }; + + // SAFETY: `erased_as_u64` points to an allocation that actually stores `u32`. + let recovered_ref: &Gc = unsafe { Gc::cast_ref_unchecked(&erased_as_u64) }; + + assert_eq!( + **recovered_ref, 13u32, + "cast_ref_unchecked recovered wrong value" + ); + assert!( + Gc::ptr_eq(&typed, recovered_ref), + "cast_ref_unchecked should preserve pointer identity" + ); +} + #[test] fn multi_gc() { let collector = &mut MarkSweepGarbageCollector::default()