1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
use quote::quote;
use synstructure::{decl_derive, AddBounds, BindStyle, Structure};

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
            .iter()
            .any(|attr| attr.path.is_ident("unsafe_ignore_trace"))
    });
    let trace_body = s.each(|bi| quote!(mark(#bi)));

    s.add_bounds(AddBounds::Fields);
    let trace_impl = s.unsafe_bound_impl(
        quote!(::jrsonnet_gc::Trace),
        quote! {
            #[inline] unsafe fn trace(&self) {
                #[allow(dead_code)]
                #[inline]
                unsafe fn mark<T: ::jrsonnet_gc::Trace + ?Sized>(it: &T) {
                    ::jrsonnet_gc::Trace::trace(it);
                }
                match *self { #trace_body }
            }
            #[inline] unsafe fn root(&self) {
                #[allow(dead_code)]
                #[inline]
                unsafe fn mark<T: ::jrsonnet_gc::Trace + ?Sized>(it: &T) {
                    ::jrsonnet_gc::Trace::root(it);
                }
                match *self { #trace_body }
            }
            #[inline] unsafe fn unroot(&self) {
                #[allow(dead_code)]
                #[inline]
                unsafe fn mark<T: ::jrsonnet_gc::Trace + ?Sized>(it: &T) {
                    ::jrsonnet_gc::Trace::unroot(it);
                }
                match *self { #trace_body }
            }
            #[inline] fn finalize_glue(&self) {
                ::jrsonnet_gc::Finalize::finalize(self);
                #[allow(dead_code)]
                #[inline]
                fn mark<T: ::jrsonnet_gc::Trace + ?Sized>(it: &T) {
                    ::jrsonnet_gc::Trace::finalize_glue(it);
                }
                match *self { #trace_body }
            }
        },
    );

    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 ::jrsonnet_gc::finalizer_safe() {
                        ::jrsonnet_gc::Finalize::finalize(self);
                    }
                }
            },
        );

        quote! {
            #trace_impl
            #drop_impl
        }
    } else {
        s.bind_with(|_| BindStyle::Move);
        let trivially_drop_body = s.each(|_| quote! {});
        let finalize_impl = s.bound_impl(
            quote!(::jrsonnet_gc::Finalize),
            quote!(
                fn finalize(&self) {
                    let _trivially_drop = |t: Self| match t { #trivially_drop_body };
                }
            ),
        );

        quote! {
            #trace_impl
            #finalize_impl
        }
    }
}

decl_derive!([Finalize] => derive_finalize);

fn derive_finalize(s: Structure<'_>) -> proc_macro2::TokenStream {
    s.unbound_impl(quote!(::jrsonnet_gc::Finalize), quote!())
}