1extern crate proc_macro;
2
3use proc_macro2::{Span, TokenStream};
4use quote::quote;
5use syn::{
6 parse_macro_input, parse_quote, AttributeArgs, Data, DataStruct, DeriveInput, Error, Field,
7 Fields, Type, Visibility,
8};
9
10macro_rules! fail {
11 ($ts:expr, $err:expr) => {
12 return Err(Error::new_spanned($ts, $err));
13 };
14}
15
16#[derive(Debug, Eq, PartialEq, Copy, Clone)]
17enum RcKind {
18 Atomic,
19 Local,
20}
21
22#[derive(Debug, Eq, PartialEq, Copy, Clone)]
23enum WeakKind {
24 NonWeak,
25 Weak,
26}
27
28#[derive(Debug, Eq, PartialEq, Copy, Clone)]
29enum FinalizeKind {
30 Finalize,
31 Pod,
32}
33
34#[derive(Debug)]
35struct Config {
36 rc_kind: RcKind,
37 weak_kind: WeakKind,
38 finalize_kind: FinalizeKind,
39}
40
41fn parse_config(args: AttributeArgs) -> Result<Config, Error> {
42 let mut rc_kind: Option<RcKind> = None;
43 let mut weak_kind = WeakKind::NonWeak;
44 let mut finalize_kind = FinalizeKind::Pod;
45
46 for arg in args {
47 use syn::{Meta::*, NestedMeta::*};
48 match arg {
49 Meta(Path(path)) if path.is_ident("atomic") => {
50 if rc_kind.is_some() {
51 fail!(path, "duplicate atomicity argument");
52 }
53 rc_kind = Some(RcKind::Atomic);
54 }
55 Meta(Path(path)) if path.is_ident("local") => {
56 if rc_kind.is_some() {
57 fail!(path, "duplicate atomicity argument");
58 }
59 rc_kind = Some(RcKind::Local);
60 }
61 Meta(Path(path)) if path.is_ident("weak") => {
62 if weak_kind == WeakKind::Weak {
63 fail!(path, "duplicate weak argument");
64 }
65 weak_kind = WeakKind::Weak;
66 }
67 Meta(Path(path)) if path.is_ident("finalize") => {
68 if finalize_kind == FinalizeKind::Finalize {
69 fail!(path, "duplicate finalize argument");
70 }
71 finalize_kind = FinalizeKind::Finalize;
72 }
73 meta => fail!(meta, "unexpected refcounted argument"),
74 }
75 }
76
77 if let Some(rc_kind) = rc_kind {
78 Ok(Config {
79 rc_kind,
80 weak_kind,
81 finalize_kind,
82 })
83 } else {
84 return Err(Error::new(
85 Span::call_site(),
86 "must specify either `local` or `atomic` atomicity",
87 ));
88 }
89}
90
91fn refcounted_impl(args: AttributeArgs, mut item: DeriveInput) -> Result<TokenStream, Error> {
92 let cfg = parse_config(args)?;
93
94 use {FinalizeKind::*, RcKind::*, WeakKind::*};
95 let rc_ty = match (cfg.rc_kind, cfg.weak_kind, cfg.finalize_kind) {
96 (Local, NonWeak, Pod) => quote!(Local),
97 (Atomic, NonWeak, Pod) => quote!(Atomic),
98 (Local, Weak, Pod) => quote!(LocalWeak),
99 (Atomic, Weak, Pod) => quote!(AtomicWeak),
100 (Local, NonWeak, Finalize) => quote!(LocalFinalize),
101 (Atomic, NonWeak, Finalize) => quote!(AtomicFinalize),
102 (Local, Weak, Finalize) => quote!(LocalWeakFinalize),
103 (Atomic, Weak, Finalize) => quote!(AtomicWeakFinalize),
104 };
105
106 match &mut item.data {
108 Data::Struct(DataStruct {
109 fields: Fields::Named(fields),
110 ..
111 }) => {
112 let ty: Type = match cfg.rc_kind {
113 RcKind::Atomic => parse_quote!(::refptr::__rt::PhantomAtomicRefcnt<Self>),
114 RcKind::Local => parse_quote!(::refptr::__rt::PhantomLocalRefcnt<Self>),
115 };
116
117 let rc_field = Field {
118 attrs: Vec::new(),
119 vis: Visibility::Inherited,
120 ident: parse_quote!(_refcnt_marker),
121 colon_token: Default::default(),
122 ty,
123 };
124 fields.named.insert(0, rc_field);
125 }
126 _ => fail!(
127 item,
128 "refcounted must be used on a struct with named fields"
129 ),
130 }
131
132 let ident = &item.ident;
133 let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl();
134
135 let metadata_func = match cfg.finalize_kind {
136 Pod => quote! {
137 unsafe fn refcount_metadata(&self) { }
138 },
139 Finalize => quote! {
140 unsafe fn refcount_metadata(&self) -> unsafe fn (*const u8) {
141 unsafe fn finalize_helper #impl_generics (this: *const u8) #where_clause {
142 let _assert_finalize_type: fn (&#ident #ty_generics) =
143 <#ident #ty_generics>::finalize;
144 <#ident #ty_generics>::finalize(&*(this as *const #ident #ty_generics));
145 }
146 finalize_helper #ty_generics
147 }
148 },
149 };
150
151 let impl_refcounted = quote! {
152 unsafe impl #impl_generics ::refptr::Refcounted for #ident #ty_generics #where_clause {
153 type Rc = ::refptr::refcnt::#rc_ty;
154
155 #metadata_func
156 }
157 };
158
159 let impl_drop = quote! {
162 impl #impl_generics Drop for #ident #ty_generics #where_clause {
163 fn drop(&mut self) { }
164 }
165 };
166
167 Ok(quote! {
168 #item
169 #impl_refcounted
170 #impl_drop
171 })
172}
173
174#[proc_macro_attribute]
175pub fn refcounted(
176 args: proc_macro::TokenStream,
177 input: proc_macro::TokenStream,
178) -> proc_macro::TokenStream {
179 let args = parse_macro_input!(args as AttributeArgs);
180 let input = parse_macro_input!(input as DeriveInput);
181
182 match refcounted_impl(args, input) {
183 Ok(ts) => ts.into(),
184 Err(e) => e.to_compile_error().into(),
185 }
186}