From aa4e896be604e5d1616dbce8a055cdd1a759644c Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sat, 2 Jan 2021 21:34:36 +0300 Subject: [PATCH 1/2] feat: allow deriving Trace without Drop/Finalize 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 --- gc/src/lib.rs | 2 +- gc/src/trace.rs | 6 +++++ gc/tests/trace_impl.rs | 11 ++++++++ gc_derive/src/lib.rs | 61 +++++++++++++++++++++++++++++++----------- 4 files changed, 63 insertions(+), 17 deletions(-) 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 + } } } From fe4fea0e1909c61650fba4479d48b73b54ec799e Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Sun, 3 Jan 2021 17:35:23 +0300 Subject: [PATCH 2/2] refactor: remove TriviallyDrop trait Signed-off-by: Yaroslav Bolyukin --- gc/src/lib.rs | 2 +- gc/src/trace.rs | 6 ------ gc_derive/src/lib.rs | 16 +++++----------- 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/gc/src/lib.rs b/gc/src/lib.rs index 77fbd0c..efc6e8b 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, TriviallyDrop}; +pub use crate::trace::{Finalize, Trace}; //////// // Gc // diff --git a/gc/src/trace.rs b/gc/src/trace.rs index ad33953..ce02f1d 100644 --- a/gc/src/trace.rs +++ b/gc/src/trace.rs @@ -17,12 +17,6 @@ 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_derive/src/lib.rs b/gc_derive/src/lib.rs index 923aaf7..195a75f 100644 --- a/gc_derive/src/lib.rs +++ b/gc_derive/src/lib.rs @@ -76,25 +76,19 @@ fn derive_trace(mut s: Structure<'_>) -> proc_macro2::TokenStream { #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), + let trivially_drop_body = s.each(|_| quote! {}); + let finalize_impl = s.bound_impl( + quote!(::gc::Finalize), quote!( - unsafe fn guard(self) { - match self { - #trivially_drop_body - } + fn finalize(&self) { + let _trivially_drop = |t: Self| match t { #trivially_drop_body }; } ), ); quote! { #trace_impl - #trivially_drop_impl #finalize_impl } }