1extern crate proc_macro;
11use proc_macro::TokenStream;
12use quote::quote;
13use syn::parse::Parser;
14use syn::spanned::Spanned;
15use syn::*;
16
17fn match_generic_type(ty: &Type, container: &str, containee: &Ident) -> bool {
19 if let Type::Path(pat) = ty
20 && let Some(seg) = pat.path.segments.last()
21 {
22 if seg.ident != container {
23 return false;
24 }
25 if let PathArguments::AngleBracketed(args) = &seg.arguments
26 && let Some(GenericArgument::Type(Type::Path(arg))) = args.args.last()
27 {
28 return Some(containee) == arg.path.get_ident();
29 }
30 }
31 false
32}
33
34fn is_pin(ty: &Type) -> Option<&Type> {
36 if let Type::Path(pat) = ty
37 && let Some(seg) = pat.path.segments.last()
38 {
39 if seg.ident != "Pin" {
40 return None;
41 }
42 if let PathArguments::AngleBracketed(args) = &seg.arguments
43 && let Some(GenericArgument::Type(t)) = args.args.last()
44 {
45 return Some(t);
46 }
47 }
48 None
49}
50
51#[proc_macro_attribute]
167pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
168 let mut input = parse_macro_input!(item as ItemStruct);
169
170 let fields = if let Fields::Named(fields) = &mut input.fields {
171 fields
172 } else {
173 return Error::new(
174 proc_macro2::Span::call_site(),
175 "Only supported for structure with named fields",
176 )
177 .to_compile_error()
178 .into();
179 };
180
181 let vtable_name = input.ident.to_string();
182 if !vtable_name.ends_with("VTable") {
183 return Error::new(input.ident.span(), "The structure does not ends in 'VTable'")
184 .to_compile_error()
185 .into();
186 }
187
188 let trait_name = Ident::new(&vtable_name[..vtable_name.len() - 6], input.ident.span());
189 let to_name = quote::format_ident!("{}TO", trait_name);
190 let module_name = quote::format_ident!("{}_vtable_mod", trait_name);
191 let static_vtable_macro_name = quote::format_ident!("{}_static", vtable_name);
192
193 let vtable_name = input.ident.clone();
194
195 let mut drop_impls = Vec::new();
196
197 let mut generated_trait = ItemTrait {
198 attrs: input
199 .attrs
200 .iter()
201 .filter(|a| a.path().get_ident().as_ref().map(|i| *i == "doc").unwrap_or(false))
202 .cloned()
203 .collect(),
204 vis: Visibility::Public(Default::default()),
205 unsafety: None,
206 auto_token: None,
207 trait_token: Default::default(),
208 ident: trait_name.clone(),
209 generics: Generics::default(),
210 colon_token: None,
211 supertraits: Default::default(),
212 brace_token: Default::default(),
213 items: Default::default(),
214 restriction: Default::default(),
215 };
216
217 let additional_doc =
218 format!("\nNote: Was generated from the [`#[vtable]`](vtable) macro on [`{vtable_name}`]");
219 generated_trait
220 .attrs
221 .append(&mut Attribute::parse_outer.parse2(quote!(#[doc = #additional_doc])).unwrap());
222
223 let mut generated_trait_assoc_const = None;
224
225 let mut generated_to_fn_trait = Vec::new();
226 let mut generated_type_assoc_fn = Vec::new();
227 let mut vtable_ctor = Vec::new();
228
229 for field in &mut fields.named {
230 field.vis = Visibility::Public(Default::default());
232
233 let ident = field.ident.as_ref().unwrap();
234 let mut some = None;
235
236 let func_ty = if let Type::BareFn(f) = &mut field.ty {
237 Some(f)
238 } else if let Type::Path(pat) = &mut field.ty {
239 pat.path.segments.last_mut().and_then(|seg| {
240 if seg.ident == "Option" {
241 some = Some(quote!(Some));
242 if let PathArguments::AngleBracketed(args) = &mut seg.arguments {
243 if let Some(GenericArgument::Type(Type::BareFn(f))) = args.args.first_mut()
244 {
245 Some(f)
246 } else {
247 None
248 }
249 } else {
250 None
251 }
252 } else {
253 None
254 }
255 })
256 } else {
257 None
258 };
259
260 if let Some(f) = func_ty {
261 let mut sig = Signature {
262 constness: None,
263 asyncness: None,
264 unsafety: f.unsafety,
265 abi: None,
266 fn_token: f.fn_token,
267 ident: ident.clone(),
268 generics: Default::default(),
269 paren_token: f.paren_token,
270 inputs: Default::default(),
271 variadic: None,
272 output: f.output.clone(),
273 };
274
275 let mut sig_extern = sig.clone();
276 sig_extern.generics = parse_str(&format!("<T : {trait_name}>")).unwrap();
277
278 let mut call_code = None;
280 let mut self_call = None;
281 let mut forward_code = None;
282
283 let mut has_self = false;
284
285 for param in &f.inputs {
286 let arg_name = quote::format_ident!("_{}", sig_extern.inputs.len());
287 let typed_arg = FnArg::Typed(PatType {
288 attrs: param.attrs.clone(),
289 pat: Box::new(Pat::Path(syn::PatPath {
290 attrs: Default::default(),
291 qself: None,
292 path: arg_name.clone().into(),
293 })),
294 colon_token: Default::default(),
295 ty: Box::new(param.ty.clone()),
296 });
297 sig_extern.inputs.push(typed_arg.clone());
298
299 if let Type::Ptr(TypePtr { mutability, elem, .. })
301 | Type::Reference(TypeReference { mutability, elem, .. }) = ¶m.ty
302 && let Type::Path(p) = &**elem
303 && let Some(pointer_to) = p.path.get_ident()
304 && pointer_to == &vtable_name
305 {
306 if mutability.is_some() {
307 return Error::new(p.span(), "VTable cannot be mutable")
308 .to_compile_error()
309 .into();
310 }
311 if call_code.is_some() || !sig.inputs.is_empty() {
312 return Error::new(p.span(), "VTable pointer need to be the first")
313 .to_compile_error()
314 .into();
315 }
316 call_code = Some(quote!(vtable as _,));
317 continue;
318 }
319
320 let (is_pin, self_ty) = match is_pin(¶m.ty) {
321 Some(t) => (true, t),
322 None => (false, ¶m.ty),
323 };
324
325 if let (true, mutability) = if match_generic_type(self_ty, "VRef", &vtable_name) {
327 (true, None)
328 } else if match_generic_type(self_ty, "VRefMut", &vtable_name) {
329 (true, Some(Default::default()))
330 } else {
331 (false, None)
332 } {
333 if !sig.inputs.is_empty() {
334 return Error::new(param.span(), "Self pointer need to be the first")
335 .to_compile_error()
336 .into();
337 }
338
339 let const_or_mut = mutability.map_or_else(|| quote!(const), |x| quote!(#x));
340 has_self = true;
341 if !is_pin {
342 sig.inputs.push(FnArg::Receiver(Receiver {
343 attrs: param.attrs.clone(),
344 reference: Some(Default::default()),
345 mutability,
346 self_token: Default::default(),
347 colon_token: None,
348 ty: Box::new(parse_quote!(& #mutability Self)),
349 }));
350 call_code =
351 Some(quote!(#call_code <#self_ty>::from_raw(self.vtable, self.ptr),));
352 self_call =
353 Some(quote!(&#mutability (*(#arg_name.as_ptr() as *#const_or_mut T)),));
354 } else {
355 sig.inputs.push(FnArg::Typed(PatType {
357 attrs: param.attrs.clone(),
358 pat: Box::new(Pat::parse_single.parse2(quote!(self)).unwrap()),
359 colon_token: Default::default(),
360 ty: parse_quote!(core::pin::Pin<& #mutability Self>),
361 }));
362
363 call_code = Some(
364 quote!(#call_code ::core::pin::Pin::new_unchecked(<#self_ty>::from_raw(self.vtable, self.ptr)),),
365 );
366 self_call = Some(
367 quote!(::core::pin::Pin::new_unchecked(&#mutability (*(#arg_name.as_ptr() as *#const_or_mut T))),),
368 );
369 }
370 continue;
371 }
372 sig.inputs.push(typed_arg);
373 call_code = Some(quote!(#call_code #arg_name,));
374 forward_code = Some(quote!(#forward_code #arg_name,));
375 }
376
377 f.unsafety = Some(Default::default());
379
380 sig_extern.abi.clone_from(&f.abi);
381
382 let mut wrap_trait_call = None;
383 if !has_self {
384 sig.generics = Generics {
385 where_clause: Some(parse_str("where Self : Sized").unwrap()),
386 ..Default::default()
387 };
388
389 if let ReturnType::Type(_, ret) = &f.output
391 && match_generic_type(ret, "VBox", &vtable_name)
392 {
393 sig.output = parse_str("-> Self").unwrap();
395 wrap_trait_call = Some(quote! {
396 let wrap_trait_call = |x| unsafe {
397 let ptr = ::core::ptr::NonNull::from(Box::leak(Box::new(x)));
399 VBox::<#vtable_name>::from_raw(vtable, ptr.cast())
400 };
401 wrap_trait_call
402 });
403 }
404 }
405
406 if ident == "drop" {
407 vtable_ctor.push(quote!(#ident: {
408 #sig_extern {
409 unsafe {
410 ::core::mem::drop(Box::from_raw((#self_call).0 as *mut _));
411 }
412 }
413 #ident::<T>
414 },));
415
416 drop_impls.push(quote! {
417 unsafe impl VTableMetaDrop for #vtable_name {
418 unsafe fn drop(ptr: *mut #to_name) {
419 unsafe {
422 let (vtable, ptr) = ((*ptr).vtable, (*ptr).ptr);
423 (vtable.as_ref().#ident)(VRefMut::from_raw(vtable, ptr)) }
424 }
425 fn new_box<X: HasStaticVTable<#vtable_name>>(value: X) -> VBox<#vtable_name> {
426 let ptr = ::core::ptr::NonNull::from(Box::leak(Box::new(value)));
428 unsafe { VBox::from_raw(core::ptr::NonNull::from(X::static_vtable()), ptr.cast()) }
429 }
430 }
431 });
432 continue;
433 }
434
435 if ident == "drop_in_place" {
436 vtable_ctor.push(quote!(#ident: {
437 #[allow(unsafe_code)]
438 #sig_extern {
439 #[allow(unused_unsafe)]
440 unsafe { ::core::ptr::drop_in_place((#self_call).0 as *mut T) };
441 ::core::alloc::Layout::new::<T>().into()
442 }
443 #ident::<T>
444 },));
445
446 drop_impls.push(quote! {
447 #[allow(unsafe_code)]
448 unsafe impl VTableMetaDropInPlace for #vtable_name {
449 unsafe fn #ident(vtable: &Self::VTable, ptr: *mut u8) -> vtable::Layout {
450 (vtable.#ident)(VRefMut::from_raw(core::ptr::NonNull::from(vtable), ::core::ptr::NonNull::new_unchecked(ptr).cast()))
452 }
453 unsafe fn dealloc(vtable: &Self::VTable, ptr: *mut u8, layout: vtable::Layout) {
454 (vtable.dealloc)(vtable, ptr, layout)
455 }
456 }
457 });
458 continue;
459 }
460 if ident == "dealloc" {
461 let abi = &sig_extern.abi;
462 vtable_ctor.push(quote!(#ident: {
463 #[allow(unsafe_code)]
464 unsafe #abi fn #ident(_: &#vtable_name, ptr: *mut u8, layout: vtable::Layout) {
465 use ::core::convert::TryInto;
466 unsafe { vtable::internal::dealloc(ptr, layout.try_into().unwrap()) }
467 }
468 #ident
469 },));
470 continue;
471 }
472
473 generated_trait.items.push(TraitItem::Fn(TraitItemFn {
474 attrs: field.attrs.clone(),
475 sig: sig.clone(),
476 default: None,
477 semi_token: Some(Default::default()),
478 }));
479
480 generated_to_fn_trait.push(ImplItemFn {
481 attrs: field.attrs.clone(),
482 vis: Visibility::Public(Default::default()),
483 defaultness: None,
484 sig: sig.clone(),
485 block: if has_self {
486 parse_quote!({
487 #[allow(unsafe_code)]
489 unsafe {
490 let vtable = self.vtable.as_ref();
491 if let #some(func) = vtable.#ident {
492 func (#call_code)
493 } else {
494 panic!("Called a not-implemented method")
495 }
496 }
497 })
498 } else {
499 parse_quote!({ panic!("Calling Sized method on a Trait Object") })
501 },
502 });
503
504 if !has_self {
505 sig.inputs.insert(
506 0,
507 FnArg::Receiver(Receiver {
508 attrs: Default::default(),
509 reference: Some(Default::default()),
510 mutability: None,
511 self_token: Default::default(),
512 colon_token: None,
513 ty: Box::new(parse_quote!(&Self)),
514 }),
515 );
516 sig.output = sig_extern.output.clone();
517 generated_type_assoc_fn.push(ImplItemFn {
518 attrs: field.attrs.clone(),
519 vis: generated_trait.vis.clone(),
520 defaultness: None,
521 sig,
522 block: parse_quote!({
523 let vtable = self;
524 #[allow(unsafe_code)]
526 unsafe { (self.#ident)(#call_code) }
527 }),
528 });
529
530 vtable_ctor.push(quote!(#ident: {
531 #sig_extern {
532 #[allow(unused)]
534 #[allow(unsafe_code)]
535 let vtable = unsafe { ::core::ptr::NonNull::from(&*_0) };
536 #wrap_trait_call(T::#ident(#self_call #forward_code))
537 }
538 #some(#ident::<T>)
539 },));
540 } else {
541 let erase_return_type_lifetime = match &sig_extern.output {
542 ReturnType::Default => quote!(),
543 ReturnType::Type(_, r) => {
546 quote!(#[allow(clippy::useless_transmute)] ::core::mem::transmute::<#r, #r>)
547 }
548 };
549 vtable_ctor.push(quote!(#ident: {
550 #sig_extern {
551 #[allow(unsafe_code)]
553 unsafe { #erase_return_type_lifetime(T::#ident(#self_call #forward_code)) }
554 }
555 #ident::<T>
556 },));
557 }
558 } else {
559 let generated_trait_assoc_const =
562 generated_trait_assoc_const.get_or_insert_with(|| ItemTrait {
563 attrs: Attribute::parse_outer.parse_str(&format!(
564 "/** Trait containing the associated constant relative to the trait {trait_name}.\n{additional_doc} */",
565 )).unwrap(),
566 ident: quote::format_ident!("{}Consts", trait_name),
567 items: Vec::new(),
568 ..generated_trait.clone()
569 });
570
571 let const_type = if let Some(o) = field
572 .attrs
573 .iter()
574 .position(|a| a.path().get_ident().map(|a| a == "field_offset").unwrap_or(false))
575 {
576 let a = field.attrs.remove(o);
577 let member_type = match a.parse_args::<Type>() {
578 Err(e) => return e.to_compile_error().into(),
579 Ok(ty) => ty,
580 };
581
582 match &field.ty {
583 Type::Path(p) if p.path.get_ident().map(|i| i == "usize").unwrap_or(false) => {}
584 ty => {
585 return Error::new(
586 ty.span(),
587 "The type of an #[field_offset] member in the vtable must be 'usize'",
588 )
589 .to_compile_error()
590 .into();
591 }
592 }
593
594 if generated_trait_assoc_const.supertraits.is_empty() {
596 generated_trait_assoc_const.colon_token = Some(Default::default());
597 generated_trait_assoc_const.supertraits.push(parse_quote!(Sized));
598 }
599
600 let offset_type: Type = parse_quote!(vtable::FieldOffset<Self, #member_type>);
601
602 vtable_ctor.push(quote!(#ident: T::#ident.get_byte_offset(),));
603
604 let attrs = &field.attrs;
605
606 let vis = &field.vis;
607 generated_to_fn_trait.push(
608 parse_quote! {
609 #(#attrs)*
610 #vis fn #ident(&self) -> &#member_type {
611 unsafe {
612 &*(self.ptr.as_ptr().add(self.vtable.as_ref().#ident) as *const #member_type)
613 }
614 }
615 },
616 );
617 let ident_mut = quote::format_ident!("{}_mut", ident);
618 generated_to_fn_trait.push(
619 parse_quote! {
620 #(#attrs)*
621 #vis fn #ident_mut(&mut self) -> &mut #member_type {
622 unsafe {
623 &mut *(self.ptr.as_ptr().add(self.vtable.as_ref().#ident) as *mut #member_type)
624 }
625 }
626 },
627 );
628
629 offset_type
630 } else {
631 vtable_ctor.push(quote!(#ident: T::#ident,));
632 field.ty.clone()
633 };
634
635 generated_trait_assoc_const.items.push(TraitItem::Const(TraitItemConst {
636 attrs: field.attrs.clone(),
637 const_token: Default::default(),
638 ident: ident.clone(),
639 colon_token: Default::default(),
640 ty: const_type,
641 default: None,
642 semi_token: Default::default(),
643 generics: Default::default(),
644 }));
645 };
646 }
647
648 let vis = input.vis;
649 input.vis = Visibility::Public(Default::default());
650
651 let new_trait_extra = generated_trait_assoc_const.as_ref().map(|x| {
652 let i = &x.ident;
653 quote!(+ #i)
654 });
655
656 let static_vtable_macro_doc = format!(
657 r"Instantiate a static {vtable} for a given type and implements `vtable::HasStaticVTable<{vtable}>` for it.
658
659```ignore
660// The preview above is misleading because of rust-lang/rust#45939, so it is reproduced below
661macro_rules! {macro} {{
662 ($(#[$meta:meta])* $vis:vis static $ident:ident for $ty:ty) => {{ ... }}
663}}
664```
665
666Given a type `MyType` that implements the trait `{trait} {trait_extra}`,
667create a static variable of type {vtable},
668and implements HasStaticVTable for it.
669
670```ignore
671 struct Foo {{ ... }}
672 impl {trait} for Foo {{ ... }}
673 {macro}!(static FOO_VTABLE for Foo);
674 // now VBox::new can be called
675 let vbox = VBox::new(Foo{{ ... }});
676```
677
678 {extra}",
679 vtable = vtable_name,
680 trait = trait_name,
681 trait_extra = new_trait_extra.as_ref().map(|x| x.to_string()).unwrap_or_default(),
682 macro = static_vtable_macro_name,
683 extra = additional_doc,
684 );
685
686 let result = quote!(
687 #[allow(non_snake_case)]
688 #[macro_use]
689 mod #module_name {
691 #![allow(unused_parens)]
692 #[allow(unused)]
693 use super::*;
694 use ::vtable::*;
695 use ::vtable::internal::*;
696 #input
697
698 impl #vtable_name {
699 pub fn new<T: #trait_name #new_trait_extra>() -> Self {
702 Self {
703 #(#vtable_ctor)*
704 }
705 }
706 #(#generated_type_assoc_fn)*
707 }
708
709 #generated_trait
710 #generated_trait_assoc_const
711
712 #[doc(hidden)]
714 #[repr(C)]
715 pub struct #to_name {
716 vtable: ::core::ptr::NonNull<#vtable_name>,
717 ptr: ::core::ptr::NonNull<u8>,
718 }
719 impl #to_name {
720 #(#generated_to_fn_trait)*
721
722 pub fn get_vtable(&self) -> &#vtable_name {
724 unsafe { self.vtable.as_ref() }
725 }
726
727 pub fn as_ptr(&self) -> *const u8 {
729 self.ptr.as_ptr()
730 }
731 }
732
733 unsafe impl VTableMeta for #vtable_name {
734 type VTable = #vtable_name;
735 type Target = #to_name;
736 }
737
738 #(#drop_impls)*
739
740 #[macro_export]
741 #[doc = #static_vtable_macro_doc]
742 macro_rules! #static_vtable_macro_name {
743 ($(#[$meta:meta])* $vis:vis static $ident:ident for $ty:ty) => {
744 $(#[$meta])* $vis static $ident : #vtable_name = {
745 use vtable::*;
746 type T = $ty;
747 #vtable_name {
748 #(#vtable_ctor)*
749 }
750 };
751 #[allow(unsafe_code)]
752 unsafe impl vtable::HasStaticVTable<#vtable_name> for $ty {
753 fn static_vtable() -> &'static #vtable_name {
754 &$ident
755 }
756 }
757 }
758 }
759 }
760 #[doc(inline)]
761 #[macro_use]
762 #vis use #module_name::*;
763 );
764 result.into()
766}