Skip to content

Commit

Permalink
Merge pull request #71 from moulins/elided-rootable
Browse files Browse the repository at this point in the history
Use elided lifetimes instead of `'gc` for the short form of `Rootable!`
  • Loading branch information
kyren authored Jun 2, 2023
2 parents 6e2677b + 5c5546c commit d718a49
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 27 deletions.
2 changes: 1 addition & 1 deletion src/gc-arena-derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ proc-macro = true
[dependencies]
proc-macro2 = "1.0.56"
quote = "1.0.26"
syn = "2.0.13"
syn = { version = "2.0.17", features = ["default", "visit-mut"] }
synstructure = "0.13"
41 changes: 40 additions & 1 deletion src/gc-arena-derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned, ToTokens};
use syn::spanned::Spanned;
use syn::{
parse::{Parse, ParseStream},
spanned::Spanned,
visit_mut::VisitMut,
};
use synstructure::{decl_derive, AddBounds};

fn find_collect_meta(attrs: &[syn::Attribute]) -> syn::Result<Option<&syn::Attribute>> {
Expand Down Expand Up @@ -259,3 +263,38 @@ decl_derive! {
/// struct/enum is marked with `require_static` then this is unnecessary.
collect_derive
}

// Not public API; implementation detail of `gc_arena::Rootable!`.
// Replaces all `'_` lifetimes in a type by the specified named lifetime.
// Syntax: `__unelide_lifetimes!('lt; SomeType)`.
#[doc(hidden)]
#[proc_macro]
pub fn __unelide_lifetimes(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
struct Input {
lt: syn::Lifetime,
ty: syn::Type,
}

impl Parse for Input {
fn parse(input: ParseStream) -> syn::Result<Self> {
let lt: syn::Lifetime = input.parse()?;
let _: syn::Token!(;) = input.parse()?;
let ty: syn::Type = input.parse()?;
Ok(Self { lt, ty })
}
}

struct UnelideLifetimes(syn::Lifetime);

impl VisitMut for UnelideLifetimes {
fn visit_lifetime_mut(&mut self, i: &mut syn::Lifetime) {
if i.ident == "_" {
*i = self.0.clone();
}
}
}

let mut input = syn::parse_macro_input!(input as Input);
UnelideLifetimes(input.lt).visit_type_mut(&mut input.ty);
input.ty.to_token_stream().into()
}
17 changes: 10 additions & 7 deletions src/gc-arena/src/arena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,11 @@ impl<'a, T: ?Sized + Rootable<'a>> Rootable<'a> for __DynRootable<T> {
type Root = <T as Rootable<'a>>::Root;
}

/// A convenience macro for quickly creating type that implements of `Rootable`.
/// A convenience macro for quickly creating type that implements `Rootable`.
///
/// 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.
/// The macro takes a single argument, which should be a generic type with elided lifetimes.
/// When used as a root object, every instances of the elided lifetime will be replaced with
/// the branding lifetime.
///
/// ```
/// # use gc_arena::{Arena, Collect, Gc, Rootable};
Expand All @@ -96,7 +96,10 @@ impl<'a, T: ?Sized + Rootable<'a>> Rootable<'a> for __DynRootable<T> {
/// ptr: Gc<'gc, i32>,
/// }
///
/// type MyArena = Arena<Rootable![MyRoot<'gc>]>;
/// type MyArena = Arena<Rootable![MyRoot<'_>]>;
///
/// // If desired, the branding lifetime can also be explicitely named:
/// type MyArena2 = Arena<Rootable!['gc => MyRoot<'gc>]>;
/// # }
/// ```
///
Expand All @@ -113,7 +116,7 @@ impl<'a, T: ?Sized + Rootable<'a>> Rootable<'a> for __DynRootable<T> {
/// ptr: Gc<'gc, StaticCollect<T>>,
/// }
///
/// type MyGenericArena<T> = Arena<Rootable![MyGenericRoot<'gc, T>]>;
/// type MyGenericArena<T> = Arena<Rootable![MyGenericRoot<'_, T>]>;
/// # }
/// ```
#[macro_export]
Expand All @@ -124,7 +127,7 @@ macro_rules! Rootable {
$crate::__DynRootable::<dyn for<$gc> $crate::Rootable<$gc, Root = $root>>
};
($root:ty) => {
$crate::Rootable!['gc => $root]
$crate::Rootable!['__gc => $crate::__unelide_lifetimes!('__gc; $root)]
};
}

Expand Down
36 changes: 18 additions & 18 deletions src/gc-arena/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fn simple_allocation() {
test: Gc<'gc, i32>,
}

let arena = Arena::<Rootable![TestRoot<'gc>]>::new(ArenaParameters::default(), |mc| TestRoot {
let arena = Arena::<Rootable![TestRoot<'_>]>::new(ArenaParameters::default(), |mc| TestRoot {
test: Gc::new(mc, 42),
});

Expand All @@ -36,7 +36,7 @@ fn weak_allocation() {
weak: GcWeak<'gc, i32>,
}

let mut arena = Arena::<Rootable![TestRoot<'gc>]>::new(ArenaParameters::default(), |mc| {
let mut arena = Arena::<Rootable![TestRoot<'_>]>::new(ArenaParameters::default(), |mc| {
let test = Gc::new(mc, 42);
let weak = Gc::downgrade(test);
assert!(weak.upgrade(mc).is_some());
Expand Down Expand Up @@ -87,7 +87,7 @@ fn dyn_sized_allocation() {

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

let mut arena = Arena::<Rootable![TestRoot<'gc>]>::new(ArenaParameters::default(), |mc| {
let mut arena = Arena::<Rootable![TestRoot<'_>]>::new(ArenaParameters::default(), |mc| {
let array: [_; SIZE] = core::array::from_fn(|_| Gc::new(mc, counter.clone()));
let slice = unsize!(Gc::new(mc, array) => [_]);
TestRoot { slice }
Expand Down Expand Up @@ -121,7 +121,7 @@ fn repeated_allocation_deallocation() {

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

let mut arena = Arena::<Rootable![TestRoot<'gc>]>::new(ArenaParameters::default(), |mc| {
let mut arena = Arena::<Rootable![TestRoot<'_>]>::new(ArenaParameters::default(), |mc| {
TestRoot(Gc::new(mc, RefLock::new(HashMap::new())))
});

Expand Down Expand Up @@ -168,7 +168,7 @@ fn all_dropped() {

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

let arena = Arena::<Rootable![TestRoot<'gc>]>::new(ArenaParameters::default(), |mc| {
let arena = Arena::<Rootable![TestRoot<'_>]>::new(ArenaParameters::default(), |mc| {
TestRoot(Gc::new(mc, RefLock::new(Vec::new())))
});

Expand All @@ -194,7 +194,7 @@ fn all_garbage_collected() {

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

let mut arena = Arena::<Rootable![TestRoot<'gc>]>::new(ArenaParameters::default(), |mc| {
let mut arena = Arena::<Rootable![TestRoot<'_>]>::new(ArenaParameters::default(), |mc| {
TestRoot(Gc::new(mc, RefLock::new(Vec::new())))
});

Expand Down Expand Up @@ -354,7 +354,7 @@ fn test_map() {
some_complex_state: Vec<Gc<'gc, i32>>,
}

let arena = Arena::<Rootable![Root<'gc>]>::new(ArenaParameters::default(), |mc| Root {
let arena = Arena::<Rootable![Root<'_>]>::new(ArenaParameters::default(), |mc| Root {
some_complex_state: vec![Gc::new(mc, 42), Gc::new(mc, 69)],
});

Expand All @@ -365,7 +365,7 @@ fn test_map() {
state: Gc<'gc, i32>,
}

let arena = arena.map_root::<Rootable![Intermediate<'gc>]>(|_, root| {
let arena = arena.map_root::<Rootable![Intermediate<'_>]>(|_, root| {
let state = root.some_complex_state[0];
Intermediate { root, state }
});
Expand All @@ -376,7 +376,7 @@ fn test_map() {
});

let arena = arena
.try_map_root::<Rootable![Intermediate<'gc>], ()>(|_, intermediate| {
.try_map_root::<Rootable![Intermediate<'_>], ()>(|_, intermediate| {
let state = intermediate.root.some_complex_state[1];
Ok(Intermediate {
root: intermediate.root,
Expand All @@ -393,20 +393,20 @@ fn test_map() {

#[test]
fn test_dynamic_roots() {
let mut arena: Arena<Rootable![DynamicRootSet<'gc>]> =
let mut arena: Arena<Rootable![DynamicRootSet<'_>]> =
Arena::new(ArenaParameters::default(), |mc| DynamicRootSet::new(mc));

let initial_size = arena.total_allocated();

let root1 =
arena.mutate(|mc, root_set| root_set.stash::<Rootable![Gc<'gc, i32>]>(mc, Gc::new(mc, 12)));
arena.mutate(|mc, root_set| root_set.stash::<Rootable![Gc<'_, i32>]>(mc, Gc::new(mc, 12)));

#[derive(Collect)]
#[collect(no_drop)]
struct Root2<'gc>(Gc<'gc, i32>, Gc<'gc, bool>);

let root2 = arena.mutate(|mc, root_set| {
root_set.stash::<Rootable![Root2<'gc>]>(mc, Root2(Gc::new(mc, 27), Gc::new(mc, true)))
root_set.stash::<Rootable![Root2<'_>]>(mc, Root2(Gc::new(mc, 27), Gc::new(mc, true)))
});

arena.collect_all();
Expand Down Expand Up @@ -435,18 +435,18 @@ fn test_dynamic_roots() {
#[test]
#[should_panic]
fn test_dynamic_bad_set() {
let arena1: Arena<Rootable![DynamicRootSet<'gc>]> =
let arena1: Arena<Rootable![DynamicRootSet<'_>]> =
Arena::new(ArenaParameters::default(), |mc| DynamicRootSet::new(mc));

let arena2: Arena<Rootable![DynamicRootSet<'gc>]> =
let arena2: Arena<Rootable![DynamicRootSet<'_>]> =
Arena::new(ArenaParameters::default(), |mc| DynamicRootSet::new(mc));

#[derive(Collect)]
#[collect(no_drop)]
struct Root<'gc>(Gc<'gc, i32>);

let dyn_root =
arena1.mutate(|mc, root| root.stash::<Rootable![Root<'gc>]>(mc, Root(Gc::new(mc, 44))));
arena1.mutate(|mc, root| root.stash::<Rootable![Root<'_>]>(mc, Root(Gc::new(mc, 44))));

arena2.mutate(|_, root| {
root.fetch(&dyn_root);
Expand Down Expand Up @@ -485,7 +485,7 @@ fn test_collect_overflow() {
}

let mut arena =
Arena::<Rootable![TestRoot<'gc>]>::new(ArenaParameters::default(), |mc| TestRoot {
Arena::<Rootable![TestRoot<'_>]>::new(ArenaParameters::default(), |mc| TestRoot {
test: Gc::new(mc, [0; 256]),
});

Expand Down Expand Up @@ -577,7 +577,7 @@ fn okay_panic() {
}
}

let mut arena = Arena::<Rootable![Gc<'gc, Test<'gc>>]>::new(ArenaParameters::default(), |mc| {
let mut arena = Arena::<Rootable![Gc<'_, Test<'_>>]>::new(ArenaParameters::default(), |mc| {
Gc::new(
mc,
Test {
Expand Down Expand Up @@ -622,7 +622,7 @@ fn field_locks() {
nested: Nested<'gc>,
}

let arena = Arena::<Rootable![Gc<'gc, Test<'gc>>]>::new(ArenaParameters::default(), |mc| {
let arena = Arena::<Rootable![Gc<'_, Test<'_>>]>::new(ArenaParameters::default(), |mc| {
Gc::new(
mc,
Test {
Expand Down

0 comments on commit d718a49

Please sign in to comment.