Skip to main content

jrsonnet_gc_derive/
lib.rs

1use quote::quote;
2use synstructure::{decl_derive, AddBounds, BindStyle, Structure};
3
4decl_derive!([Trace, attributes(unsafe_ignore_trace, trivially_drop)] => derive_trace);
5
6fn derive_trace(mut s: Structure<'_>) -> proc_macro2::TokenStream {
7    let is_trivially_drop = s
8        .ast()
9        .attrs
10        .iter()
11        .any(|attr| attr.path.is_ident("trivially_drop"));
12    s.filter(|bi| {
13        !bi.ast()
14            .attrs
15            .iter()
16            .any(|attr| attr.path.is_ident("unsafe_ignore_trace"))
17    });
18    let trace_body = s.each(|bi| quote!(mark(#bi)));
19
20    s.add_bounds(AddBounds::Fields);
21    let trace_impl = s.unsafe_bound_impl(
22        quote!(::jrsonnet_gc::Trace),
23        quote! {
24            #[inline] unsafe fn trace(&self) {
25                #[allow(dead_code)]
26                #[inline]
27                unsafe fn mark<T: ::jrsonnet_gc::Trace + ?Sized>(it: &T) {
28                    ::jrsonnet_gc::Trace::trace(it);
29                }
30                match *self { #trace_body }
31            }
32            #[inline] unsafe fn root(&self) {
33                #[allow(dead_code)]
34                #[inline]
35                unsafe fn mark<T: ::jrsonnet_gc::Trace + ?Sized>(it: &T) {
36                    ::jrsonnet_gc::Trace::root(it);
37                }
38                match *self { #trace_body }
39            }
40            #[inline] unsafe fn unroot(&self) {
41                #[allow(dead_code)]
42                #[inline]
43                unsafe fn mark<T: ::jrsonnet_gc::Trace + ?Sized>(it: &T) {
44                    ::jrsonnet_gc::Trace::unroot(it);
45                }
46                match *self { #trace_body }
47            }
48            #[inline] fn finalize_glue(&self) {
49                ::jrsonnet_gc::Finalize::finalize(self);
50                #[allow(dead_code)]
51                #[inline]
52                fn mark<T: ::jrsonnet_gc::Trace + ?Sized>(it: &T) {
53                    ::jrsonnet_gc::Trace::finalize_glue(it);
54                }
55                match *self { #trace_body }
56            }
57        },
58    );
59
60    if !is_trivially_drop {
61        // We also implement drop to prevent unsafe drop implementations on this
62        // type and encourage people to use Finalize. This implementation will
63        // call `Finalize::finalize` if it is safe to do so.
64        let drop_impl = s.unbound_impl(
65            quote!(::std::ops::Drop),
66            quote! {
67                fn drop(&mut self) {
68                    if ::jrsonnet_gc::finalizer_safe() {
69                        ::jrsonnet_gc::Finalize::finalize(self);
70                    }
71                }
72            },
73        );
74
75        quote! {
76            #trace_impl
77            #drop_impl
78        }
79    } else {
80        s.bind_with(|_| BindStyle::Move);
81        let trivially_drop_body = s.each(|_| quote! {});
82        let finalize_impl = s.bound_impl(
83            quote!(::jrsonnet_gc::Finalize),
84            quote!(
85                fn finalize(&self) {
86                    let _trivially_drop = |t: Self| match t { #trivially_drop_body };
87                }
88            ),
89        );
90
91        quote! {
92            #trace_impl
93            #finalize_impl
94        }
95    }
96}
97
98decl_derive!([Finalize] => derive_finalize);
99
100fn derive_finalize(s: Structure<'_>) -> proc_macro2::TokenStream {
101    s.unbound_impl(quote!(::jrsonnet_gc::Finalize), quote!())
102}