diff --git a/gc/src/lib.rs b/gc/src/lib.rs index efc6e8b..77fbd0c 100644 --- a/gc/src/lib.rs +++ b/gc/src/lib.rs @@ -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 // diff --git a/gc/src/trace.rs b/gc/src/trace.rs index ce02f1d..ad33953 100644 --- a/gc/src/trace.rs +++ b/gc/src/trace.rs @@ -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 Finalize for T { // XXX: Should this function somehow tell its caller (which is presumably diff --git a/gc/tests/trace_impl.rs b/gc/tests/trace_impl.rs index da492bd..e6b096d 100644 --- a/gc/tests/trace_impl.rs +++ b/gc/tests/trace_impl.rs @@ -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| { @@ -59,6 +62,12 @@ struct Baz { b: Bar, } +#[derive(Trace)] +#[trivially_drop] +struct Dereferenced { + a: FooWithoutCopy, +} + #[test] fn test() { let bar = Bar { inner: Foo }; @@ -74,4 +83,6 @@ fn test() { baz.trace(); } X.with(|x| assert!(*x.borrow() == 3)); + + let Dereferenced { a: _a } = Dereferenced { a: FooWithoutCopy }; } diff --git a/gc_derive/src/lib.rs b/gc_derive/src/lib.rs index a2e37b3..923aaf7 100644 --- a/gc_derive/src/lib.rs +++ b/gc_derive/src/lib.rs @@ -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 @@ -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 + } } }