diff --git a/src/arena.rs b/src/arena.rs index 9b116c9f..413bc915 100644 --- a/src/arena.rs +++ b/src/arena.rs @@ -13,8 +13,8 @@ use crate::{ /// In order to use an implementation of this trait in an [`Arena`], it must implement /// `Rootable<'a>` for *any* possible `'a`. This is necessary so that the `Root` types can be /// branded by the unique, invariant lifetimes that makes an `Arena` sound. -pub trait Rootable<'a>: 'static { - type Root: Collect + 'a; +pub trait Rootable<'a> { + type Root: ?Sized + 'a; } /// A marker type used by the `Rootable!` macro instead of a bare trait object. @@ -115,12 +115,19 @@ pub enum CollectionPhase { /// this way, incremental garbage collection can be achieved (assuming "sufficiently small" calls /// to `mutate`) that is both extremely safe and zero overhead vs what you would write in C with raw /// pointers and manually ensuring that invariants are held. -pub struct Arena Rootable<'a>> { +pub struct Arena +where + R: for<'a> Rootable<'a>, +{ context: Box, root: Root<'static, R>, } -impl Rootable<'a>> Arena { +impl Arena +where + R: for<'a> Rootable<'a>, + for<'a> Root<'a, R>: Sized, +{ /// Create a new arena with the given garbage collector tuning parameters. You must provide a /// closure that accepts a `&Mutation<'gc>` and returns the appropriate root. pub fn new(f: F) -> Arena @@ -155,6 +162,51 @@ impl Rootable<'a>> Arena { } } + #[inline] + pub fn map_root( + self, + f: impl for<'gc> FnOnce(&'gc Mutation<'gc>, Root<'gc, R>) -> Root<'gc, R2>, + ) -> Arena + where + R2: for<'a> Rootable<'a>, + for<'a> Root<'a, R2>: Sized, + { + self.context.root_barrier(); + let new_root: Root<'static, R2> = unsafe { + let mc: &'static Mutation<'_> = &*(self.context.mutation_context() as *const _); + f(mc, self.root) + }; + Arena { + context: self.context, + root: new_root, + } + } + + #[inline] + pub fn try_map_root( + self, + f: impl for<'gc> FnOnce(&'gc Mutation<'gc>, Root<'gc, R>) -> Result, E>, + ) -> Result, E> + where + R2: for<'a> Rootable<'a>, + for<'a> Root<'a, R2>: Sized, + { + self.context.root_barrier(); + let new_root: Root<'static, R2> = unsafe { + let mc: &'static Mutation<'_> = &*(self.context.mutation_context() as *const _); + f(mc, self.root)? + }; + Ok(Arena { + context: self.context, + root: new_root, + }) + } +} + +impl Arena +where + R: for<'a> Rootable<'a>, +{ /// The primary means of interacting with a garbage collected arena. Accepts a callback which /// receives a `&Mutation<'gc>` and a reference to the root, and can return any non garbage /// collected value. The callback may "mutate" any part of the object graph during this call, @@ -186,38 +238,6 @@ impl Rootable<'a>> Arena { } } - #[inline] - pub fn map_root Rootable<'a>>( - self, - f: impl for<'gc> FnOnce(&'gc Mutation<'gc>, Root<'gc, R>) -> Root<'gc, R2>, - ) -> Arena { - self.context.root_barrier(); - let new_root: Root<'static, R2> = unsafe { - let mc: &'static Mutation<'_> = &*(self.context.mutation_context() as *const _); - f(mc, self.root) - }; - Arena { - context: self.context, - root: new_root, - } - } - - #[inline] - pub fn try_map_root Rootable<'a>, E>( - self, - f: impl for<'gc> FnOnce(&'gc Mutation<'gc>, Root<'gc, R>) -> Result, E>, - ) -> Result, E> { - self.context.root_barrier(); - let new_root: Root<'static, R2> = unsafe { - let mc: &'static Mutation<'_> = &*(self.context.mutation_context() as *const _); - f(mc, self.root)? - }; - Ok(Arena { - context: self.context, - root: new_root, - }) - } - #[inline] pub fn metrics(&self) -> &Metrics { self.context.metrics() @@ -238,7 +258,13 @@ impl Rootable<'a>> Arena { Phase::Drop => unreachable!(), } } +} +impl Arena +where + R: for<'a> Rootable<'a>, + for<'a> Root<'a, R>: Collect, +{ /// Run incremental garbage collection until the allocation debt is <= 0.0. /// /// There is no minimum unit of work enforced here, so it may be faster to only call this method @@ -314,7 +340,11 @@ impl Rootable<'a>> Arena { pub struct MarkedArena<'a, R: for<'b> Rootable<'b>>(&'a mut Arena); -impl<'a, R: for<'b> Rootable<'b>> MarkedArena<'a, R> { +impl<'a, R> MarkedArena<'a, R> +where + R: for<'b> Rootable<'b>, + for<'b> Root<'b, R>: Collect, +{ /// Examine the state of a fully marked arena. /// /// Allows you to determine whether `GcWeak` pointers are "dead" (aka, soon-to-be-dropped) and diff --git a/src/context.rs b/src/context.rs index 3ef165ac..8a6a673f 100644 --- a/src/context.rs +++ b/src/context.rs @@ -229,7 +229,7 @@ impl Context { // reachable from the given root object. // // If we are currently in `Phase::Sleep`, this will transition the collector to `Phase::Mark`. - pub(crate) unsafe fn do_collection( + pub(crate) unsafe fn do_collection( &self, root: &R, target_debt: f64, @@ -238,7 +238,7 @@ impl Context { self.do_collection_inner(root, target_debt, early_stop) } - fn do_collection_inner( + fn do_collection_inner( &self, root: &R, mut target_debt: f64, diff --git a/tests/tests.rs b/tests/tests.rs index 1998f310..a2fdc4cd 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -394,7 +394,7 @@ fn test_dynamic_roots() { let rc_a = Rc::new(12); let rc_b = Rc::new("hello".to_owned()); - let mut arena: Arena]> = Arena::new(|mc| DynamicRootSet::new(mc)); + let mut arena = Arena::]>::new(|mc| DynamicRootSet::new(mc)); let root_a = arena .mutate(|mc, root_set| root_set.stash::]>(mc, Gc::new(mc, rc_a.clone()))); @@ -456,9 +456,8 @@ fn test_dynamic_roots() { #[test] #[should_panic] fn test_dynamic_bad_set() { - let arena1: Arena]> = Arena::new(|mc| DynamicRootSet::new(mc)); - - let arena2: Arena]> = Arena::new(|mc| DynamicRootSet::new(mc)); + let arena1 = Arena::]>::new(|mc| DynamicRootSet::new(mc)); + let arena2 = Arena::]>::new(|mc| DynamicRootSet::new(mc)); let dyn_root = arena1.mutate(|mc, root| root.stash::(mc, Gc::new(mc, 44)));