From e68cdb52b0d85c4bb3d9e1d5ba84107acea2772d Mon Sep 17 00:00:00 2001 From: shruti2522 Date: Wed, 12 Aug 2026 02:18:26 +0000 Subject: [PATCH 1/2] Replace type_name with TypeId using StaticId proxies --- .../src/collectors/mark_sweep_branded/cell.rs | 7 +- .../mark_sweep_branded/ephemeron.rs | 7 +- .../src/collectors/mark_sweep_branded/gc.rs | 22 ++- .../collectors/mark_sweep_branded/gc_box.rs | 18 ++- .../src/collectors/mark_sweep_branded/mod.rs | 6 + .../mark_sweep_branded/tests/mod.rs | 2 + .../mark_sweep_branded/tests/uaf.rs | 2 + .../collectors/mark_sweep_branded/trace.rs | 143 +++++++++++++++--- .../src/collectors/mark_sweep_branded/weak.rs | 10 +- .../collectors/null_collector_branded/cell.rs | 17 ++- .../null_collector_branded/ephemeron.rs | 7 +- .../collectors/null_collector_branded/gc.rs | 22 ++- .../null_collector_branded/gc_box.rs | 19 ++- .../null_collector_branded/mutation_ctx.rs | 12 ++ .../null_collector_branded/trace.rs | 97 ++++++++++-- .../collectors/null_collector_branded/weak.rs | 7 +- 16 files changed, 335 insertions(+), 63 deletions(-) diff --git a/oscars/src/collectors/mark_sweep_branded/cell.rs b/oscars/src/collectors/mark_sweep_branded/cell.rs index 779fb9d..f55d600 100644 --- a/oscars/src/collectors/mark_sweep_branded/cell.rs +++ b/oscars/src/collectors/mark_sweep_branded/cell.rs @@ -174,7 +174,12 @@ impl<'a, T: Trace + ?Sized> GcRefMut<'a, T> { impl Finalize for GcRefCell {} -unsafe impl Trace for GcRefCell { +unsafe impl Trace for GcRefCell +where + T::StaticId: Sized, +{ + // GcRefCell branded by T's lifetime, map to the static form. + type StaticId = GcRefCell; unsafe fn trace(&self, tracer: &mut Tracer) { let val = unsafe { &*self.inner.as_ptr() }; unsafe { diff --git a/oscars/src/collectors/mark_sweep_branded/ephemeron.rs b/oscars/src/collectors/mark_sweep_branded/ephemeron.rs index f183f2d..587ea2b 100644 --- a/oscars/src/collectors/mark_sweep_branded/ephemeron.rs +++ b/oscars/src/collectors/mark_sweep_branded/ephemeron.rs @@ -106,7 +106,12 @@ impl<'id, K: Trace + ?Sized, V: Trace> Copy for Ephemeron<'id, K, V> {} impl<'id, K: Trace + ?Sized, V: Trace> Finalize for Ephemeron<'id, K, V> {} -unsafe impl<'id, K: Trace + ?Sized, V: Trace> Trace for Ephemeron<'id, K, V> { +unsafe impl<'id, K: Trace + ?Sized, V: Trace> Trace for Ephemeron<'id, K, V> +where + K::StaticId: Sized, +{ + // Ephemeron<'id, K, V> -> Ephemeron<'static, K::StaticId, V::StaticId> + type StaticId = Ephemeron<'static, K::StaticId, V::StaticId>; // Ephemerons do not mark their key; liveness of the key is determined // by the GC independently. The value is marked via the GC's ephemeron // fixpoint phase in `Collector::collect`. diff --git a/oscars/src/collectors/mark_sweep_branded/gc.rs b/oscars/src/collectors/mark_sweep_branded/gc.rs index 683c6c2..d9b7964 100644 --- a/oscars/src/collectors/mark_sweep_branded/gc.rs +++ b/oscars/src/collectors/mark_sweep_branded/gc.rs @@ -89,10 +89,16 @@ impl<'gc, T: Trace + ?Sized + 'gc> Gc<'gc, T> { } } + /// Returns `true` if the inner value is of type `U`. + /// + /// Uses `TypeId` via `U::StaticId`, sound even when `U` carries a branded + /// lifetime because `StaticId` is the lifetime erased proxy defined on the + /// `Trace` trait. This avoids the `T: 'static` restriction while still + /// giving us a stable, unique identity guarantee. #[inline] - pub fn is(&self) -> bool { - let actual_type_name = unsafe { (*self.ptr.as_ptr().as_ptr()).0.type_name }; - actual_type_name == core::any::type_name::() + pub fn is(&self) -> bool { + let actual_type_id = unsafe { (*self.ptr.as_ptr().as_ptr()).0.type_id }; + actual_type_id == core::any::TypeId::of::() } #[inline] @@ -149,7 +155,15 @@ impl<'gc, T: Trace + ?Sized + 'gc> Deref for Gc<'gc, T> { } impl Finalize for Gc<'_, T> {} -unsafe impl Trace for Gc<'_, T> { +unsafe impl<'gc, T: Trace + ?Sized + 'gc> Trace for Gc<'gc, T> +where + T::StaticId: Sized, +{ + // The StaticId of Gc<'gc, T> is Gc<'static, T::StaticId>. + // This maps any branded Gc to a fully 'static form, giving a unique TypeId + // per pointee type regardless of which 'gc brand is in use. + type StaticId = Gc<'static, T::StaticId>; + unsafe fn trace(&self, tracer: &mut crate::collectors::mark_sweep_branded::trace::Tracer) { tracer.mark(self); } diff --git a/oscars/src/collectors/mark_sweep_branded/gc_box.rs b/oscars/src/collectors/mark_sweep_branded/gc_box.rs index 833af15..91431ee 100644 --- a/oscars/src/collectors/mark_sweep_branded/gc_box.rs +++ b/oscars/src/collectors/mark_sweep_branded/gc_box.rs @@ -1,5 +1,6 @@ //! The heap header wrapping every GC-managed value. +use core::any::TypeId; use core::cell::Cell; use core::ptr::NonNull; @@ -32,8 +33,13 @@ pub struct GcBox { pub(crate) drop_fn: DropFn, /// Allocation ID used to validate weak pointers. pub(crate) alloc_id: usize, - /// Type name of the underlying value - pub(crate) type_name: &'static str, + /// Unique identifier for the concrete type `T`. + /// + /// Stored as `TypeId::of::()`, we use the `StaticId` proxy + /// type so that branded lifetimes (eg. `'gc`) don't require `T: 'static`. + /// Two values whose erased types share the same `StaticId` produce the + /// same `TypeId`, which is exactly what we want for sound downcasting. + pub(crate) type_id: TypeId, /// The user value. pub(crate) value: T, } @@ -42,15 +48,17 @@ impl GcBox { pub(crate) const FREED_ALLOC_ID: usize = usize::MAX; } -impl GcBox { - /// Create a [`GcBox`] for `value`, `color` starts as [`GcColor::White`] +impl GcBox { + /// Create a [`GcBox`] for `value`, `color` starts as [`GcColor::White`]. + /// + /// Requires `T: Trace` to access `T::StaticId` for the `TypeId`. pub(crate) fn new(value: T, trace_fn: TraceFn, drop_fn: DropFn, alloc_id: usize) -> Self { Self { color: Cell::new(GcColor::White), trace_fn, drop_fn, alloc_id, - type_name: core::any::type_name::(), + type_id: TypeId::of::(), value, } } diff --git a/oscars/src/collectors/mark_sweep_branded/mod.rs b/oscars/src/collectors/mark_sweep_branded/mod.rs index 21f757d..3c026ed 100644 --- a/oscars/src/collectors/mark_sweep_branded/mod.rs +++ b/oscars/src/collectors/mark_sweep_branded/mod.rs @@ -47,6 +47,12 @@ pub struct Collector { pub(crate) ephemerons: RefCell>, } +impl Default for Collector { + fn default() -> Self { + Self::new() + } +} + impl Collector { pub fn new() -> Self { Self { diff --git a/oscars/src/collectors/mark_sweep_branded/tests/mod.rs b/oscars/src/collectors/mark_sweep_branded/tests/mod.rs index dd95e42..e59f2ee 100644 --- a/oscars/src/collectors/mark_sweep_branded/tests/mod.rs +++ b/oscars/src/collectors/mark_sweep_branded/tests/mod.rs @@ -7,6 +7,8 @@ struct JsObject { } unsafe impl crate::collectors::mark_sweep_branded::Trace for JsObject { + // JsObject contains no GC pointers and no lifetimes, so its static proxy is itself. + type StaticId = JsObject; unsafe fn trace(&self, _tracer: &mut crate::collectors::mark_sweep_branded::trace::Tracer) {} } impl crate::collectors::mark_sweep_branded::Finalize for JsObject {} diff --git a/oscars/src/collectors/mark_sweep_branded/tests/uaf.rs b/oscars/src/collectors/mark_sweep_branded/tests/uaf.rs index 921d148..612925d 100644 --- a/oscars/src/collectors/mark_sweep_branded/tests/uaf.rs +++ b/oscars/src/collectors/mark_sweep_branded/tests/uaf.rs @@ -6,6 +6,8 @@ use core::cell::Cell; struct DetectDrop<'a>(&'a Cell); unsafe impl<'a> Trace for DetectDrop<'a> { + // DetectDrop<'a> borrows a local `Cell`, the static proxy is DetectDrop<'static>. + type StaticId = DetectDrop<'static>; unsafe fn trace(&self, _tracer: &mut crate::collectors::mark_sweep_branded::trace::Tracer) {} } diff --git a/oscars/src/collectors/mark_sweep_branded/trace.rs b/oscars/src/collectors/mark_sweep_branded/trace.rs index e48b16b..c92d02d 100644 --- a/oscars/src/collectors/mark_sweep_branded/trace.rs +++ b/oscars/src/collectors/mark_sweep_branded/trace.rs @@ -23,6 +23,7 @@ pub use crate::collectors::common::Finalize; /// /// Use `Tracer::mark` for every reachable `Gc` pointer. pub unsafe trait Trace { + type StaticId: 'static + Trace; /// Marks all `Gc` pointers reachable from `self`. /// /// # Safety @@ -126,7 +127,14 @@ impl<'a> Tracer<'a> { } } -unsafe impl Trace for &T { +// For &T, the StaticId is &'static T::StaticId. Since &U is always Sized, this +// satisfies the Sized requirement on StaticId even when T::StaticId is a DST. +// We add the bound T::StaticId: Sized to keep things simple and unambiguous. +unsafe impl Trace for &T +where + T::StaticId: Sized, +{ + type StaticId = &'static T::StaticId; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } @@ -137,6 +145,7 @@ macro_rules! empty_trace { ($($T:ty),* $(,)?) => { $( unsafe impl Trace for $T { + type StaticId = $T; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } @@ -149,7 +158,6 @@ empty_trace![ bool, isize, usize, - str, i8, u8, i16, @@ -180,7 +188,15 @@ empty_trace![ core::sync::atomic::AtomicUsize, ]; +// str is a DST; we cannot allocate it directly. Use String as the Sized proxy. +unsafe impl Trace for str { + type StaticId = String; + #[inline] + unsafe fn trace(&self, _tracer: &mut Tracer) {} +} + unsafe impl Trace for [T; N] { + type StaticId = [T::StaticId; N]; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.iter() { v.trace(tracer); @@ -188,13 +204,32 @@ unsafe impl Trace for [T; N] { } } -unsafe impl Trace for Box { +// Slices [T] cannot be allocated directly in the GC. Their StaticId is a +// Vec, which is always Sized and avoids Box fixed point divergence. +unsafe impl Trace for [T] { + type StaticId = Vec; + #[inline] + unsafe fn trace(&self, tracer: &mut Tracer) { + for v in self { + v.trace(tracer); + } + } +} + +// Box where T: ?Sized. Box is always Sized even for DST contents. +// We require T::StaticId: Sized to produce a concrete Sized StaticId. +unsafe impl Trace for Box +where + T::StaticId: Sized, +{ + type StaticId = Box; unsafe fn trace(&self, tracer: &mut Tracer) { (**self).trace(tracer); } } unsafe impl Trace for Option { + type StaticId = Option; unsafe fn trace(&self, tracer: &mut Tracer) { if let Some(v) = self { v.trace(tracer); @@ -203,6 +238,7 @@ unsafe impl Trace for Option { } unsafe impl Trace for Result { + type StaticId = Result; unsafe fn trace(&self, tracer: &mut Tracer) { match self { Ok(v) => v.trace(tracer), @@ -212,6 +248,7 @@ unsafe impl Trace for Result { } unsafe impl Trace for Vec { + type StaticId = Vec; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.iter() { v.trace(tracer); @@ -221,6 +258,7 @@ unsafe impl Trace for Vec { #[cfg(feature = "thin-vec")] unsafe impl Trace for thin_vec::ThinVec { + type StaticId = thin_vec::ThinVec; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.iter() { v.trace(tracer); @@ -229,6 +267,7 @@ unsafe impl Trace for thin_vec::ThinVec { } unsafe impl Trace for VecDeque { + type StaticId = VecDeque; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.iter() { v.trace(tracer); @@ -237,6 +276,7 @@ unsafe impl Trace for VecDeque { } unsafe impl Trace for LinkedList { + type StaticId = LinkedList; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.iter() { v.trace(tracer); @@ -244,28 +284,27 @@ unsafe impl Trace for LinkedList { } } -unsafe impl Trace for PhantomData { +// PhantomData doesn't trace T, so T need not implement Trace. +// For StaticId we require T: 'static so the proxy type itself is 'static. +unsafe impl Trace for PhantomData { + type StaticId = PhantomData; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } -unsafe impl Trace for [T] { - #[inline] - unsafe fn trace(&self, tracer: &mut Tracer) { - for v in self { - v.trace(tracer); - } - } -} - unsafe impl Trace for core::any::TypeId { + type StaticId = core::any::TypeId; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } // Cell> requires T: Copy to safely read the value via Cell::get(). // For non-Copy types, use GcRefCell instead. -unsafe impl Trace for Cell { +unsafe impl Trace for Cell +where + T::StaticId: Default, +{ + type StaticId = Cell; unsafe fn trace(&self, tracer: &mut Tracer) { let v = self.take(); v.trace(tracer); @@ -274,6 +313,7 @@ unsafe impl Trace for Cell { } unsafe impl Trace for OnceCell { + type StaticId = OnceCell; unsafe fn trace(&self, tracer: &mut Tracer) { if let Some(v) = self.get() { v.trace(tracer); @@ -281,10 +321,13 @@ unsafe impl Trace for OnceCell { } } -unsafe impl Trace for Cow<'static, T> +unsafe impl Trace for Cow<'static, T> where T::Owned: Trace, + T::StaticId: ToOwned, { + // T is already 'static so we can use it directly as the proxy. + type StaticId = Cow<'static, T>; unsafe fn trace(&self, tracer: &mut Tracer) { if let Cow::Owned(v) = self { v.trace(tracer); @@ -293,6 +336,7 @@ where } unsafe impl Trace for (A,) { + type StaticId = (A::StaticId,); #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { self.0.trace(tracer); @@ -300,6 +344,7 @@ unsafe impl Trace for (A,) { } unsafe impl Trace for (A, B) { + type StaticId = (A::StaticId, B::StaticId); #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { self.0.trace(tracer); @@ -308,6 +353,7 @@ unsafe impl Trace for (A, B) { } unsafe impl Trace for (A, B, C) { + type StaticId = (A::StaticId, B::StaticId, C::StaticId); #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { self.0.trace(tracer); @@ -317,6 +363,7 @@ unsafe impl Trace for (A, B, C) { } unsafe impl Trace for (A, B, C, D) { + type StaticId = (A::StaticId, B::StaticId, C::StaticId, D::StaticId); unsafe fn trace(&self, tracer: &mut Tracer) { self.0.trace(tracer); self.1.trace(tracer); @@ -326,6 +373,13 @@ unsafe impl Trace for (A, B, C, D) { } unsafe impl Trace for (A, B, C, D, E) { + type StaticId = ( + A::StaticId, + B::StaticId, + C::StaticId, + D::StaticId, + E::StaticId, + ); unsafe fn trace(&self, tracer: &mut Tracer) { self.0.trace(tracer); self.1.trace(tracer); @@ -338,6 +392,14 @@ unsafe impl Trace for (A, B, C unsafe impl Trace for (A, B, C, D, E, F) { + type StaticId = ( + A::StaticId, + B::StaticId, + C::StaticId, + D::StaticId, + E::StaticId, + F::StaticId, + ); unsafe fn trace(&self, tracer: &mut Tracer) { self.0.trace(tracer); self.1.trace(tracer); @@ -351,6 +413,15 @@ unsafe impl Trace unsafe impl Trace for (A, B, C, D, E, F, G) { + type StaticId = ( + A::StaticId, + B::StaticId, + C::StaticId, + D::StaticId, + E::StaticId, + F::StaticId, + G::StaticId, + ); unsafe fn trace(&self, tracer: &mut Tracer) { self.0.trace(tracer); self.1.trace(tracer); @@ -365,6 +436,16 @@ unsafe impl Trace for (A, B, C, D, E, F, G, H) { + type StaticId = ( + A::StaticId, + B::StaticId, + C::StaticId, + D::StaticId, + E::StaticId, + F::StaticId, + G::StaticId, + H::StaticId, + ); unsafe fn trace(&self, tracer: &mut Tracer) { self.0.trace(tracer); self.1.trace(tracer); @@ -380,17 +461,24 @@ unsafe impl Trace for rust_alloc::rc::Rc { +// Rc/Arc are reference-counted, not GC-traced. They cannot contain live Gc +// pointers (that would create a cycle the GC cannot see). StaticId uses the +// 'static-bounded form so TypeId is well-formed. +unsafe impl Trace for rust_alloc::rc::Rc { + type StaticId = rust_alloc::rc::Rc; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } -unsafe impl Trace for rust_alloc::sync::Arc { +unsafe impl Trace for rust_alloc::sync::Arc { + type StaticId = rust_alloc::sync::Arc; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } -unsafe impl Trace for BTreeMap { +// K is not traced (BTreeMap keys are immutable); require K: 'static for StaticId. +unsafe impl Trace for BTreeMap { + type StaticId = BTreeMap; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.values() { v.trace(tracer); @@ -398,7 +486,9 @@ unsafe impl Trace for BTreeMap { } } -unsafe impl Trace for BTreeSet { +// BTreeSet keys are never traced; require T: 'static for StaticId. +unsafe impl Trace for BTreeSet { + type StaticId = BTreeSet; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) { // BTreeSet keys are immutable and cannot contain Gc pointers @@ -412,11 +502,13 @@ mod icu_trace { use icu_locale_core::{LanguageIdentifier, Locale}; unsafe impl Trace for LanguageIdentifier { + type StaticId = LanguageIdentifier; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } unsafe impl Trace for Locale { + type StaticId = Locale; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } @@ -427,6 +519,7 @@ mod either_trace { use crate::collectors::mark_sweep_branded::{Trace, Tracer}; unsafe impl Trace for either::Either { + type StaticId = either::Either; unsafe fn trace(&self, tracer: &mut Tracer) { match self { either::Either::Left(l) => l.trace(tracer), @@ -438,6 +531,7 @@ mod either_trace { #[cfg(feature = "arrayvec")] unsafe impl Trace for arrayvec::ArrayVec { + type StaticId = arrayvec::ArrayVec; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self { v.trace(tracer); @@ -447,30 +541,35 @@ unsafe impl Trace for arrayvec::ArrayVec { #[cfg(feature = "std")] unsafe impl Trace for std::path::Path { + type StaticId = std::path::PathBuf; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } #[cfg(feature = "std")] unsafe impl Trace for std::path::PathBuf { + type StaticId = std::path::PathBuf; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } #[cfg(feature = "std")] unsafe impl Trace for std::time::Instant { + type StaticId = std::time::Instant; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } #[cfg(feature = "std")] unsafe impl Trace for std::time::SystemTime { + type StaticId = std::time::SystemTime; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } #[cfg(feature = "std")] -unsafe impl Trace for std::collections::HashMap { +unsafe impl Trace for std::collections::HashMap { + type StaticId = std::collections::HashMap; #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { for (k, v) in self { @@ -481,7 +580,8 @@ unsafe impl Trace for std::collections::HashMap } #[cfg(feature = "std")] -unsafe impl Trace for std::collections::HashSet { +unsafe impl Trace for std::collections::HashSet { + type StaticId = std::collections::HashSet; #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { for v in self { @@ -491,6 +591,7 @@ unsafe impl Trace for std::collections::HashSet { } unsafe impl Trace for rust_alloc::collections::BinaryHeap { + type StaticId = rust_alloc::collections::BinaryHeap; #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.iter() { diff --git a/oscars/src/collectors/mark_sweep_branded/weak.rs b/oscars/src/collectors/mark_sweep_branded/weak.rs index 21564d9..0ba05b4 100644 --- a/oscars/src/collectors/mark_sweep_branded/weak.rs +++ b/oscars/src/collectors/mark_sweep_branded/weak.rs @@ -63,8 +63,7 @@ impl<'id, T: Trace + ?Sized> WeakGc<'id, T> { /// Returns `true` if the referenced value is still alive. pub fn is_upgradable(&self) -> bool { - let is_valid = unsafe { (*self.ptr.as_ptr().as_ptr()).0.alloc_id == self.alloc_id }; - is_valid + unsafe { (*self.ptr.as_ptr().as_ptr()).0.alloc_id == self.alloc_id } } } @@ -93,7 +92,12 @@ impl<'id, T: Trace + ?Sized> PartialEq for WeakGc<'id, T> { } impl<'id, T: Trace + ?Sized> Finalize for WeakGc<'id, T> {} -unsafe impl<'id, T: Trace + ?Sized> Trace for WeakGc<'id, T> { +unsafe impl<'id, T: Trace + ?Sized> Trace for WeakGc<'id, T> +where + T::StaticId: Sized, +{ + // WeakGc<'id, T> maps to WeakGc<'static, T::StaticId> as the static proxy. + type StaticId = WeakGc<'static, T::StaticId>; // Weak references do not mark their target, upgrade() returning None after collection is the intended behaviour. unsafe fn trace(&self, _tracer: &mut crate::collectors::mark_sweep_branded::trace::Tracer) {} } diff --git a/oscars/src/collectors/null_collector_branded/cell.rs b/oscars/src/collectors/null_collector_branded/cell.rs index ca36252..b825e84 100644 --- a/oscars/src/collectors/null_collector_branded/cell.rs +++ b/oscars/src/collectors/null_collector_branded/cell.rs @@ -72,10 +72,14 @@ impl DerefMut for GcRefMut<'_, T> { } } -impl<'a, T: Trace + ?Sized> GcRef<'a, T> { - pub fn clone(orig: &GcRef<'a, T>) -> GcRef<'a, T> { - GcRef(Ref::clone(&orig.0)) +impl<'a, T: Trace + ?Sized> Clone for GcRef<'a, T> { + #[inline] + fn clone(&self) -> Self { + GcRef(Ref::clone(&self.0)) } +} + +impl<'a, T: Trace + ?Sized> GcRef<'a, T> { pub fn map(orig: GcRef<'a, T>, f: F) -> GcRef<'a, U> where F: FnOnce(&T) -> &U, @@ -129,7 +133,12 @@ impl<'a, T: Trace + ?Sized> GcRefMut<'a, T> { impl Finalize for GcRefCell {} -unsafe impl Trace for GcRefCell { +unsafe impl Trace for GcRefCell +where + T::StaticId: Sized, +{ + // GcRefCell<'gc, T> is branded by T's lifetime. Map to the static form. + type StaticId = GcRefCell; #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { // SAFETY: We only access the inner value for tracing and do not mutate it. diff --git a/oscars/src/collectors/null_collector_branded/ephemeron.rs b/oscars/src/collectors/null_collector_branded/ephemeron.rs index 209acd9..3d239f1 100644 --- a/oscars/src/collectors/null_collector_branded/ephemeron.rs +++ b/oscars/src/collectors/null_collector_branded/ephemeron.rs @@ -78,6 +78,11 @@ impl<'id, K: Trace + ?Sized, V: Trace> Copy for Ephemeron<'id, K, V> {} impl<'id, K: Trace + ?Sized, V: Trace> Finalize for Ephemeron<'id, K, V> {} -unsafe impl<'id, K: Trace + ?Sized, V: Trace> Trace for Ephemeron<'id, K, V> { +unsafe impl<'id, K: Trace + ?Sized, V: Trace> Trace for Ephemeron<'id, K, V> +where + K::StaticId: Sized, +{ + // Ephemeron<'id, K, V> -> Ephemeron<'static, K::StaticId, V::StaticId> + type StaticId = Ephemeron<'static, K::StaticId, V::StaticId>; unsafe fn trace(&self, _tracer: &mut Tracer) {} } diff --git a/oscars/src/collectors/null_collector_branded/gc.rs b/oscars/src/collectors/null_collector_branded/gc.rs index 5e8667c..eaffceb 100644 --- a/oscars/src/collectors/null_collector_branded/gc.rs +++ b/oscars/src/collectors/null_collector_branded/gc.rs @@ -80,10 +80,16 @@ impl<'gc, T: Trace + ?Sized + 'gc> Gc<'gc, T> { } } + /// Returns `true` if the inner value is of type `U`. + /// + /// Uses `TypeId` via `U::StaticId`, sound even when `U` carries a branded + /// lifetime because `StaticId` is the lifetime erased proxy defined on the + /// `Trace` trait. This avoids the `T: 'static` restriction while still + /// giving us a stable, unique identity guarantee #[inline] - pub fn is(&self) -> bool { - let actual_type_name = unsafe { (*self.ptr.as_ptr().as_ptr()).0.type_name }; - actual_type_name == core::any::type_name::() + pub fn is(&self) -> bool { + let actual_type_id = unsafe { (*self.ptr.as_ptr().as_ptr()).0.type_id }; + actual_type_id == core::any::TypeId::of::() } #[inline] @@ -141,7 +147,15 @@ impl<'gc, T: Trace + ?Sized + 'gc> Deref for Gc<'gc, T> { impl Finalize for Gc<'_, T> {} -unsafe impl Trace for Gc<'_, T> { +unsafe impl<'gc, T: Trace + ?Sized + 'gc> Trace for Gc<'gc, T> +where + T::StaticId: Sized, +{ + // The StaticId of Gc<'gc, T> is Gc<'static, T::StaticId>. + // This maps any branded Gc to a fully 'static form, giving a unique TypeId + // per pointee type regardless of which 'gc brand is in use. + type StaticId = Gc<'static, T::StaticId>; + unsafe fn trace(&self, tracer: &mut crate::collectors::null_collector_branded::trace::Tracer) { tracer.mark(self); } diff --git a/oscars/src/collectors/null_collector_branded/gc_box.rs b/oscars/src/collectors/null_collector_branded/gc_box.rs index 2514b95..26910a7 100644 --- a/oscars/src/collectors/null_collector_branded/gc_box.rs +++ b/oscars/src/collectors/null_collector_branded/gc_box.rs @@ -1,6 +1,8 @@ +use core::any::TypeId; use core::ptr::NonNull; use crate::alloc::mempool3::PoolAllocator; +use crate::collectors::null_collector_branded::trace::Trace; pub type DropFn = unsafe fn(&mut PoolAllocator<'static>, NonNull); @@ -10,18 +12,25 @@ pub type DropFn = unsafe fn(&mut PoolAllocator<'static>, NonNull); pub struct GcBox { /// Type erased finalize and free fn pub(crate) drop_fn: DropFn, - /// Type name of the underlying value - pub(crate) type_name: &'static str, + /// Unique identifier for the concrete type `T`. + /// + /// Stored as `TypeId::of::()`, we use the `StaticId` proxy + /// type so that branded lifetimes (eg. `'gc`) don't require `T: 'static`. + /// Two values whose erased types share the same `StaticId` produce the + /// same `TypeId`, which is exactly what we want for sound downcasting. + pub(crate) type_id: TypeId, /// User value pub(crate) value: T, } -impl GcBox { - /// Create a [`GcBox`] for `value` +impl GcBox { + /// Create a [`GcBox`] for `value`. + /// + /// Requires `T: Trace` to access `T::StaticId` for the `TypeId`. pub(crate) fn new(value: T, drop_fn: DropFn) -> Self { Self { drop_fn, - type_name: core::any::type_name::(), + type_id: TypeId::of::(), value, } } diff --git a/oscars/src/collectors/null_collector_branded/mutation_ctx.rs b/oscars/src/collectors/null_collector_branded/mutation_ctx.rs index c1bd788..c07209a 100644 --- a/oscars/src/collectors/null_collector_branded/mutation_ctx.rs +++ b/oscars/src/collectors/null_collector_branded/mutation_ctx.rs @@ -23,12 +23,24 @@ impl<'id, 'gc> MutationContext<'id, 'gc> { /// **Note**: This is a temporary workaround to keep `boa_engine` working. /// It breaks the normal safety rules of the collector, and should only be /// used to support older code that relies on `Default` + /// + /// # Safety + /// + /// `Gc<'gc, T>` and `MutationContext` are both `!Send`, so neither can escape + /// the thread that created them. `Collector::drop` only runs at thread exit, + /// after which no `Gc` on this thread can be accessed. The raw pointer reborrow + /// below is therefore sound ,the reference cannot outlive the TLS slot. #[cfg(feature = "std")] pub fn global() -> Self { std::thread_local! { static COLLECTOR: crate::collectors::null_collector_branded::Collector = crate::collectors::null_collector_branded::Collector::new(); } COLLECTOR.with(|c| { + // SAFETY: `Gc` and `MutationContext` are `!Send`, so they cannot escape + // this thread. `COLLECTOR` is a thread-local whose destructor only runs + // at thread exit, after all thread-local values are inaccessible. + // Therefore, `c` remains valid for at least as long as any `MutationContext` + // or `Gc` that could possibly reference it. let ptr = c as *const crate::collectors::null_collector_branded::Collector; Self { collector: unsafe { &*ptr }, diff --git a/oscars/src/collectors/null_collector_branded/trace.rs b/oscars/src/collectors/null_collector_branded/trace.rs index c16c1f5..06943b9 100644 --- a/oscars/src/collectors/null_collector_branded/trace.rs +++ b/oscars/src/collectors/null_collector_branded/trace.rs @@ -21,6 +21,7 @@ use rust_alloc::vec::Vec; /// While the null collector reclaims no memory, implementations must be /// sound for other collectors to prevent UAF bugs. pub unsafe trait Trace { + type StaticId: 'static + Trace; /// Marks all `Gc` pointers reachable from `self`. /// /// # Safety @@ -57,7 +58,14 @@ impl<'a> Tracer<'a> { } } -unsafe impl Trace for &T { +// For &T, the StaticId is &'static T::StaticId. Since &U is always Sized, this +// satisfies the Sized requirement on StaticId even when T::StaticId is a DST. +// We add the bound T::StaticId: Sized to keep things simple and unambiguous. +unsafe impl Trace for &T +where + T::StaticId: Sized, +{ + type StaticId = &'static T::StaticId; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } @@ -66,6 +74,7 @@ macro_rules! empty_trace { ($($T:ty),* $(,)?) => { $( unsafe impl Trace for $T { + type StaticId = $T; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } @@ -109,6 +118,7 @@ empty_trace![ ]; unsafe impl Trace for [T; N] { + type StaticId = [T::StaticId; N]; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.iter() { v.trace(tracer); @@ -116,7 +126,10 @@ unsafe impl Trace for [T; N] { } } +// Slices [T] cannot be allocated directly in the GC. Their StaticId is a +// Vec, which is always Sized and avoids Box fixed point divergence. unsafe impl Trace for [T] { + type StaticId = Vec; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.iter() { v.trace(tracer); @@ -124,7 +137,13 @@ unsafe impl Trace for [T] { } } -unsafe impl Trace for Box { +// Box where T: ?Sized. Box is always Sized even for DST contents. +// We require T::StaticId: Sized to produce a concrete Sized StaticId. +unsafe impl Trace for Box +where + T::StaticId: Sized, +{ + type StaticId = Box; unsafe fn trace(&self, tracer: &mut Tracer) { (**self).trace(tracer); } @@ -132,6 +151,7 @@ unsafe impl Trace for Box { #[cfg(feature = "thin-vec")] unsafe impl Trace for thin_vec::ThinVec { + type StaticId = thin_vec::ThinVec; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.iter() { v.trace(tracer); @@ -140,6 +160,7 @@ unsafe impl Trace for thin_vec::ThinVec { } unsafe impl Trace for Option { + type StaticId = Option; unsafe fn trace(&self, tracer: &mut Tracer) { if let Some(v) = self { v.trace(tracer); @@ -148,6 +169,7 @@ unsafe impl Trace for Option { } unsafe impl Trace for Result { + type StaticId = Result; unsafe fn trace(&self, tracer: &mut Tracer) { match self { Ok(v) => v.trace(tracer), @@ -157,6 +179,7 @@ unsafe impl Trace for Result { } unsafe impl Trace for Vec { + type StaticId = Vec; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.iter() { v.trace(tracer); @@ -165,6 +188,7 @@ unsafe impl Trace for Vec { } unsafe impl Trace for VecDeque { + type StaticId = VecDeque; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.iter() { v.trace(tracer); @@ -173,6 +197,7 @@ unsafe impl Trace for VecDeque { } unsafe impl Trace for LinkedList { + type StaticId = LinkedList; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.iter() { v.trace(tracer); @@ -180,12 +205,19 @@ unsafe impl Trace for LinkedList { } } -unsafe impl Trace for PhantomData { +// PhantomData doesn't trace T, so T need not implement Trace. +// For StaticId we require T: 'static so the proxy type itself is 'static. +unsafe impl Trace for PhantomData { + type StaticId = PhantomData; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } -unsafe impl Trace for Cell { +unsafe impl Trace for Cell +where + T::StaticId: Default, +{ + type StaticId = Cell; unsafe fn trace(&self, tracer: &mut Tracer) { let v = self.take(); v.trace(tracer); @@ -194,6 +226,7 @@ unsafe impl Trace for Cell { } unsafe impl Trace for OnceCell { + type StaticId = OnceCell; unsafe fn trace(&self, tracer: &mut Tracer) { if let Some(v) = self.get() { v.trace(tracer); @@ -201,10 +234,13 @@ unsafe impl Trace for OnceCell { } } -unsafe impl Trace for Cow<'static, T> +unsafe impl Trace for Cow<'static, T> where T::Owned: Trace, + T::StaticId: ToOwned, { + // T is already 'static so we can use it directly as the proxy. + type StaticId = Cow<'static, T>; unsafe fn trace(&self, tracer: &mut Tracer) { if let Cow::Owned(v) = self { v.trace(tracer); @@ -213,6 +249,7 @@ where } unsafe impl Trace for (A,) { + type StaticId = (A::StaticId,); #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { self.0.trace(tracer); @@ -220,6 +257,7 @@ unsafe impl Trace for (A,) { } unsafe impl Trace for (A, B) { + type StaticId = (A::StaticId, B::StaticId); #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { self.0.trace(tracer); @@ -228,6 +266,7 @@ unsafe impl Trace for (A, B) { } unsafe impl Trace for (A, B, C) { + type StaticId = (A::StaticId, B::StaticId, C::StaticId); #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { self.0.trace(tracer); @@ -237,6 +276,7 @@ unsafe impl Trace for (A, B, C) { } unsafe impl Trace for (A, B, C, D) { + type StaticId = (A::StaticId, B::StaticId, C::StaticId, D::StaticId); #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { self.0.trace(tracer); @@ -247,6 +287,13 @@ unsafe impl Trace for (A, B, C, D) { } unsafe impl Trace for (A, B, C, D, E) { + type StaticId = ( + A::StaticId, + B::StaticId, + C::StaticId, + D::StaticId, + E::StaticId, + ); #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { self.0.trace(tracer); @@ -257,17 +304,24 @@ unsafe impl Trace for (A, B, C } } -unsafe impl Trace for rust_alloc::rc::Rc { +// Rc/Arc are reference-counted, not GC-traced. They cannot contain live Gc +// pointers (that would create a cycle the GC cannot see). StaticId uses the +// 'static-bounded form so TypeId is well-formed. +unsafe impl Trace for rust_alloc::rc::Rc { + type StaticId = rust_alloc::rc::Rc; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } -unsafe impl Trace for rust_alloc::sync::Arc { +unsafe impl Trace for rust_alloc::sync::Arc { + type StaticId = rust_alloc::sync::Arc; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } -unsafe impl Trace for BTreeMap { +// K is not traced (BTreeMap keys are immutable); require K: 'static for StaticId. +unsafe impl Trace for BTreeMap { + type StaticId = BTreeMap; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.values() { v.trace(tracer); @@ -275,30 +329,37 @@ unsafe impl Trace for BTreeMap { } } -unsafe impl Trace for BTreeSet { +// BTreeSet keys are never traced; require T: 'static for StaticId. +unsafe impl Trace for BTreeSet { + type StaticId = BTreeSet; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } +// str is a DST, so we cannot allocate it directly. Use String as the Sized proxy. unsafe impl Trace for str { + type StaticId = String; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } #[cfg(feature = "icu")] unsafe impl Trace for icu_locale_core::LanguageIdentifier { + type StaticId = icu_locale_core::LanguageIdentifier; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } #[cfg(feature = "icu")] unsafe impl Trace for icu_locale_core::Locale { + type StaticId = icu_locale_core::Locale; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } #[cfg(feature = "either")] unsafe impl Trace for either::Either { + type StaticId = either::Either; unsafe fn trace(&self, tracer: &mut Tracer) { match self { either::Either::Left(l) => l.trace(tracer), @@ -309,6 +370,7 @@ unsafe impl Trace for either::Either { #[cfg(feature = "arrayvec")] unsafe impl Trace for arrayvec::ArrayVec { + type StaticId = arrayvec::ArrayVec; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self { v.trace(tracer); @@ -316,7 +378,8 @@ unsafe impl Trace for arrayvec::ArrayVec { } } -unsafe impl Trace for hashbrown::hash_map::HashMap { +unsafe impl Trace for hashbrown::hash_map::HashMap { + type StaticId = hashbrown::hash_map::HashMap; #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { for (k, v) in self { @@ -327,7 +390,8 @@ unsafe impl Trace for hashbrown::hash_map::HashMap Trace for hashbrown::hash_set::HashSet { +unsafe impl Trace for hashbrown::hash_set::HashSet { + type StaticId = hashbrown::hash_set::HashSet; #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { for v in self { @@ -338,6 +402,7 @@ unsafe impl Trace for hashbrown::hash_set::HashSet { // Finalize is already implemented in common.rs unsafe impl Trace for rust_alloc::collections::BinaryHeap { + type StaticId = rust_alloc::collections::BinaryHeap; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) { // BinaryHeap has no iter_mut(); the null collector's trace is a no-op @@ -348,30 +413,35 @@ unsafe impl Trace for rust_alloc::collections::BinaryHeap { #[cfg(feature = "std")] unsafe impl Trace for std::path::Path { + type StaticId = std::path::PathBuf; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } #[cfg(feature = "std")] unsafe impl Trace for std::path::PathBuf { + type StaticId = std::path::PathBuf; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } #[cfg(feature = "std")] unsafe impl Trace for std::time::Instant { + type StaticId = std::time::Instant; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } #[cfg(feature = "std")] unsafe impl Trace for std::time::SystemTime { + type StaticId = std::time::SystemTime; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } #[cfg(feature = "std")] -unsafe impl Trace for std::collections::HashMap { +unsafe impl Trace for std::collections::HashMap { + type StaticId = std::collections::HashMap; #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { for (k, v) in self { @@ -382,7 +452,8 @@ unsafe impl Trace for std::collections::HashMap } #[cfg(feature = "std")] -unsafe impl Trace for std::collections::HashSet { +unsafe impl Trace for std::collections::HashSet { + type StaticId = std::collections::HashSet; #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { for v in self { diff --git a/oscars/src/collectors/null_collector_branded/weak.rs b/oscars/src/collectors/null_collector_branded/weak.rs index 012ca87..3c60f68 100644 --- a/oscars/src/collectors/null_collector_branded/weak.rs +++ b/oscars/src/collectors/null_collector_branded/weak.rs @@ -69,7 +69,12 @@ impl<'id, T: Trace + ?Sized> PartialEq for WeakGc<'id, T> { impl<'id, T: Trace + ?Sized> Eq for WeakGc<'id, T> {} impl<'id, T: Trace + ?Sized> Finalize for WeakGc<'id, T> {} -unsafe impl<'id, T: Trace + ?Sized> Trace for WeakGc<'id, T> { +unsafe impl<'id, T: Trace + ?Sized> Trace for WeakGc<'id, T> +where + T::StaticId: Sized, +{ + // WeakGc<'id, T> maps to WeakGc<'static, T::StaticId> as the static proxy. + type StaticId = WeakGc<'static, T::StaticId>; unsafe fn trace(&self, _tracer: &mut crate::collectors::null_collector_branded::trace::Tracer) { } } From 40363d4999da3434f209af1349a0bdc75399a513 Mon Sep 17 00:00:00 2001 From: shruti2522 Date: Thu, 13 Aug 2026 03:25:32 +0000 Subject: [PATCH 2/2] Replace StaticId with dtonlay's typeid crate --- Cargo.lock | 7 ++ oscars/Cargo.toml | 1 + .../src/collectors/mark_sweep_branded/cell.rs | 7 +- .../mark_sweep_branded/ephemeron.rs | 7 +- .../src/collectors/mark_sweep_branded/gc.rs | 18 +--- .../collectors/mark_sweep_branded/gc_box.rs | 11 +- .../mark_sweep_branded/tests/mod.rs | 2 - .../mark_sweep_branded/tests/uaf.rs | 2 - .../collectors/mark_sweep_branded/trace.rs | 102 ++---------------- .../src/collectors/mark_sweep_branded/weak.rs | 7 +- .../collectors/null_collector_branded/cell.rs | 6 +- .../null_collector_branded/ephemeron.rs | 7 +- .../collectors/null_collector_branded/gc.rs | 17 +-- .../null_collector_branded/gc_box.rs | 11 +- .../null_collector_branded/trace.rs | 76 ++----------- .../collectors/null_collector_branded/weak.rs | 7 +- 16 files changed, 47 insertions(+), 241 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0d3e419..49f2604 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -391,6 +391,7 @@ dependencies = [ "rustc-hash", "thin-vec", "trybuild", + "typeid", ] [[package]] @@ -699,6 +700,12 @@ dependencies = [ "toml", ] +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + [[package]] name = "unicode-ident" version = "1.0.22" diff --git a/oscars/Cargo.toml b/oscars/Cargo.toml index 491bc64..35f9455 100644 --- a/oscars/Cargo.toml +++ b/oscars/Cargo.toml @@ -12,6 +12,7 @@ thin-vec = { version = "0.2", optional = true } icu_locale_core = { version = "2.2.0", default-features = false, optional = true } either = { version = "1.16.0", optional = true } arrayvec = { version = "0.7.6", optional = true } +typeid = "1.0.3" [dev-dependencies] criterion = { version = "0.5", features = ["html_reports"] } diff --git a/oscars/src/collectors/mark_sweep_branded/cell.rs b/oscars/src/collectors/mark_sweep_branded/cell.rs index f55d600..779fb9d 100644 --- a/oscars/src/collectors/mark_sweep_branded/cell.rs +++ b/oscars/src/collectors/mark_sweep_branded/cell.rs @@ -174,12 +174,7 @@ impl<'a, T: Trace + ?Sized> GcRefMut<'a, T> { impl Finalize for GcRefCell {} -unsafe impl Trace for GcRefCell -where - T::StaticId: Sized, -{ - // GcRefCell branded by T's lifetime, map to the static form. - type StaticId = GcRefCell; +unsafe impl Trace for GcRefCell { unsafe fn trace(&self, tracer: &mut Tracer) { let val = unsafe { &*self.inner.as_ptr() }; unsafe { diff --git a/oscars/src/collectors/mark_sweep_branded/ephemeron.rs b/oscars/src/collectors/mark_sweep_branded/ephemeron.rs index 587ea2b..f183f2d 100644 --- a/oscars/src/collectors/mark_sweep_branded/ephemeron.rs +++ b/oscars/src/collectors/mark_sweep_branded/ephemeron.rs @@ -106,12 +106,7 @@ impl<'id, K: Trace + ?Sized, V: Trace> Copy for Ephemeron<'id, K, V> {} impl<'id, K: Trace + ?Sized, V: Trace> Finalize for Ephemeron<'id, K, V> {} -unsafe impl<'id, K: Trace + ?Sized, V: Trace> Trace for Ephemeron<'id, K, V> -where - K::StaticId: Sized, -{ - // Ephemeron<'id, K, V> -> Ephemeron<'static, K::StaticId, V::StaticId> - type StaticId = Ephemeron<'static, K::StaticId, V::StaticId>; +unsafe impl<'id, K: Trace + ?Sized, V: Trace> Trace for Ephemeron<'id, K, V> { // Ephemerons do not mark their key; liveness of the key is determined // by the GC independently. The value is marked via the GC's ephemeron // fixpoint phase in `Collector::collect`. diff --git a/oscars/src/collectors/mark_sweep_branded/gc.rs b/oscars/src/collectors/mark_sweep_branded/gc.rs index d9b7964..df5f18c 100644 --- a/oscars/src/collectors/mark_sweep_branded/gc.rs +++ b/oscars/src/collectors/mark_sweep_branded/gc.rs @@ -10,6 +10,7 @@ use crate::{ use core::fmt; use core::marker::PhantomData; use core::ops::Deref; +use typeid; /// A transient pointer to a GC-managed value. #[derive(Debug)] @@ -91,14 +92,13 @@ impl<'gc, T: Trace + ?Sized + 'gc> Gc<'gc, T> { /// Returns `true` if the inner value is of type `U`. /// - /// Uses `TypeId` via `U::StaticId`, sound even when `U` carries a branded - /// lifetime because `StaticId` is the lifetime erased proxy defined on the - /// `Trace` trait. This avoids the `T: 'static` restriction while still + /// Uses `typeid::of::()`, sound even when `U` carries a branded + /// lifetime because it properly handles branded lifetimes. This avoids the `T: 'static` restriction while still /// giving us a stable, unique identity guarantee. #[inline] pub fn is(&self) -> bool { let actual_type_id = unsafe { (*self.ptr.as_ptr().as_ptr()).0.type_id }; - actual_type_id == core::any::TypeId::of::() + actual_type_id == typeid::of::() } #[inline] @@ -155,15 +155,7 @@ impl<'gc, T: Trace + ?Sized + 'gc> Deref for Gc<'gc, T> { } impl Finalize for Gc<'_, T> {} -unsafe impl<'gc, T: Trace + ?Sized + 'gc> Trace for Gc<'gc, T> -where - T::StaticId: Sized, -{ - // The StaticId of Gc<'gc, T> is Gc<'static, T::StaticId>. - // This maps any branded Gc to a fully 'static form, giving a unique TypeId - // per pointee type regardless of which 'gc brand is in use. - type StaticId = Gc<'static, T::StaticId>; - +unsafe impl<'gc, T: Trace + ?Sized + 'gc> Trace for Gc<'gc, T> { unsafe fn trace(&self, tracer: &mut crate::collectors::mark_sweep_branded::trace::Tracer) { tracer.mark(self); } diff --git a/oscars/src/collectors/mark_sweep_branded/gc_box.rs b/oscars/src/collectors/mark_sweep_branded/gc_box.rs index 91431ee..8a2a0d2 100644 --- a/oscars/src/collectors/mark_sweep_branded/gc_box.rs +++ b/oscars/src/collectors/mark_sweep_branded/gc_box.rs @@ -35,10 +35,9 @@ pub struct GcBox { pub(crate) alloc_id: usize, /// Unique identifier for the concrete type `T`. /// - /// Stored as `TypeId::of::()`, we use the `StaticId` proxy - /// type so that branded lifetimes (eg. `'gc`) don't require `T: 'static`. - /// Two values whose erased types share the same `StaticId` produce the - /// same `TypeId`, which is exactly what we want for sound downcasting. + /// Stored as `typeid::of::()`. This safely erases branded lifetimes + /// (eg. `'gc`) without requiring `T: 'static`, giving us a stable + /// unique identity guarantee for sound downcasting. pub(crate) type_id: TypeId, /// The user value. pub(crate) value: T, @@ -51,14 +50,14 @@ impl GcBox { impl GcBox { /// Create a [`GcBox`] for `value`, `color` starts as [`GcColor::White`]. /// - /// Requires `T: Trace` to access `T::StaticId` for the `TypeId`. + /// Requires `T: Trace` for the `TypeId`. pub(crate) fn new(value: T, trace_fn: TraceFn, drop_fn: DropFn, alloc_id: usize) -> Self { Self { color: Cell::new(GcColor::White), trace_fn, drop_fn, alloc_id, - type_id: TypeId::of::(), + type_id: typeid::of::(), value, } } diff --git a/oscars/src/collectors/mark_sweep_branded/tests/mod.rs b/oscars/src/collectors/mark_sweep_branded/tests/mod.rs index e59f2ee..dd95e42 100644 --- a/oscars/src/collectors/mark_sweep_branded/tests/mod.rs +++ b/oscars/src/collectors/mark_sweep_branded/tests/mod.rs @@ -7,8 +7,6 @@ struct JsObject { } unsafe impl crate::collectors::mark_sweep_branded::Trace for JsObject { - // JsObject contains no GC pointers and no lifetimes, so its static proxy is itself. - type StaticId = JsObject; unsafe fn trace(&self, _tracer: &mut crate::collectors::mark_sweep_branded::trace::Tracer) {} } impl crate::collectors::mark_sweep_branded::Finalize for JsObject {} diff --git a/oscars/src/collectors/mark_sweep_branded/tests/uaf.rs b/oscars/src/collectors/mark_sweep_branded/tests/uaf.rs index 612925d..921d148 100644 --- a/oscars/src/collectors/mark_sweep_branded/tests/uaf.rs +++ b/oscars/src/collectors/mark_sweep_branded/tests/uaf.rs @@ -6,8 +6,6 @@ use core::cell::Cell; struct DetectDrop<'a>(&'a Cell); unsafe impl<'a> Trace for DetectDrop<'a> { - // DetectDrop<'a> borrows a local `Cell`, the static proxy is DetectDrop<'static>. - type StaticId = DetectDrop<'static>; unsafe fn trace(&self, _tracer: &mut crate::collectors::mark_sweep_branded::trace::Tracer) {} } diff --git a/oscars/src/collectors/mark_sweep_branded/trace.rs b/oscars/src/collectors/mark_sweep_branded/trace.rs index c92d02d..34dffac 100644 --- a/oscars/src/collectors/mark_sweep_branded/trace.rs +++ b/oscars/src/collectors/mark_sweep_branded/trace.rs @@ -23,7 +23,6 @@ pub use crate::collectors::common::Finalize; /// /// Use `Tracer::mark` for every reachable `Gc` pointer. pub unsafe trait Trace { - type StaticId: 'static + Trace; /// Marks all `Gc` pointers reachable from `self`. /// /// # Safety @@ -127,14 +126,7 @@ impl<'a> Tracer<'a> { } } -// For &T, the StaticId is &'static T::StaticId. Since &U is always Sized, this -// satisfies the Sized requirement on StaticId even when T::StaticId is a DST. -// We add the bound T::StaticId: Sized to keep things simple and unambiguous. -unsafe impl Trace for &T -where - T::StaticId: Sized, -{ - type StaticId = &'static T::StaticId; +unsafe impl Trace for &T { #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } @@ -145,7 +137,6 @@ macro_rules! empty_trace { ($($T:ty),* $(,)?) => { $( unsafe impl Trace for $T { - type StaticId = $T; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } @@ -190,13 +181,11 @@ empty_trace![ // str is a DST; we cannot allocate it directly. Use String as the Sized proxy. unsafe impl Trace for str { - type StaticId = String; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } unsafe impl Trace for [T; N] { - type StaticId = [T::StaticId; N]; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.iter() { v.trace(tracer); @@ -204,10 +193,8 @@ unsafe impl Trace for [T; N] { } } -// Slices [T] cannot be allocated directly in the GC. Their StaticId is a -// Vec, which is always Sized and avoids Box fixed point divergence. +// Slices [T] cannot be allocated directly in the GC. unsafe impl Trace for [T] { - type StaticId = Vec; #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { for v in self { @@ -217,19 +204,13 @@ unsafe impl Trace for [T] { } // Box where T: ?Sized. Box is always Sized even for DST contents. -// We require T::StaticId: Sized to produce a concrete Sized StaticId. -unsafe impl Trace for Box -where - T::StaticId: Sized, -{ - type StaticId = Box; +unsafe impl Trace for Box { unsafe fn trace(&self, tracer: &mut Tracer) { (**self).trace(tracer); } } unsafe impl Trace for Option { - type StaticId = Option; unsafe fn trace(&self, tracer: &mut Tracer) { if let Some(v) = self { v.trace(tracer); @@ -238,7 +219,6 @@ unsafe impl Trace for Option { } unsafe impl Trace for Result { - type StaticId = Result; unsafe fn trace(&self, tracer: &mut Tracer) { match self { Ok(v) => v.trace(tracer), @@ -248,7 +228,6 @@ unsafe impl Trace for Result { } unsafe impl Trace for Vec { - type StaticId = Vec; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.iter() { v.trace(tracer); @@ -258,7 +237,6 @@ unsafe impl Trace for Vec { #[cfg(feature = "thin-vec")] unsafe impl Trace for thin_vec::ThinVec { - type StaticId = thin_vec::ThinVec; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.iter() { v.trace(tracer); @@ -267,7 +245,6 @@ unsafe impl Trace for thin_vec::ThinVec { } unsafe impl Trace for VecDeque { - type StaticId = VecDeque; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.iter() { v.trace(tracer); @@ -276,7 +253,6 @@ unsafe impl Trace for VecDeque { } unsafe impl Trace for LinkedList { - type StaticId = LinkedList; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.iter() { v.trace(tracer); @@ -285,26 +261,19 @@ unsafe impl Trace for LinkedList { } // PhantomData doesn't trace T, so T need not implement Trace. -// For StaticId we require T: 'static so the proxy type itself is 'static. unsafe impl Trace for PhantomData { - type StaticId = PhantomData; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } unsafe impl Trace for core::any::TypeId { - type StaticId = core::any::TypeId; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } // Cell> requires T: Copy to safely read the value via Cell::get(). // For non-Copy types, use GcRefCell instead. -unsafe impl Trace for Cell -where - T::StaticId: Default, -{ - type StaticId = Cell; +unsafe impl Trace for Cell { unsafe fn trace(&self, tracer: &mut Tracer) { let v = self.take(); v.trace(tracer); @@ -313,7 +282,6 @@ where } unsafe impl Trace for OnceCell { - type StaticId = OnceCell; unsafe fn trace(&self, tracer: &mut Tracer) { if let Some(v) = self.get() { v.trace(tracer); @@ -324,10 +292,8 @@ unsafe impl Trace for OnceCell { unsafe impl Trace for Cow<'static, T> where T::Owned: Trace, - T::StaticId: ToOwned, { // T is already 'static so we can use it directly as the proxy. - type StaticId = Cow<'static, T>; unsafe fn trace(&self, tracer: &mut Tracer) { if let Cow::Owned(v) = self { v.trace(tracer); @@ -336,7 +302,6 @@ where } unsafe impl Trace for (A,) { - type StaticId = (A::StaticId,); #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { self.0.trace(tracer); @@ -344,7 +309,6 @@ unsafe impl Trace for (A,) { } unsafe impl Trace for (A, B) { - type StaticId = (A::StaticId, B::StaticId); #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { self.0.trace(tracer); @@ -353,7 +317,6 @@ unsafe impl Trace for (A, B) { } unsafe impl Trace for (A, B, C) { - type StaticId = (A::StaticId, B::StaticId, C::StaticId); #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { self.0.trace(tracer); @@ -363,7 +326,6 @@ unsafe impl Trace for (A, B, C) { } unsafe impl Trace for (A, B, C, D) { - type StaticId = (A::StaticId, B::StaticId, C::StaticId, D::StaticId); unsafe fn trace(&self, tracer: &mut Tracer) { self.0.trace(tracer); self.1.trace(tracer); @@ -373,13 +335,6 @@ unsafe impl Trace for (A, B, C, D) { } unsafe impl Trace for (A, B, C, D, E) { - type StaticId = ( - A::StaticId, - B::StaticId, - C::StaticId, - D::StaticId, - E::StaticId, - ); unsafe fn trace(&self, tracer: &mut Tracer) { self.0.trace(tracer); self.1.trace(tracer); @@ -392,14 +347,6 @@ unsafe impl Trace for (A, B, C unsafe impl Trace for (A, B, C, D, E, F) { - type StaticId = ( - A::StaticId, - B::StaticId, - C::StaticId, - D::StaticId, - E::StaticId, - F::StaticId, - ); unsafe fn trace(&self, tracer: &mut Tracer) { self.0.trace(tracer); self.1.trace(tracer); @@ -413,15 +360,6 @@ unsafe impl Trace unsafe impl Trace for (A, B, C, D, E, F, G) { - type StaticId = ( - A::StaticId, - B::StaticId, - C::StaticId, - D::StaticId, - E::StaticId, - F::StaticId, - G::StaticId, - ); unsafe fn trace(&self, tracer: &mut Tracer) { self.0.trace(tracer); self.1.trace(tracer); @@ -436,16 +374,6 @@ unsafe impl Trace for (A, B, C, D, E, F, G, H) { - type StaticId = ( - A::StaticId, - B::StaticId, - C::StaticId, - D::StaticId, - E::StaticId, - F::StaticId, - G::StaticId, - H::StaticId, - ); unsafe fn trace(&self, tracer: &mut Tracer) { self.0.trace(tracer); self.1.trace(tracer); @@ -462,23 +390,19 @@ unsafe impl Trace for rust_alloc::rc::Rc { - type StaticId = rust_alloc::rc::Rc; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } unsafe impl Trace for rust_alloc::sync::Arc { - type StaticId = rust_alloc::sync::Arc; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } -// K is not traced (BTreeMap keys are immutable); require K: 'static for StaticId. +// K is not traced (BTreeMap keys are immutable). unsafe impl Trace for BTreeMap { - type StaticId = BTreeMap; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.values() { v.trace(tracer); @@ -486,9 +410,8 @@ unsafe impl Trace for BTreeMap { } } -// BTreeSet keys are never traced; require T: 'static for StaticId. +// BTreeSet keys are never traced. unsafe impl Trace for BTreeSet { - type StaticId = BTreeSet; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) { // BTreeSet keys are immutable and cannot contain Gc pointers @@ -502,13 +425,11 @@ mod icu_trace { use icu_locale_core::{LanguageIdentifier, Locale}; unsafe impl Trace for LanguageIdentifier { - type StaticId = LanguageIdentifier; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } unsafe impl Trace for Locale { - type StaticId = Locale; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } @@ -519,7 +440,6 @@ mod either_trace { use crate::collectors::mark_sweep_branded::{Trace, Tracer}; unsafe impl Trace for either::Either { - type StaticId = either::Either; unsafe fn trace(&self, tracer: &mut Tracer) { match self { either::Either::Left(l) => l.trace(tracer), @@ -531,7 +451,6 @@ mod either_trace { #[cfg(feature = "arrayvec")] unsafe impl Trace for arrayvec::ArrayVec { - type StaticId = arrayvec::ArrayVec; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self { v.trace(tracer); @@ -541,35 +460,30 @@ unsafe impl Trace for arrayvec::ArrayVec { #[cfg(feature = "std")] unsafe impl Trace for std::path::Path { - type StaticId = std::path::PathBuf; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } #[cfg(feature = "std")] unsafe impl Trace for std::path::PathBuf { - type StaticId = std::path::PathBuf; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } #[cfg(feature = "std")] unsafe impl Trace for std::time::Instant { - type StaticId = std::time::Instant; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } #[cfg(feature = "std")] unsafe impl Trace for std::time::SystemTime { - type StaticId = std::time::SystemTime; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } #[cfg(feature = "std")] unsafe impl Trace for std::collections::HashMap { - type StaticId = std::collections::HashMap; #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { for (k, v) in self { @@ -581,7 +495,6 @@ unsafe impl Trace for std::collections::HashMap< #[cfg(feature = "std")] unsafe impl Trace for std::collections::HashSet { - type StaticId = std::collections::HashSet; #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { for v in self { @@ -591,7 +504,6 @@ unsafe impl Trace for std::collections::HashSet { } unsafe impl Trace for rust_alloc::collections::BinaryHeap { - type StaticId = rust_alloc::collections::BinaryHeap; #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.iter() { diff --git a/oscars/src/collectors/mark_sweep_branded/weak.rs b/oscars/src/collectors/mark_sweep_branded/weak.rs index 0ba05b4..9ff658e 100644 --- a/oscars/src/collectors/mark_sweep_branded/weak.rs +++ b/oscars/src/collectors/mark_sweep_branded/weak.rs @@ -92,12 +92,7 @@ impl<'id, T: Trace + ?Sized> PartialEq for WeakGc<'id, T> { } impl<'id, T: Trace + ?Sized> Finalize for WeakGc<'id, T> {} -unsafe impl<'id, T: Trace + ?Sized> Trace for WeakGc<'id, T> -where - T::StaticId: Sized, -{ - // WeakGc<'id, T> maps to WeakGc<'static, T::StaticId> as the static proxy. - type StaticId = WeakGc<'static, T::StaticId>; +unsafe impl<'id, T: Trace + ?Sized> Trace for WeakGc<'id, T> { // Weak references do not mark their target, upgrade() returning None after collection is the intended behaviour. unsafe fn trace(&self, _tracer: &mut crate::collectors::mark_sweep_branded::trace::Tracer) {} } diff --git a/oscars/src/collectors/null_collector_branded/cell.rs b/oscars/src/collectors/null_collector_branded/cell.rs index b825e84..6040ab6 100644 --- a/oscars/src/collectors/null_collector_branded/cell.rs +++ b/oscars/src/collectors/null_collector_branded/cell.rs @@ -133,12 +133,8 @@ impl<'a, T: Trace + ?Sized> GcRefMut<'a, T> { impl Finalize for GcRefCell {} -unsafe impl Trace for GcRefCell -where - T::StaticId: Sized, -{ +unsafe impl Trace for GcRefCell { // GcRefCell<'gc, T> is branded by T's lifetime. Map to the static form. - type StaticId = GcRefCell; #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { // SAFETY: We only access the inner value for tracing and do not mutate it. diff --git a/oscars/src/collectors/null_collector_branded/ephemeron.rs b/oscars/src/collectors/null_collector_branded/ephemeron.rs index 3d239f1..209acd9 100644 --- a/oscars/src/collectors/null_collector_branded/ephemeron.rs +++ b/oscars/src/collectors/null_collector_branded/ephemeron.rs @@ -78,11 +78,6 @@ impl<'id, K: Trace + ?Sized, V: Trace> Copy for Ephemeron<'id, K, V> {} impl<'id, K: Trace + ?Sized, V: Trace> Finalize for Ephemeron<'id, K, V> {} -unsafe impl<'id, K: Trace + ?Sized, V: Trace> Trace for Ephemeron<'id, K, V> -where - K::StaticId: Sized, -{ - // Ephemeron<'id, K, V> -> Ephemeron<'static, K::StaticId, V::StaticId> - type StaticId = Ephemeron<'static, K::StaticId, V::StaticId>; +unsafe impl<'id, K: Trace + ?Sized, V: Trace> Trace for Ephemeron<'id, K, V> { unsafe fn trace(&self, _tracer: &mut Tracer) {} } diff --git a/oscars/src/collectors/null_collector_branded/gc.rs b/oscars/src/collectors/null_collector_branded/gc.rs index eaffceb..1d93714 100644 --- a/oscars/src/collectors/null_collector_branded/gc.rs +++ b/oscars/src/collectors/null_collector_branded/gc.rs @@ -82,14 +82,13 @@ impl<'gc, T: Trace + ?Sized + 'gc> Gc<'gc, T> { /// Returns `true` if the inner value is of type `U`. /// - /// Uses `TypeId` via `U::StaticId`, sound even when `U` carries a branded - /// lifetime because `StaticId` is the lifetime erased proxy defined on the - /// `Trace` trait. This avoids the `T: 'static` restriction while still + /// Uses `typeid::of::()`, sound even when `U` carries a branded + /// lifetime because it properly handles branded lifetimes. This avoids the `T: 'static` restriction while still /// giving us a stable, unique identity guarantee #[inline] pub fn is(&self) -> bool { let actual_type_id = unsafe { (*self.ptr.as_ptr().as_ptr()).0.type_id }; - actual_type_id == core::any::TypeId::of::() + actual_type_id == typeid::of::() } #[inline] @@ -147,15 +146,7 @@ impl<'gc, T: Trace + ?Sized + 'gc> Deref for Gc<'gc, T> { impl Finalize for Gc<'_, T> {} -unsafe impl<'gc, T: Trace + ?Sized + 'gc> Trace for Gc<'gc, T> -where - T::StaticId: Sized, -{ - // The StaticId of Gc<'gc, T> is Gc<'static, T::StaticId>. - // This maps any branded Gc to a fully 'static form, giving a unique TypeId - // per pointee type regardless of which 'gc brand is in use. - type StaticId = Gc<'static, T::StaticId>; - +unsafe impl<'gc, T: Trace + ?Sized + 'gc> Trace for Gc<'gc, T> { unsafe fn trace(&self, tracer: &mut crate::collectors::null_collector_branded::trace::Tracer) { tracer.mark(self); } diff --git a/oscars/src/collectors/null_collector_branded/gc_box.rs b/oscars/src/collectors/null_collector_branded/gc_box.rs index 26910a7..933ba1c 100644 --- a/oscars/src/collectors/null_collector_branded/gc_box.rs +++ b/oscars/src/collectors/null_collector_branded/gc_box.rs @@ -14,10 +14,9 @@ pub struct GcBox { pub(crate) drop_fn: DropFn, /// Unique identifier for the concrete type `T`. /// - /// Stored as `TypeId::of::()`, we use the `StaticId` proxy - /// type so that branded lifetimes (eg. `'gc`) don't require `T: 'static`. - /// Two values whose erased types share the same `StaticId` produce the - /// same `TypeId`, which is exactly what we want for sound downcasting. + /// Stored as `typeid::of::()`. This safely erases branded lifetimes + /// (eg. `'gc`) without requiring `T: 'static`, giving us a stable + /// unique identity guarantee for sound downcasting. pub(crate) type_id: TypeId, /// User value pub(crate) value: T, @@ -26,11 +25,11 @@ pub struct GcBox { impl GcBox { /// Create a [`GcBox`] for `value`. /// - /// Requires `T: Trace` to access `T::StaticId` for the `TypeId`. + /// Requires `T: Trace` for the `TypeId`. pub(crate) fn new(value: T, drop_fn: DropFn) -> Self { Self { drop_fn, - type_id: TypeId::of::(), + type_id: typeid::of::(), value, } } diff --git a/oscars/src/collectors/null_collector_branded/trace.rs b/oscars/src/collectors/null_collector_branded/trace.rs index 06943b9..567020c 100644 --- a/oscars/src/collectors/null_collector_branded/trace.rs +++ b/oscars/src/collectors/null_collector_branded/trace.rs @@ -21,7 +21,6 @@ use rust_alloc::vec::Vec; /// While the null collector reclaims no memory, implementations must be /// sound for other collectors to prevent UAF bugs. pub unsafe trait Trace { - type StaticId: 'static + Trace; /// Marks all `Gc` pointers reachable from `self`. /// /// # Safety @@ -58,14 +57,7 @@ impl<'a> Tracer<'a> { } } -// For &T, the StaticId is &'static T::StaticId. Since &U is always Sized, this -// satisfies the Sized requirement on StaticId even when T::StaticId is a DST. -// We add the bound T::StaticId: Sized to keep things simple and unambiguous. -unsafe impl Trace for &T -where - T::StaticId: Sized, -{ - type StaticId = &'static T::StaticId; +unsafe impl Trace for &T { #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } @@ -74,7 +66,6 @@ macro_rules! empty_trace { ($($T:ty),* $(,)?) => { $( unsafe impl Trace for $T { - type StaticId = $T; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } @@ -118,7 +109,6 @@ empty_trace![ ]; unsafe impl Trace for [T; N] { - type StaticId = [T::StaticId; N]; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.iter() { v.trace(tracer); @@ -126,10 +116,8 @@ unsafe impl Trace for [T; N] { } } -// Slices [T] cannot be allocated directly in the GC. Their StaticId is a -// Vec, which is always Sized and avoids Box fixed point divergence. +// Slices [T] cannot be allocated directly in the GC. unsafe impl Trace for [T] { - type StaticId = Vec; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.iter() { v.trace(tracer); @@ -138,12 +126,7 @@ unsafe impl Trace for [T] { } // Box where T: ?Sized. Box is always Sized even for DST contents. -// We require T::StaticId: Sized to produce a concrete Sized StaticId. -unsafe impl Trace for Box -where - T::StaticId: Sized, -{ - type StaticId = Box; +unsafe impl Trace for Box { unsafe fn trace(&self, tracer: &mut Tracer) { (**self).trace(tracer); } @@ -151,7 +134,6 @@ where #[cfg(feature = "thin-vec")] unsafe impl Trace for thin_vec::ThinVec { - type StaticId = thin_vec::ThinVec; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.iter() { v.trace(tracer); @@ -160,7 +142,6 @@ unsafe impl Trace for thin_vec::ThinVec { } unsafe impl Trace for Option { - type StaticId = Option; unsafe fn trace(&self, tracer: &mut Tracer) { if let Some(v) = self { v.trace(tracer); @@ -169,7 +150,6 @@ unsafe impl Trace for Option { } unsafe impl Trace for Result { - type StaticId = Result; unsafe fn trace(&self, tracer: &mut Tracer) { match self { Ok(v) => v.trace(tracer), @@ -179,7 +159,6 @@ unsafe impl Trace for Result { } unsafe impl Trace for Vec { - type StaticId = Vec; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.iter() { v.trace(tracer); @@ -188,7 +167,6 @@ unsafe impl Trace for Vec { } unsafe impl Trace for VecDeque { - type StaticId = VecDeque; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.iter() { v.trace(tracer); @@ -197,7 +175,6 @@ unsafe impl Trace for VecDeque { } unsafe impl Trace for LinkedList { - type StaticId = LinkedList; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.iter() { v.trace(tracer); @@ -206,18 +183,12 @@ unsafe impl Trace for LinkedList { } // PhantomData doesn't trace T, so T need not implement Trace. -// For StaticId we require T: 'static so the proxy type itself is 'static. unsafe impl Trace for PhantomData { - type StaticId = PhantomData; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } -unsafe impl Trace for Cell -where - T::StaticId: Default, -{ - type StaticId = Cell; +unsafe impl Trace for Cell { unsafe fn trace(&self, tracer: &mut Tracer) { let v = self.take(); v.trace(tracer); @@ -226,7 +197,6 @@ where } unsafe impl Trace for OnceCell { - type StaticId = OnceCell; unsafe fn trace(&self, tracer: &mut Tracer) { if let Some(v) = self.get() { v.trace(tracer); @@ -237,10 +207,8 @@ unsafe impl Trace for OnceCell { unsafe impl Trace for Cow<'static, T> where T::Owned: Trace, - T::StaticId: ToOwned, { // T is already 'static so we can use it directly as the proxy. - type StaticId = Cow<'static, T>; unsafe fn trace(&self, tracer: &mut Tracer) { if let Cow::Owned(v) = self { v.trace(tracer); @@ -249,7 +217,6 @@ where } unsafe impl Trace for (A,) { - type StaticId = (A::StaticId,); #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { self.0.trace(tracer); @@ -257,7 +224,6 @@ unsafe impl Trace for (A,) { } unsafe impl Trace for (A, B) { - type StaticId = (A::StaticId, B::StaticId); #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { self.0.trace(tracer); @@ -266,7 +232,6 @@ unsafe impl Trace for (A, B) { } unsafe impl Trace for (A, B, C) { - type StaticId = (A::StaticId, B::StaticId, C::StaticId); #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { self.0.trace(tracer); @@ -276,7 +241,6 @@ unsafe impl Trace for (A, B, C) { } unsafe impl Trace for (A, B, C, D) { - type StaticId = (A::StaticId, B::StaticId, C::StaticId, D::StaticId); #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { self.0.trace(tracer); @@ -287,13 +251,6 @@ unsafe impl Trace for (A, B, C, D) { } unsafe impl Trace for (A, B, C, D, E) { - type StaticId = ( - A::StaticId, - B::StaticId, - C::StaticId, - D::StaticId, - E::StaticId, - ); #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { self.0.trace(tracer); @@ -305,23 +262,19 @@ unsafe impl Trace for (A, B, C } // Rc/Arc are reference-counted, not GC-traced. They cannot contain live Gc -// pointers (that would create a cycle the GC cannot see). StaticId uses the -// 'static-bounded form so TypeId is well-formed. +// pointers (that would create a cycle the GC cannot see). unsafe impl Trace for rust_alloc::rc::Rc { - type StaticId = rust_alloc::rc::Rc; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } unsafe impl Trace for rust_alloc::sync::Arc { - type StaticId = rust_alloc::sync::Arc; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } -// K is not traced (BTreeMap keys are immutable); require K: 'static for StaticId. +// K is not traced (BTreeMap keys are immutable). unsafe impl Trace for BTreeMap { - type StaticId = BTreeMap; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self.values() { v.trace(tracer); @@ -329,37 +282,32 @@ unsafe impl Trace for BTreeMap { } } -// BTreeSet keys are never traced; require T: 'static for StaticId. +// BTreeSet keys are never traced. unsafe impl Trace for BTreeSet { - type StaticId = BTreeSet; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } // str is a DST, so we cannot allocate it directly. Use String as the Sized proxy. unsafe impl Trace for str { - type StaticId = String; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } #[cfg(feature = "icu")] unsafe impl Trace for icu_locale_core::LanguageIdentifier { - type StaticId = icu_locale_core::LanguageIdentifier; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } #[cfg(feature = "icu")] unsafe impl Trace for icu_locale_core::Locale { - type StaticId = icu_locale_core::Locale; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } #[cfg(feature = "either")] unsafe impl Trace for either::Either { - type StaticId = either::Either; unsafe fn trace(&self, tracer: &mut Tracer) { match self { either::Either::Left(l) => l.trace(tracer), @@ -370,7 +318,6 @@ unsafe impl Trace for either::Either { #[cfg(feature = "arrayvec")] unsafe impl Trace for arrayvec::ArrayVec { - type StaticId = arrayvec::ArrayVec; unsafe fn trace(&self, tracer: &mut Tracer) { for v in self { v.trace(tracer); @@ -379,7 +326,6 @@ unsafe impl Trace for arrayvec::ArrayVec { } unsafe impl Trace for hashbrown::hash_map::HashMap { - type StaticId = hashbrown::hash_map::HashMap; #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { for (k, v) in self { @@ -391,7 +337,6 @@ unsafe impl Trace for hashbrown::hash_map::HashM // Finalize is already implemented in common.rs unsafe impl Trace for hashbrown::hash_set::HashSet { - type StaticId = hashbrown::hash_set::HashSet; #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { for v in self { @@ -402,7 +347,6 @@ unsafe impl Trace for hashbrown::hash_set::HashSet { // Finalize is already implemented in common.rs unsafe impl Trace for rust_alloc::collections::BinaryHeap { - type StaticId = rust_alloc::collections::BinaryHeap; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) { // BinaryHeap has no iter_mut(); the null collector's trace is a no-op @@ -413,35 +357,30 @@ unsafe impl Trace for rust_alloc::collections::BinaryHeap { #[cfg(feature = "std")] unsafe impl Trace for std::path::Path { - type StaticId = std::path::PathBuf; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } #[cfg(feature = "std")] unsafe impl Trace for std::path::PathBuf { - type StaticId = std::path::PathBuf; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } #[cfg(feature = "std")] unsafe impl Trace for std::time::Instant { - type StaticId = std::time::Instant; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } #[cfg(feature = "std")] unsafe impl Trace for std::time::SystemTime { - type StaticId = std::time::SystemTime; #[inline] unsafe fn trace(&self, _tracer: &mut Tracer) {} } #[cfg(feature = "std")] unsafe impl Trace for std::collections::HashMap { - type StaticId = std::collections::HashMap; #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { for (k, v) in self { @@ -453,7 +392,6 @@ unsafe impl Trace for std::collections::HashMap< #[cfg(feature = "std")] unsafe impl Trace for std::collections::HashSet { - type StaticId = std::collections::HashSet; #[inline] unsafe fn trace(&self, tracer: &mut Tracer) { for v in self { diff --git a/oscars/src/collectors/null_collector_branded/weak.rs b/oscars/src/collectors/null_collector_branded/weak.rs index 3c60f68..012ca87 100644 --- a/oscars/src/collectors/null_collector_branded/weak.rs +++ b/oscars/src/collectors/null_collector_branded/weak.rs @@ -69,12 +69,7 @@ impl<'id, T: Trace + ?Sized> PartialEq for WeakGc<'id, T> { impl<'id, T: Trace + ?Sized> Eq for WeakGc<'id, T> {} impl<'id, T: Trace + ?Sized> Finalize for WeakGc<'id, T> {} -unsafe impl<'id, T: Trace + ?Sized> Trace for WeakGc<'id, T> -where - T::StaticId: Sized, -{ - // WeakGc<'id, T> maps to WeakGc<'static, T::StaticId> as the static proxy. - type StaticId = WeakGc<'static, T::StaticId>; +unsafe impl<'id, T: Trace + ?Sized> Trace for WeakGc<'id, T> { unsafe fn trace(&self, _tracer: &mut crate::collectors::null_collector_branded::trace::Tracer) { } }