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