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
103
104
105
106
107
108
109
110
111
112
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use synstructure::{decl_derive, AddBounds};
fn collect_derive(s: synstructure::Structure) -> TokenStream {
#[derive(PartialEq)]
enum Mode {
RequireStatic,
NoDrop,
UnsafeDrop,
}
let mut mode = None;
for attr in &s.ast().attrs {
match attr.parse_meta() {
Ok(syn::Meta::List(syn::MetaList { path, nested, .. })) => {
if path.is_ident("collect") {
if let Some(prev_mode) = mode {
let prev_mode_str = match prev_mode {
Mode::RequireStatic => "require_static",
Mode::NoDrop => "no_drop",
Mode::UnsafeDrop => "unsafe_drop",
};
panic!("`Collect` mode was already specified with `#[collect({})]`, cannot specify twice", prev_mode_str);
}
if let Some(syn::NestedMeta::Meta(syn::Meta::Path(path))) = nested.first() {
if path.is_ident("require_static") {
mode = Some(Mode::RequireStatic);
} else if path.is_ident("no_drop") {
mode = Some(Mode::NoDrop);
} else if path.is_ident("unsafe_drop") {
mode = Some(Mode::UnsafeDrop);
} else {
panic!("`#[collect]` requires one of: \"require_static\", \"no_drop\", or \"unsafe_drop\" as an argument");
}
}
}
}
_ => {}
}
}
let mode = mode.expect("deriving `Collect` requires a `#[collect(<mode>)]` attribute, where `<mode>` is one of \"require_static\", \"no_drop\", or \"unsafe_drop\"");
let where_clause = if mode == Mode::RequireStatic {
quote!(where Self: 'static)
} else {
quote!()
};
let collect_impl = if mode == Mode::RequireStatic {
s.clone().add_bounds(AddBounds::None).gen_impl(quote! {
gen unsafe impl gc_arena::Collect for @Self #where_clause {
#[inline]
fn needs_trace() -> bool {
false
}
}
})
} else {
let mut needs_trace_body = TokenStream::new();
quote!(false).to_tokens(&mut needs_trace_body);
for v in s.variants() {
for b in v.bindings() {
let ty = &b.ast().ty;
quote!(|| <#ty as gc_arena::Collect>::needs_trace())
.to_tokens(&mut needs_trace_body);
}
}
let trace_body = s.each(|bi| quote!(gc_arena::Collect::trace(#bi, cc)));
s.clone().add_bounds(AddBounds::Fields).gen_impl(quote! {
gen unsafe impl gc_arena::Collect for @Self #where_clause {
#[inline]
fn needs_trace() -> bool {
#needs_trace_body
}
#[inline]
fn trace(&self, cc: ::gc_arena::CollectionContext) {
match *self { #trace_body }
}
}
})
};
let drop_impl = if mode == Mode::NoDrop {
let mut s = s;
s.add_bounds(AddBounds::None).gen_impl(quote! {
gen impl gc_arena::MustNotImplDrop for @Self {}
})
} else {
quote!()
};
quote! {
#collect_impl
#drop_impl
}
}
decl_derive!([Collect, attributes(collect)] => collect_derive);