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 if let Some(seg) = pat.path.segments.last() {
21 if seg.ident != container {
22 return false;
23 }
24 if let PathArguments::AngleBracketed(args) = &seg.arguments {
25 if let Some(GenericArgument::Type(Type::Path(arg))) = args.args.last() {
26 return Some(containee) == arg.path.get_ident();
27 }
28 }
29 }
30 }
31 false
32}
33
34fn is_pin(ty: &Type) -> Option<&Type> {
36 if let Type::Path(pat) = ty {
37 if let Some(seg) = pat.path.segments.last() {
38 if seg.ident != "Pin" {
39 return None;
40 }
41 if let PathArguments::AngleBracketed(args) = &seg.arguments {
42 if let Some(GenericArgument::Type(t)) = args.args.last() {
43 return Some(t);
44 }
45 }
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![];
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![];
226 let mut generated_type_assoc_fn = vec![];
227 let mut vtable_ctor = vec![];
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 {
303 if let Type::Path(p) = &**elem {
304 if let Some(pointer_to) = p.path.get_ident() {
305 if pointer_to == &vtable_name {
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(
313 p.span(),
314 "VTable pointer need to be the first",
315 )
316 .to_compile_error()
317 .into();
318 }
319 call_code = Some(quote!(vtable as _,));
320 continue;
321 }
322 }
323 }
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 reference: Some(Default::default()),
351 mutability,
352 self_token: Default::default(),
353 colon_token: None,
354 ty: Box::new(parse_quote!(& #mutability Self)),
355 }));
356 call_code =
357 Some(quote!(#call_code <#self_ty>::from_raw(self.vtable, self.ptr),));
358 self_call =
359 Some(quote!(&#mutability (*(#arg_name.as_ptr() as *#const_or_mut T)),));
360 } else {
361 sig.inputs.push(FnArg::Typed(PatType {
363 attrs: param.attrs.clone(),
364 pat: Box::new(Pat::parse_single.parse2(quote!(self)).unwrap()),
365 colon_token: Default::default(),
366 ty: parse_quote!(core::pin::Pin<& #mutability Self>),
367 }));
368
369 call_code = Some(
370 quote!(#call_code core::pin::Pin::new_unchecked(<#self_ty>::from_raw(self.vtable, self.ptr)),),
371 );
372 self_call = Some(
373 quote!(core::pin::Pin::new_unchecked(&#mutability (*(#arg_name.as_ptr() as *#const_or_mut T))),),
374 );
375 }
376 continue;
377 }
378 sig.inputs.push(typed_arg);
379 call_code = Some(quote!(#call_code #arg_name,));
380 forward_code = Some(quote!(#forward_code #arg_name,));
381 }
382
383 f.unsafety = Some(Default::default());
385
386 sig_extern.abi.clone_from(&f.abi);
387
388 let mut wrap_trait_call = None;
389 if !has_self {
390 sig.generics = Generics {
391 where_clause: Some(parse_str("where Self : Sized").unwrap()),
392 ..Default::default()
393 };
394
395 if let ReturnType::Type(_, ret) = &f.output {
397 if match_generic_type(ret, "VBox", &vtable_name) {
398 sig.output = parse_str("-> Self").unwrap();
400 wrap_trait_call = Some(quote! {
401 let wrap_trait_call = |x| unsafe {
402 let ptr = core::ptr::NonNull::from(Box::leak(Box::new(x)));
404 VBox::<#vtable_name>::from_raw(vtable, ptr.cast())
405 };
406 wrap_trait_call
407 });
408 }
409 }
410 }
411
412 if ident == "drop" {
413 vtable_ctor.push(quote!(#ident: {
414 #sig_extern {
415 unsafe {
416 ::core::mem::drop(Box::from_raw((#self_call).0 as *mut _));
417 }
418 }
419 #ident::<T>
420 },));
421
422 drop_impls.push(quote! {
423 unsafe impl VTableMetaDrop for #vtable_name {
424 unsafe fn drop(ptr: *mut #to_name) {
425 unsafe {
428 let (vtable, ptr) = ((*ptr).vtable, (*ptr).ptr);
429 (vtable.as_ref().#ident)(VRefMut::from_raw(vtable, ptr)) }
430 }
431 fn new_box<X: HasStaticVTable<#vtable_name>>(value: X) -> VBox<#vtable_name> {
432 let ptr = core::ptr::NonNull::from(Box::leak(Box::new(value)));
434 unsafe { VBox::from_raw(core::ptr::NonNull::from(X::static_vtable()), ptr.cast()) }
435 }
436 }
437 });
438 continue;
439 }
440
441 if ident == "drop_in_place" {
442 vtable_ctor.push(quote!(#ident: {
443 #[allow(unsafe_code)]
444 #sig_extern {
445 #[allow(unused_unsafe)]
446 unsafe { ::core::ptr::drop_in_place((#self_call).0 as *mut T) };
447 ::core::alloc::Layout::new::<T>().into()
448 }
449 #ident::<T>
450 },));
451
452 drop_impls.push(quote! {
453 #[allow(unsafe_code)]
454 unsafe impl VTableMetaDropInPlace for #vtable_name {
455 unsafe fn #ident(vtable: &Self::VTable, ptr: *mut u8) -> vtable::Layout {
456 (vtable.#ident)(VRefMut::from_raw(core::ptr::NonNull::from(vtable), core::ptr::NonNull::new_unchecked(ptr).cast()))
458 }
459 unsafe fn dealloc(vtable: &Self::VTable, ptr: *mut u8, layout: vtable::Layout) {
460 (vtable.dealloc)(vtable, ptr, layout)
461 }
462 }
463 });
464 continue;
465 }
466 if ident == "dealloc" {
467 let abi = &sig_extern.abi;
468 vtable_ctor.push(quote!(#ident: {
469 #[allow(unsafe_code)]
470 unsafe #abi fn #ident(_: &#vtable_name, ptr: *mut u8, layout: vtable::Layout) {
471 use ::core::convert::TryInto;
472 vtable::internal::dealloc(ptr, layout.try_into().unwrap())
473 }
474 #ident
475 },));
476 continue;
477 }
478
479 generated_trait.items.push(TraitItem::Fn(TraitItemFn {
480 attrs: field.attrs.clone(),
481 sig: sig.clone(),
482 default: None,
483 semi_token: Some(Default::default()),
484 }));
485
486 generated_to_fn_trait.push(ImplItemFn {
487 attrs: field.attrs.clone(),
488 vis: Visibility::Public(Default::default()),
489 defaultness: None,
490 sig: sig.clone(),
491 block: if has_self {
492 parse_quote!({
493 #[allow(unsafe_code)]
495 unsafe {
496 let vtable = self.vtable.as_ref();
497 if let #some(func) = vtable.#ident {
498 func (#call_code)
499 } else {
500 panic!("Called a not-implemented method")
501 }
502 }
503 })
504 } else {
505 parse_quote!({ panic!("Calling Sized method on a Trait Object") })
507 },
508 });
509
510 if !has_self {
511 sig.inputs.insert(
512 0,
513 FnArg::Receiver(Receiver {
514 attrs: Default::default(),
515 reference: Some(Default::default()),
516 mutability: None,
517 self_token: Default::default(),
518 colon_token: None,
519 ty: Box::new(parse_quote!(&Self)),
520 }),
521 );
522 sig.output = sig_extern.output.clone();
523 generated_type_assoc_fn.push(ImplItemFn {
524 attrs: field.attrs.clone(),
525 vis: generated_trait.vis.clone(),
526 defaultness: None,
527 sig,
528 block: parse_quote!({
529 let vtable = self;
530 #[allow(unsafe_code)]
532 unsafe { (self.#ident)(#call_code) }
533 }),
534 });
535
536 vtable_ctor.push(quote!(#ident: {
537 #sig_extern {
538 #[allow(unused)]
540 #[allow(unsafe_code)]
541 let vtable = unsafe { core::ptr::NonNull::from(&*_0) };
542 #wrap_trait_call(T::#ident(#self_call #forward_code))
543 }
544 #some(#ident::<T>)
545 },));
546 } else {
547 let erase_return_type_lifetime = match &sig_extern.output {
548 ReturnType::Default => quote!(),
549 ReturnType::Type(_, r) => {
552 quote!(#[allow(clippy::useless_transmute)] core::mem::transmute::<#r, #r>)
553 }
554 };
555 vtable_ctor.push(quote!(#ident: {
556 #sig_extern {
557 #[allow(unsafe_code)]
559 unsafe { #erase_return_type_lifetime(T::#ident(#self_call #forward_code)) }
560 }
561 #ident::<T>
562 },));
563 }
564 } else {
565 let generated_trait_assoc_const =
568 generated_trait_assoc_const.get_or_insert_with(|| ItemTrait {
569 attrs: Attribute::parse_outer.parse_str(&format!(
570 "/** Trait containing the associated constant relative to the trait {trait_name}.\n{additional_doc} */",
571 )).unwrap(),
572 ident: quote::format_ident!("{}Consts", trait_name),
573 items: vec![],
574 ..generated_trait.clone()
575 });
576
577 let const_type = if let Some(o) = field
578 .attrs
579 .iter()
580 .position(|a| a.path().get_ident().map(|a| a == "field_offset").unwrap_or(false))
581 {
582 let a = field.attrs.remove(o);
583 let member_type = match a.parse_args::<Type>() {
584 Err(e) => return e.to_compile_error().into(),
585 Ok(ty) => ty,
586 };
587
588 match &field.ty {
589 Type::Path(p) if p.path.get_ident().map(|i| i == "usize").unwrap_or(false) => {}
590 ty => {
591 return Error::new(
592 ty.span(),
593 "The type of an #[field_offset] member in the vtable must be 'usize'",
594 )
595 .to_compile_error()
596 .into()
597 }
598 }
599
600 if generated_trait_assoc_const.supertraits.is_empty() {
602 generated_trait_assoc_const.colon_token = Some(Default::default());
603 generated_trait_assoc_const.supertraits.push(parse_quote!(Sized));
604 }
605
606 let offset_type: Type = parse_quote!(vtable::FieldOffset<Self, #member_type>);
607
608 vtable_ctor.push(quote!(#ident: T::#ident.get_byte_offset(),));
609
610 let attrs = &field.attrs;
611
612 let vis = &field.vis;
613 generated_to_fn_trait.push(
614 parse_quote! {
615 #(#attrs)*
616 #vis fn #ident(&self) -> &#member_type {
617 unsafe {
618 &*(self.ptr.as_ptr().add(self.vtable.as_ref().#ident) as *const #member_type)
619 }
620 }
621 },
622 );
623 let ident_mut = quote::format_ident!("{}_mut", ident);
624 generated_to_fn_trait.push(
625 parse_quote! {
626 #(#attrs)*
627 #vis fn #ident_mut(&mut self) -> &mut #member_type {
628 unsafe {
629 &mut *(self.ptr.as_ptr().add(self.vtable.as_ref().#ident) as *mut #member_type)
630 }
631 }
632 },
633 );
634
635 offset_type
636 } else {
637 vtable_ctor.push(quote!(#ident: T::#ident,));
638 field.ty.clone()
639 };
640
641 generated_trait_assoc_const.items.push(TraitItem::Const(TraitItemConst {
642 attrs: field.attrs.clone(),
643 const_token: Default::default(),
644 ident: ident.clone(),
645 colon_token: Default::default(),
646 ty: const_type,
647 default: None,
648 semi_token: Default::default(),
649 generics: Default::default(),
650 }));
651 };
652 }
653
654 let vis = input.vis;
655 input.vis = Visibility::Public(Default::default());
656
657 let new_trait_extra = generated_trait_assoc_const.as_ref().map(|x| {
658 let i = &x.ident;
659 quote!(+ #i)
660 });
661
662 let static_vtable_macro_doc = format!(
663 r"Instantiate a static {vtable} for a given type and implements `vtable::HasStaticVTable<{vtable}>` for it.
664
665```ignore
666// The preview above is misleading because of rust-lang/rust#45939, so it is reproduced below
667macro_rules! {macro} {{
668 ($(#[$meta:meta])* $vis:vis static $ident:ident for $ty:ty) => {{ ... }}
669}}
670```
671
672Given a type `MyType` that implements the trait `{trait} {trait_extra}`,
673create a static variable of type {vtable},
674and implements HasStaticVTable for it.
675
676```ignore
677 struct Foo {{ ... }}
678 impl {trait} for Foo {{ ... }}
679 {macro}!(static FOO_VTABLE for Foo);
680 // now VBox::new can be called
681 let vbox = VBox::new(Foo{{ ... }});
682```
683
684 {extra}",
685 vtable = vtable_name,
686 trait = trait_name,
687 trait_extra = new_trait_extra.as_ref().map(|x| x.to_string()).unwrap_or_default(),
688 macro = static_vtable_macro_name,
689 extra = additional_doc,
690 );
691
692 let result = quote!(
693 #[allow(non_snake_case)]
694 #[macro_use]
695 mod #module_name {
697 #![allow(unused_parens)]
698 #[allow(unused)]
699 use super::*;
700 use ::vtable::*;
701 use ::vtable::internal::*;
702 #input
703
704 impl #vtable_name {
705 pub fn new<T: #trait_name #new_trait_extra>() -> Self {
708 Self {
709 #(#vtable_ctor)*
710 }
711 }
712 #(#generated_type_assoc_fn)*
713 }
714
715 #generated_trait
716 #generated_trait_assoc_const
717
718 #[doc(hidden)]
720 #[repr(C)]
721 pub struct #to_name {
722 vtable: core::ptr::NonNull<#vtable_name>,
723 ptr: core::ptr::NonNull<u8>,
724 }
725 impl #to_name {
726 #(#generated_to_fn_trait)*
727
728 pub fn get_vtable(&self) -> &#vtable_name {
730 unsafe { self.vtable.as_ref() }
731 }
732
733 pub fn as_ptr(&self) -> *const u8 {
735 self.ptr.as_ptr()
736 }
737 }
738
739 unsafe impl VTableMeta for #vtable_name {
740 type VTable = #vtable_name;
741 type Target = #to_name;
742 }
743
744 #(#drop_impls)*
745
746 #[macro_export]
747 #[doc = #static_vtable_macro_doc]
748 macro_rules! #static_vtable_macro_name {
749 ($(#[$meta:meta])* $vis:vis static $ident:ident for $ty:ty) => {
750 $(#[$meta])* $vis static $ident : #vtable_name = {
751 use vtable::*;
752 type T = $ty;
753 #vtable_name {
754 #(#vtable_ctor)*
755 }
756 };
757 #[allow(unsafe_code)]
758 unsafe impl vtable::HasStaticVTable<#vtable_name> for $ty {
759 fn static_vtable() -> &'static #vtable_name {
760 &$ident
761 }
762 }
763 }
764 }
765 }
766 #[doc(inline)]
767 #[macro_use]
768 #vis use #module_name::*;
769 );
770 result.into()
772}