traitor_derive/
lib.rs

1#![recursion_limit = "128"]
2extern crate proc_macro;
3
4use quote::quote;
5use syn::spanned::Spanned;
6use syn::visit_mut::visit_type_mut;
7use syn::{
8    parse_macro_input, parse_quote, FnArg, Ident, ItemTrait, Lifetime, ReturnType, TraitItem,
9    TraitItemMethod, Type,
10};
11
12type Error = syn::parse::Error;
13
14#[proc_macro_attribute]
15pub fn shadow(
16    _args: proc_macro::TokenStream,
17    item_tokens: proc_macro::TokenStream,
18) -> proc_macro::TokenStream {
19    let item_tokens_clone = item_tokens.clone();
20    let item_tokens = proc_macro2::TokenStream::from(item_tokens);
21    let shadow = parse_macro_input!(item_tokens_clone as ItemTrait);
22
23    let shadow_tokens = match generate_shadow(shadow) {
24        Err(err) => err.to_compile_error().into(),
25        Ok(tokens) => tokens,
26    };
27
28    let output = quote! {
29      #item_tokens
30
31      #shadow_tokens
32    };
33    output.into()
34}
35
36fn generate_shadow(mut shadow_trait: ItemTrait) -> Result<proc_macro2::TokenStream, Error> {
37    let target_ident = shadow_trait.ident.clone();
38    let shadow_ident = Ident::new(&format!("{}Shadow", target_ident), target_ident.span());
39    let details_ident = Ident::new(&format!("{}ShadowInfo", target_ident), target_ident.span());
40    let bind_box_ident = Ident::new(&format!("bind_{}_box", target_ident), target_ident.span());
41    let visibility = shadow_trait.vis.clone();
42
43    shadow_trait.ident = shadow_ident.clone();
44
45    // Mark as unsafe! our "shadows" are sketchy and only work for limited cases.
46    shadow_trait.unsafety = Some(syn::token::Unsafe {
47        span: shadow_trait.ident.span(),
48    });
49
50    if !shadow_trait.supertraits.is_empty() {
51        return Err(Error::new(
52            shadow_trait.supertraits.span(),
53            "supertraits are not supported yet!",
54        ));
55    }
56
57    let mut funcs = Vec::new();
58    for item in &mut shadow_trait.items {
59        if let TraitItem::Method(ref mut method) = item {
60            let (lifetime, mutability) = self_lifetime(method)?.clone();
61            // Replace the first argument
62            let first_mut = method.sig.decl.inputs.iter_mut().next().unwrap();
63
64            *first_mut = parse_quote! { data: &#lifetime #mutability Self::Data };
65
66            // Collect function arguments we care about
67            let mut arg_types = Vec::new();
68            for arg in &method.sig.decl.inputs {
69                if let FnArg::Captured(arg) = arg {
70                    let mut ty = arg.ty.clone();
71                    visit_type_mut(&mut EraseLifetimes {}, &mut ty);
72                    arg_types.push(ty);
73                } else {
74                    return Err(Error::new(arg.span(), "argument not supported"));
75                }
76            }
77
78            // Generate layout info for the function
79            let mut ret_type: Type = match method.sig.decl.output {
80                ReturnType::Default => parse_quote! { () },
81                ReturnType::Type(_, ref ty) => ty.as_ref().clone(),
82            };
83            visit_type_mut(&mut EraseLifetimes {}, &mut ret_type);
84
85            let func_ident = &method.sig.ident;
86            funcs.push(quote! {
87              traitor::details::ShadowLayout {
88                ret_slots: ret_slots::<#ret_type>(),
89                arg_slots: #(slots::<#arg_types>())+*,
90                func_ptr: <S as #shadow_ident>::#func_ident as *const u8,
91              }
92            });
93
94            // Append &Self as a last argument -- this is our extended metadata. Use the same lifetime as
95            // the original `&self` parameter, so we can borrow from the metadata (we guarantee that
96            // metadata lives longer than any borrow of the data itself).
97            method
98                .sig
99                .decl
100                .inputs
101                .push(parse_quote! { meta: &#lifetime Self });
102        }
103    }
104
105    // Append `Data` associated type
106    // FIXME: bounds for supertraits: type Data: Sized + std::fmt::Debug + ...;
107    shadow_trait.items.push(parse_quote! {
108        type Data: Sized;
109    });
110
111    let details_struct = quote! {
112        #visibility struct #details_ident<'meta, S>
113            where
114                S: #shadow_ident + 'meta,
115        {
116            inner: S,
117            _phantom: std::marker::PhantomData<&'meta S>,
118        }
119    };
120
121    let details_impl = quote! {
122        impl<'meta, S> #details_ident<'meta, S>
123        where
124            S: #shadow_ident + 'meta,
125        {
126            pub fn new(meta: S) -> Self {
127                Self {
128                    inner: meta,
129                    _phantom: Default::default(),
130                }
131            }
132        }
133    };
134
135    // FIXME: we should handle floats / doubles!
136    let details_binding = quote! {
137        impl<'meta, S> traitor::details::InternalBindingInfo for #details_ident<'meta, S>
138          where
139            S: #shadow_ident + 'meta,
140        {
141            type Data = <S as #shadow_ident>::Data;
142            type Shadow = S;
143            type Target = #target_ident;
144
145            fn layout() -> traitor::details::LayoutInfo {
146                const US: usize = std::mem::size_of::<usize>();
147                fn slots<T>() -> usize {
148                    match (std::mem::size_of::<T>() + US - 1) / US {
149                        v if v <= 2 => v,
150                        // uses pointer instead which is one slot
151                        _ => 1,
152                    }
153                }
154                fn ret_slots<T>() -> usize {
155                    match (std::mem::size_of::<T>() + US - 1) / US {
156                        // uses pointer to return value, pointer is passed by caller
157                        v if v > 2 => 1,
158                        // uses registers, no slots are taken
159                        _ => 0,
160                    }
161                }
162                traitor::details::LayoutInfo {
163                    shadow: vec![
164                        #(#funcs),*
165                    ],
166                  other: vec![
167                //            <traitor::details::VTableInfo as traitor::details::VTableInfoTrait<
168                //              Self::Data,
169                //              std::fmt::Debug,
170                //            >>::VTABLE_FUNCS,
171                  ],
172                }
173            }
174
175            fn into_shadow(self) -> S {
176                self.inner
177            }
178        }
179
180    };
181
182    let bind_box = quote! {
183        pub fn #bind_box_ident<'b, D>(
184            data: Box<D>,
185            binder: &'b traitor::Binder<D, #target_ident>
186        ) -> Box<#target_ident + 'b> {
187            unsafe {
188                let raw = Box::into_raw(data);
189                let bound = binder.bind_mut(std::mem::transmute::<*mut D, &mut D>(raw));
190                Box::from_raw(bound)
191            }
192        }
193    };
194
195    let result = quote! {
196        #shadow_trait
197
198        #details_struct
199        #details_impl
200        #details_binding
201        #bind_box
202    };
203    Ok(result)
204}
205
206fn self_lifetime(method: &TraitItemMethod) -> Result<(Lifetime, Option<syn::token::Mut>), Error> {
207    match method.sig.decl.inputs.first().as_ref().map(|v| v.value()) {
208        Some(FnArg::SelfRef(self_ref)) => {
209            let lifetime = self_ref.lifetime.as_ref().ok_or_else(|| {
210                Error::new(
211                    self_ref.self_token.span,
212                    "&self argument must have an explicit lifetime!",
213                )
214            })?;
215
216            Ok((lifetime.clone(), self_ref.mutability.clone()))
217        }
218        _ => {
219            return Err(Error::new(
220                method.sig.ident.span(),
221                "function must be object-safe and have `&self` as a first argument!",
222            ));
223        }
224    }
225}
226
227struct EraseLifetimes {}
228
229impl syn::visit_mut::VisitMut for EraseLifetimes {
230    fn visit_type_reference_mut(&mut self, tr: &mut syn::TypeReference) {
231        tr.lifetime = None;
232        syn::visit_mut::visit_type_reference_mut(self, tr)
233    }
234
235    fn visit_angle_bracketed_generic_arguments_mut(
236        &mut self,
237        list: &mut syn::AngleBracketedGenericArguments,
238    ) {
239        let old = std::mem::replace(&mut list.args, Default::default());
240        for item in old {
241            if let syn::GenericArgument::Lifetime(_) = item {
242                // drop all lifetimes
243            } else {
244                list.args.push(item);
245            }
246        }
247        syn::visit_mut::visit_angle_bracketed_generic_arguments_mut(self, list)
248    }
249}