Skip to content

Commit

Permalink
feat: allow deriving Trace without Drop/Finalize
Browse files Browse the repository at this point in the history
Custom Drop implementation still can't be written, because generated
TriviallyDrop implementation tries to destruct value, which is only
possible when there is no user defined Drop impl

Empty Finalize impl generated too, because it will never be called

Signed-off-by: Yaroslav Bolyukin <[email protected]>
  • Loading branch information
CertainLach committed Jan 2, 2021
1 parent 7f3837f commit aa4e896
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 17 deletions.
2 changes: 1 addition & 1 deletion gc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub use gc_derive::{Finalize, Trace};
// We re-export the Trace method, as well as some useful internal methods for
// managing collections or configuring the garbage collector.
pub use crate::gc::{finalizer_safe, force_collect};
pub use crate::trace::{Finalize, Trace};
pub use crate::trace::{Finalize, Trace, TriviallyDrop};

////////
// Gc //
Expand Down
6 changes: 6 additions & 0 deletions gc/src/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ pub trait Finalize {
fn finalize(&self) {}
}

/// Ensures type has no custom Drop implementation, without implementing it itself
/// In case of Drop implementation present, obscure compilation error will be raised
pub unsafe trait TriviallyDrop {
unsafe fn guard(self);
}

#[cfg(feature = "nightly")]
impl<T: ?Sized> Finalize for T {
// XXX: Should this function somehow tell its caller (which is presumably
Expand Down
11 changes: 11 additions & 0 deletions gc/tests/trace_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ use gc::Trace;
#[derive(Copy, Clone, Finalize)]
struct Foo;

#[derive(Trace, Finalize)]
struct FooWithoutCopy;

unsafe impl Trace for Foo {
unsafe fn trace(&self) {
X.with(|x| {
Expand Down Expand Up @@ -59,6 +62,12 @@ struct Baz {
b: Bar,
}

#[derive(Trace)]
#[trivially_drop]
struct Dereferenced {
a: FooWithoutCopy,
}

#[test]
fn test() {
let bar = Bar { inner: Foo };
Expand All @@ -74,4 +83,6 @@ fn test() {
baz.trace();
}
X.with(|x| assert!(*x.borrow() == 3));

let Dereferenced { a: _a } = Dereferenced { a: FooWithoutCopy };
}
61 changes: 45 additions & 16 deletions gc_derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
use quote::quote;
use synstructure::{decl_derive, Structure};
use synstructure::{decl_derive, BindStyle, Structure};

decl_derive!([Trace, attributes(unsafe_ignore_trace)] => derive_trace);
decl_derive!([Trace, attributes(unsafe_ignore_trace, trivially_drop)] => derive_trace);

fn derive_trace(mut s: Structure<'_>) -> proc_macro2::TokenStream {
let is_trivially_drop = s
.ast()
.attrs
.iter()
.any(|attr| attr.path.is_ident("trivially_drop"));
s.filter(|bi| {
!bi.ast()
.attrs
Expand Down Expand Up @@ -51,23 +56,47 @@ fn derive_trace(mut s: Structure<'_>) -> proc_macro2::TokenStream {
},
);

// We also implement drop to prevent unsafe drop implementations on this
// type and encourage people to use Finalize. This implementation will
// call `Finalize::finalize` if it is safe to do so.
let drop_impl = s.unbound_impl(
quote!(::std::ops::Drop),
if !is_trivially_drop {
// We also implement drop to prevent unsafe drop implementations on this
// type and encourage people to use Finalize. This implementation will
// call `Finalize::finalize` if it is safe to do so.
let drop_impl = s.unbound_impl(
quote!(::std::ops::Drop),
quote! {
fn drop(&mut self) {
if ::gc::finalizer_safe() {
::gc::Finalize::finalize(self);
}
}
},
);

quote! {
fn drop(&mut self) {
if ::gc::finalizer_safe() {
::gc::Finalize::finalize(self);
#trace_impl
#drop_impl
}
} else {
let finalize_impl = derive_finalize(s.clone());

s.bind_with(|_| BindStyle::Move);
let trivially_drop_body = s.each(|bi| quote!(drop(#bi)));

let trivially_drop_impl = s.unsafe_bound_impl(
quote!(::gc::TriviallyDrop),
quote!(
unsafe fn guard(self) {
match self {
#trivially_drop_body
}
}
}
},
);
),
);

quote! {
#trace_impl
#drop_impl
quote! {
#trace_impl
#trivially_drop_impl
#finalize_impl
}
}
}

Expand Down

0 comments on commit aa4e896

Please sign in to comment.