cell_gc_derive/
lib.rs

1#![recursion_limit = "1000"]
2
3extern crate proc_macro;
4extern crate syn;
5#[macro_use]
6extern crate quote;
7
8use proc_macro::TokenStream;
9use syn::Ident;
10use quote::Tokens;
11
12#[proc_macro_derive(IntoHeap)]
13pub fn derive_into_heap(input: TokenStream) -> TokenStream {
14    let source = input.to_string();
15    let ast = syn::parse_derive_input(&source).unwrap();
16    let expanded = impl_into_heap(&ast);
17    println!("{:?}", expanded);
18    expanded.parse().unwrap()
19}
20
21fn impl_into_heap(ast: &syn::DeriveInput) -> Tokens {
22    match ast.body {
23        syn::Body::Struct(ref data) => impl_into_heap_for_struct(ast, data),
24        syn::Body::Enum(ref variants) => impl_into_heap_for_enum(ast, variants),
25    }
26}
27
28fn impl_into_heap_for_struct(ast: &syn::DeriveInput, data: &syn::VariantData) -> Tokens {
29    let name = &ast.ident;
30    let name_str: &str = name.as_ref();
31    let storage_type_name: Ident = Ident::from(name_str.to_string() + "Storage");
32    let vis = &ast.vis;
33    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
34    let heap_lifetime = &ast.generics
35        .lifetimes
36        .first()
37        .expect("lifetime parameter required")
38        .lifetime;
39
40    match *data {
41        syn::VariantData::Struct(ref fields) => {
42            let field_vis: &Vec<_> = &fields.iter().map(|f| &f.vis).collect();
43            let field_names: &Vec<_> = &fields.iter().map(|f| &f.ident).collect();
44            let field_types: &Vec<_> = &fields.iter().map(|f| &f.ty).collect();
45            let field_storage_types: Vec<_> = fields
46                .iter()
47                .map(|f| {
48                    let field_ty = &f.ty;
49                    quote! {
50                    <#field_ty as ::cell_gc::traits::IntoHeap<#heap_lifetime>>::In
51                }
52                })
53                .collect();
54
55            // 1. The in-heap representation of the struct.
56            let storage_struct = quote! {
57                #vis struct #storage_type_name #impl_generics #where_clause {
58                    #( #field_vis #field_names: #field_storage_types ),*
59                }
60            };
61
62            // 2. IntoHeap implementation.
63            // Body of the trace() method.
64            let trace_fields: Vec<Tokens> = fields
65                .iter()
66                .map(|f| {
67                    let name = &f.ident;
68                    let ty = &f.ty;
69                    quote! {
70                    <#ty as ::cell_gc::traits::IntoHeap<#heap_lifetime>>
71                        ::trace(&storage.#name, tracer);
72                }
73                })
74                .collect();
75
76            // Oddly you can't use the same identifier more than once in the
77            // same loop. So create an alias.
78            let field_names_1 = field_names;
79
80            let into_heap = quote! {
81                unsafe impl #impl_generics ::cell_gc::traits::IntoHeap<#heap_lifetime>
82                    for #name #ty_generics
83                    #where_clause
84                {
85                    type In = #storage_type_name #ty_generics;
86
87                    fn into_heap(self) -> Self::In {
88                        #storage_type_name {
89                            #(
90                                #field_names:
91                                    ::cell_gc::traits::IntoHeap::into_heap(
92                                        self.#field_names_1)
93                            ),*
94                        }
95                    }
96
97                    unsafe fn trace<R>(storage: &Self::In, tracer: &mut R)
98                        where R: ::cell_gc::traits::Tracer
99                    {
100                        #( #trace_fields )*
101
102                        // Quiet unused variable warnings when `$(...)*` expands
103                        // to nothing.
104                        let _ = tracer;
105                    }
106
107                    unsafe fn from_heap(storage: &Self::In) -> Self {
108                        #name {
109                            #(
110                                #field_names:
111                                    ::cell_gc::traits::IntoHeap::from_heap(
112                                        &storage.#field_names_1)
113                            ),*
114                        }
115                    }
116                }
117            };
118
119            // 3. IntoHeapAllocation implementation.
120            let ref_type_name: Ident = Ident::from(name_str.to_string() + "Ref");
121            let into_heap_allocation = quote! {
122                impl #impl_generics ::cell_gc::traits::IntoHeapAllocation<#heap_lifetime>
123                    for #name #ty_generics
124                    #where_clause
125                {
126                    type Ref = #ref_type_name #ty_generics;
127
128                    fn wrap_gcref(gcref: ::cell_gc::GcRef<#heap_lifetime,
129                                                          #name #ty_generics>)
130                        -> #ref_type_name #ty_generics
131                    {
132                        #ref_type_name(gcref)
133                    }
134                }
135            };
136
137            // 4. #ref_type_name: A safe reference to the struct
138            let ref_type = quote! {
139                #[derive(Clone, Debug, PartialEq, Eq)]
140                #vis struct #ref_type_name #impl_generics
141                    (::cell_gc::GcRef<#heap_lifetime, #name #ty_generics>)
142                    #where_clause;
143            };
144
145            // 5. The ref type also gets an IntoHeap impl...
146            let ref_type_into_heap = quote! {
147                unsafe impl #impl_generics ::cell_gc::traits::IntoHeap<#heap_lifetime>
148                    for #ref_type_name #ty_generics
149                    #where_clause
150                {
151                    type In = ::cell_gc::ptr::Pointer<#storage_type_name #ty_generics>;
152
153                    fn into_heap(self) -> Self::In {
154                        self.0.ptr()
155                    }
156
157                    unsafe fn trace<R>(storage: &Self::In, tracer: &mut R)
158                        where R: ::cell_gc::traits::Tracer
159                    {
160                        if !storage.is_null() {
161                            tracer.visit::<#name #ty_generics>(*storage);
162                        }
163                    }
164
165                    unsafe fn from_heap(storage: &Self::In) -> #ref_type_name #ty_generics {
166                        #ref_type_name(::cell_gc::GcRef::<#name #ty_generics>::new(*storage))
167                    }
168                }
169            };
170
171            // 6. Getters and setters.
172            let field_setter_names: Vec<_> = fields
173                .iter()
174                .map(|f| {
175                    let field_str: &str = f.ident.as_ref().unwrap().as_ref();
176                    Ident::from(format!("set_{}", field_str))
177                })
178                .collect();
179            let accessors = quote! {
180                impl #impl_generics #ref_type_name #ty_generics #where_clause {
181                    #(
182                        #field_vis fn #field_names(&self) -> #field_types {
183                            let ptr = self.0.as_ptr();
184                            unsafe {
185                                ::cell_gc::traits::IntoHeap::from_heap(
186                                    &(*ptr).#field_names_1)
187                            }
188                        }
189                    )*
190
191                    #(
192                        #field_vis fn #field_setter_names(&self, v: #field_types) {
193                            let ptr = self.0.as_mut_ptr();
194                            let u = ::cell_gc::traits::IntoHeap::into_heap(v);
195                            unsafe {
196                                (*ptr).#field_names = u;
197                            }
198                        }
199                    )*
200
201                    ///// Get all fields at once.
202                    //pub fn get(&self) -> #name {
203                    //    ::cell_gc::traits::IntoHeap::from_heap(ptr)
204                    //}
205
206                    pub fn as_mut_ptr(&self) -> *mut #storage_type_name #ty_generics {
207                        self.0.as_mut_ptr()
208                    }
209                }
210            };
211
212            quote! {
213                #storage_struct
214                #into_heap
215                #into_heap_allocation
216                #ref_type
217                #ref_type_into_heap
218                #accessors
219            }
220        }
221        syn::VariantData::Tuple(ref _fields) => {
222            panic!("#[derive(IntoHeap)] does not support tuple structs");
223        }
224        syn::VariantData::Unit => {
225            panic!("#[derive(IntoHeap)] does not support unit structs");
226        }
227    }
228}
229
230fn impl_into_heap_for_enum(ast: &syn::DeriveInput, variants: &[syn::Variant]) -> Tokens {
231    let attrs = &ast.attrs;
232    let name = &ast.ident;
233    let name_str: &str = name.as_ref();
234    let storage_type_name: Ident = Ident::from(name_str.to_string() + "Storage");
235    let vis = &ast.vis;
236    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
237    let heap_lifetime = &ast.generics
238        .lifetimes
239        .first()
240        .expect("lifetime parameter required")
241        .lifetime;
242
243    let variant_storage = variants.iter().map(|v| {
244        let attrs = &v.attrs;
245        let ident = &v.ident;
246        if v.discriminant.is_some() {
247            panic!("#[derive(IntoHeap)] does not support enum variants with discriminants");
248        }
249        match v.data {
250            syn::VariantData::Struct(ref fields) => {
251                let field_decls = fields.iter().map(|f| {
252                    let field_attrs = &f.attrs;
253                    let field_name = &f.ident;
254                    let field_ty = &f.ty;
255                    quote! {
256                        #( #field_attrs )*
257                        #field_name:
258                            <#field_ty as ::cell_gc::traits::IntoHeap<#heap_lifetime>>::In
259                    }
260                });
261                quote! {
262                    #( #attrs )*
263                    #ident { #( #field_decls ),* }
264                }
265            }
266            syn::VariantData::Tuple(ref fields) => {
267                let field_decls = fields.iter().map(|f| {
268                    let field_ty = &f.ty;
269                    let field_attrs = &f.attrs;
270                    quote! {
271                        #( #field_attrs )*
272                        <#field_ty as ::cell_gc::traits::IntoHeap<#heap_lifetime>>::In
273                    }
274                });
275                quote! {
276                    #( #attrs )*
277                    #ident( #( #field_decls ),* )
278                }
279            }
280            syn::VariantData::Unit => {
281                quote! { #( #attrs )* #ident }
282            }
283        }
284    });
285    let storage_enum = quote! {
286        #( #attrs )*
287        #vis enum #storage_type_name #impl_generics
288            #where_clause
289        {
290            #( #variant_storage ),*
291        }
292    };
293
294    let into_heap_arms = variants.iter().map(|v| {
295        let ident = &v.ident;
296        match v.data {
297            syn::VariantData::Struct(ref fields) => {
298                let field_names: &Vec<_> = &fields.iter().map(|f| &f.ident).collect();
299                let field_names_1 = field_names;
300                let field_types = fields.iter().map(|f| &f.ty);
301                quote! {
302                    #name::#ident { #(#field_names),* } => {
303                        #storage_type_name::#ident {
304                            #(
305                                #field_names:
306                                    <#field_types as ::cell_gc::traits::IntoHeap>
307                                    ::into_heap(#field_names_1)
308                            ),*
309                        }
310                    }
311                }
312            }
313            syn::VariantData::Tuple(ref fields) => {
314                let field_types = fields.iter().map(|f| &f.ty);
315                let bindings: &Vec<Ident> = &(0..fields.len())
316                    .map(|n| Ident::from(format!("x{}", n)))
317                    .collect();
318                quote! {
319                    #name::#ident( #(#bindings),* ) => {
320                        #storage_type_name::#ident(
321                            #(
322                                <#field_types as ::cell_gc::traits::IntoHeap>
323                                    ::into_heap(#bindings)
324                            ),*
325                        )
326                    }
327                }
328            }
329            syn::VariantData::Unit => {
330                quote! {
331                    #name::#ident => #storage_type_name::#ident
332                }
333            }
334        }
335    });
336
337    let from_heap_arms = variants.iter().map(|v| {
338        let ident = &v.ident;
339        match v.data {
340            syn::VariantData::Struct(ref fields) => {
341                let field_names: &Vec<_> = &fields.iter().map(|f| &f.ident).collect();
342                let field_names_1 = field_names;
343                let field_types = fields.iter().map(|f| &f.ty);
344                quote! {
345                    #storage_type_name::#ident { #(ref #field_names),* } => {
346                        #name::#ident {
347                            #(
348                                #field_names:
349                                    <#field_types as ::cell_gc::traits::IntoHeap>
350                                    ::from_heap(#field_names_1)
351                            ),*
352                        }
353                    }
354                }
355            }
356            syn::VariantData::Tuple(ref fields) => {
357                let field_types = fields.iter().map(|f| &f.ty);
358                let bindings: &Vec<Ident> = &(0..fields.len())
359                    .map(|n| Ident::from(format!("x{}", n)))
360                    .collect();
361                quote! {
362                    #storage_type_name::#ident( #(ref #bindings),* ) => {
363                        #name::#ident(
364                            #(
365                                <#field_types as ::cell_gc::traits::IntoHeap>
366                                    ::from_heap(#bindings)
367                            ),*
368                        )
369                    }
370                }
371            }
372            syn::VariantData::Unit => {
373                quote! {
374                    #storage_type_name::#ident => #name::#ident
375                }
376            }
377        }
378    });
379
380    let trace_arms = variants.iter().map(|v| {
381        let ident = &v.ident;
382        match v.data {
383            syn::VariantData::Struct(ref fields) => {
384                let field_names: &Vec<_> = &fields.iter().map(|f| &f.ident).collect();
385                let field_types = fields.iter().map(|f| &f.ty);
386                quote! {
387                    #storage_type_name::#ident { #(ref #field_names),* } => {
388                        #(
389                            <#field_types as ::cell_gc::traits::IntoHeap>
390                                ::trace(#field_names, tracer);
391                        )*
392                    }
393                }
394            }
395            syn::VariantData::Tuple(ref fields) => {
396                let field_types = fields.iter().map(|f| &f.ty);
397                let bindings: &Vec<Ident> = &(0..fields.len())
398                    .map(|n| Ident::from(format!("x{}", n)))
399                    .collect();
400                quote! {
401                    #storage_type_name::#ident( #(ref #bindings),* ) => {
402                        #(
403                            <#field_types as ::cell_gc::traits::IntoHeap>
404                                ::trace(#bindings, tracer);
405                        )*
406                    }
407                }
408            }
409            syn::VariantData::Unit => {
410                quote! { #storage_type_name::#ident => () }
411            }
412        }
413    });
414
415    let into_heap = quote! {
416        unsafe impl #impl_generics
417            ::cell_gc::traits::IntoHeap<#heap_lifetime>
418            for #name #ty_generics
419            #where_clause
420        {
421            type In = #storage_type_name #ty_generics;
422
423            fn into_heap(self) -> Self::In {
424                match self {
425                    #( #into_heap_arms ),*
426                }
427            }
428
429            unsafe fn from_heap(storage: &Self::In) -> Self {
430                match *storage {
431                    #( #from_heap_arms ),*
432                }
433            }
434
435            unsafe fn trace<R>(storage: &Self::In, tracer: &mut R)
436                where R: ::cell_gc::traits::Tracer
437            {
438                match *storage {
439                    #( #trace_arms ),*
440                }
441
442                // Quiet unused variable warnings when `$(...)*` expands to
443                // nothing.
444                let _ = tracer;
445            }
446        }
447    };
448
449    quote! {
450        #storage_enum
451        #into_heap
452    }
453}