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 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 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 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 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 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 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 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 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 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 let _ = tracer;
445 }
446 }
447 };
448
449 quote! {
450 #storage_enum
451 #into_heap
452 }
453}