Skip to content

Commit

Permalink
Massively simplify the situation with Root-able types and the arena m…
Browse files Browse the repository at this point in the history
…acros
  • Loading branch information
kyren committed Nov 27, 2022
1 parent 3a2ef58 commit 9fea270
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 48 deletions.
53 changes: 22 additions & 31 deletions src/gc-arena/src/arena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,51 +60,42 @@ impl ArenaParameters {
}
}

/// A convenience macro for creating a typedef for an [`Arena`] of a specific type without having to
/// go through an implementation of `RootProvider`.
/// A trait that produces a [`Collect`]-able type for the given lifetime. This is used to produce
/// the root [`Collect`] instance in an [`Arena`].
///
/// In order to use an implementation of this trait in an [`Arena`], it must implement
/// `RootProvider<'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 RootProvider<'a> {
type Root: Collect;
}

/// A convenience macro for quickly creating type that implements of `RootProvider`.
///
/// The macro takes two parameters, the name you would like to give the arena type, and the type of
/// the arena root. The root type must implement the [`Collect`] trait, and be a type that takes a
/// single generic lifetime parameter which is used for any held `Gc` pointer types.
/// The macro takes a single argument, which should be a generic type that references a `'gc`
/// lifetime. When used as a root object, this `'gc` lifetime will be replaced with the branding
/// lifetime.
///
/// An example:
/// ```
/// # use gc_arena::{Collect, Gc, make_arena};
/// # use gc_arena::{Arena, Collect, Gc, root_provider};
/// #
/// # fn main() {
/// #[derive(Collect)]
/// #[collect(no_drop)]
/// struct MyRoot<'gc> {
/// ptr: Gc<'gc, i32>,
/// }
/// make_arena!(MyArena, MyRoot);
///
/// type MyArena = Arena<root_provider!(MyRoot<'gc>)>;
/// # }
/// ```
#[macro_export]
macro_rules! make_arena {
($arena:ident, $root:ident) => {
make_arena!(pub(self) $arena, $root);
macro_rules! root_provider {
($root:ty) => {
// Instead of generating an impl of `RootProvider`, we use a trait object. Thus, we avoid
// the need to generate a new type for each invocation of this macro.
dyn for<'gc> $crate::RootProvider<'gc, Root = $root>
};

($vis:vis $arena:ident, $root:ident) => {
// Instead of generating an impl of `RootProvider`, we use a trait object.
// The projection `<R as RootProvider<'gc>>::Root` is used to obtain the root
// type with the lifetime `'gc` applied
// By using a trait object, we avoid the need to generate a new type for each
// invocation of this macro, which would lead to name conflicts if the macro was
// used multiple times in the same scope.
$vis type $arena = $crate::Arena<dyn for<'a> $crate::RootProvider<'a, Root = $root<'a>>>;
};
}

/// A trait that produces a [`Collect`]-able type for the given lifetime. This is used to produce
/// the root [`Collect`] instance in an [`Arena`].
///
/// In order to use an implementation of this trait in an [`Arena`], it must implement
/// `RootProvider<'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 RootProvider<'a> {
type Root: Collect;
}

/// A helper type alias for a `RootProvider::Root` for a specific lifetime.
Expand Down
26 changes: 11 additions & 15 deletions src/gc-arena/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ use rand::distributions::Distribution;
use std::collections::HashMap;
use std::rc::Rc;

use gc_arena::{make_arena, unsafe_empty_collect, ArenaParameters, Collect, Gc, GcCell, GcWeak};
use gc_arena::{
root_provider, unsafe_empty_collect, Arena, ArenaParameters, Collect, Gc, GcCell, GcWeak,
};

#[test]
fn simple_allocation() {
Expand All @@ -14,11 +16,10 @@ fn simple_allocation() {
test: Gc<'gc, i32>,
}

make_arena!(TestArena, TestRoot);

let arena = TestArena::new(ArenaParameters::default(), |mc| TestRoot {
test: Gc::allocate(mc, 42),
});
let arena =
Arena::<root_provider!(TestRoot<'gc>)>::new(ArenaParameters::default(), |mc| TestRoot {
test: Gc::allocate(mc, 42),
});

arena.mutate(|_mc, root| {
assert_eq!(*((*root).test), 42);
Expand All @@ -34,9 +35,7 @@ fn weak_allocation() {
weak: GcWeak<'gc, i32>,
}

make_arena!(TestArena, TestRoot);

let mut arena = TestArena::new(ArenaParameters::default(), |mc| {
let mut arena = Arena::<root_provider!(TestRoot<'gc>)>::new(ArenaParameters::default(), |mc| {
let test = Gc::allocate(mc, 42);
let weak = Gc::downgrade(test);
assert!(weak.upgrade(mc).is_some());
Expand Down Expand Up @@ -80,11 +79,10 @@ fn repeated_allocation_deallocation() {
#[derive(Collect)]
#[collect(no_drop)]
struct TestRoot<'gc>(GcCell<'gc, HashMap<i32, Gc<'gc, (i32, RefCounter)>>>);
make_arena!(TestArena, TestRoot);

let r = RefCounter(Rc::new(()));

let mut arena = TestArena::new(ArenaParameters::default(), |mc| {
let mut arena = Arena::<root_provider!(TestRoot<'gc>)>::new(ArenaParameters::default(), |mc| {
TestRoot(GcCell::allocate(mc, HashMap::new()))
});

Expand Down Expand Up @@ -128,11 +126,10 @@ fn all_dropped() {
#[derive(Collect)]
#[collect(no_drop)]
struct TestRoot<'gc>(GcCell<'gc, Vec<Gc<'gc, RefCounter>>>);
make_arena!(TestArena, TestRoot);

let r = RefCounter(Rc::new(()));

let arena = TestArena::new(ArenaParameters::default(), |mc| {
let arena = Arena::<root_provider!(TestRoot<'gc>)>::new(ArenaParameters::default(), |mc| {
TestRoot(GcCell::allocate(mc, Vec::new()))
});

Expand All @@ -155,11 +152,10 @@ fn all_garbage_collected() {
#[derive(Collect)]
#[collect(no_drop)]
struct TestRoot<'gc>(GcCell<'gc, Vec<Gc<'gc, RefCounter>>>);
make_arena!(TestArena, TestRoot);

let r = RefCounter(Rc::new(()));

let mut arena = TestArena::new(ArenaParameters::default(), |mc| {
let mut arena = Arena::<root_provider!(TestRoot<'gc>)>::new(ArenaParameters::default(), |mc| {
TestRoot(GcCell::allocate(mc, Vec::new()))
});

Expand Down
4 changes: 2 additions & 2 deletions src/gc-sequence/src/sequencable_arena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ macro_rules! make_sequencable_arena {
use core::any::Any;
use core::marker::PhantomData;

use gc_arena::{make_arena, ArenaParameters, Collect, GcCell, MutationContext};
use gc_arena::{root_provider, ArenaParameters, Collect, GcCell, MutationContext};
use gc_sequence::{Sequence, SequenceExt};

use super::$root;
Expand All @@ -77,7 +77,7 @@ macro_rules! make_sequencable_arena {
>,
}

make_arena!(InnerArena, InnerRoot);
type InnerArena = gc_arena::Arena<root_provider!(InnerRoot<'gc>)>;

$innervis struct Arena(InnerArena);

Expand Down

0 comments on commit 9fea270

Please sign in to comment.