Skip to main content

vtable_macro/
macro.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4// cSpell: ignore asyncness constness containee defaultness impls qself supertraits vref
5
6/*!
7Implementation detail for the vtable crate
8*/
9
10extern crate proc_macro;
11use proc_macro::TokenStream;
12use quote::quote;
13use syn::parse::Parser;
14use syn::spanned::Spanned;
15use syn::*;
16
17/// Returns true if the type `ty` is  "Container<Containee>"
18fn 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
34/// Returns Some(type) if the type is `Pin<type>`
35fn 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/**
52This macro needs to be applied to a VTable structure
53
54The design choice is that it is applied to a VTable and not to a trait so that cbindgen
55can see the actual vtable struct.
56
57This macro needs to be applied to a struct whose name ends with "VTable", and which
58contains members which are function pointers.
59
60For example, if it is applied to `struct MyTraitVTable`, it will create:
61 - The `MyTrait` trait with all the functions.
62 - The `MyTraitConsts` trait for the associated constants, if any
63 - `MyTraitVTable_static!` macro.
64
65It will also implement the `VTableMeta` and `VTableMetaDrop` traits so that VRef and so on can work,
66allowing to access methods from the trait directly from VRef.
67
68This macro does the following transformation:
69
70For function type fields:
71 - `unsafe` is added to the signature, since it is unsafe to call these functions directly from
72   the vtable without having a valid pointer to the actual object. But if the original function was
73   marked unsafe, the unsafety is forwarded to the trait.
74 - If a field is called `drop`, then it is understood that this is the destructor for a VBox.
75   It must have the type `fn(VRefMut<MyVTable>)`
76 - If two fields called `drop_in_place` and `dealloc` are present, then they are understood to be
77   in-place destructors and deallocation functions. `drop_in_place` must have the signature
78   `fn(VRefMut<MyVTable> -> Layout`, and `dealloc` must have the signature
79   `fn(&MyVTable, ptr: *mut u8, layout: Layout)`.
80   `drop_in_place` is responsible for destructing the object and returning the memory layout that
81   was used for the initial allocation. It will be passed to `dealloc`, which is responsible for releasing
82   the memory. These two functions are used to enable the use of `VRc` and `VWeak`.
83 - If the first argument of the function is `VRef<MyVTable>` or `VRefMut<MyVTable>`, then it is
84   understood as a `&self` or `&mut self` argument in the trait.
85 - Similarly, if it is a `Pin<VRef<MyVTable>>` or `Pin<VRefMut<MyVTable>>`, self is mapped
86   to `Pin<&Self>` or `Pin<&mut Self>`
87
88For the other fields:
89 - They are considered associated constants of the MyTraitConsts trait.
90 - If they are annotated with the `#[field_offset(FieldType)]` attribute, the type of the field must be `usize`,
91   and the associated const in the trait will be of type `FieldOffset<Self, FieldType>`, and an accessor to
92   the field reference and reference mut will be added to the Target of VRef and VRefMut.
93
94The VRef/VRefMut/VBox structure will dereference to a type which has the following associated items:
95 - The functions from the vtable that have a VRef or VRefMut first parameter for self.
96 - For each `#[field_offset]` attributes, a corresponding getter returns a reference
97   to that field, and mutable accessor that ends with `_mut` returns a mutable reference.
98 - `as_ptr` returns a `*mut u8`
99 - `get_vtable` Return a reference to the VTable so one can access the associated consts.
100
101The VTable struct gets a `new` associated function that creates a vtable for any type
102that implements the generated traits.
103
104## Example
105
106
107```
108use vtable::*;
109// we are going to declare a VTable structure for an Animal trait
110#[vtable]
111#[repr(C)]
112struct AnimalVTable {
113    /// Pointer to a function that make noise.
114    /// `unsafe` will automatically be added
115    make_noise: fn(VRef<AnimalVTable>, i32) -> i32,
116
117    /// if there is a 'drop' member, it is considered as the destructor
118    drop: fn(VRefMut<AnimalVTable>),
119
120    /// Associated constant.
121    LEG_NUMBER: i8,
122
123    /// There exist a `bool` field in the structure and this is an offset
124    #[field_offset(bool)]
125    IS_HUNGRY: usize,
126
127}
128
129#[repr(C)]
130struct Dog{ strength: i32, is_hungry: bool };
131
132// The #[vtable] macro created the Animal Trait
133impl Animal for Dog {
134    fn make_noise(&self, intensity: i32) -> i32 {
135        println!("Wof!");
136        return self.strength * intensity;
137    }
138}
139
140// The #[vtable] macro created the AnimalConsts Trait
141impl AnimalConsts for Dog {
142    const LEG_NUMBER: i8 = 4;
143    const IS_HUNGRY: vtable::FieldOffset<Self, bool> = unsafe { vtable::FieldOffset::new_from_offset(4) };
144}
145
146
147// The #[vtable] macro also exposed a macro to create a vtable
148AnimalVTable_static!(static DOG_VT for Dog);
149
150// with that, it is possible to instantiate a vtable::VRefMut
151let mut dog = Dog { strength: 100, is_hungry: false };
152{
153    let mut animal_vref = VRefMut::<AnimalVTable>::new(&mut dog);
154
155    // access to the vtable through the get_vtable() function
156    assert_eq!(animal_vref.get_vtable().LEG_NUMBER, 4);
157    // functions are also added for the #[field_offset] member
158    assert_eq!(*animal_vref.IS_HUNGRY(), false);
159    *animal_vref.IS_HUNGRY_mut() = true;
160}
161assert_eq!(dog.is_hungry, true);
162```
163
164
165*/
166#[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        // The vtable can only be accessed in unsafe code, so it is ok if all its fields are Public
230        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            // check parameters
277            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                // check for the vtable
298                let ptr_target = match &param.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(&param.ty) {
327                    Some(t) => (true, t),
328                    None => (false, &param.ty),
329                };
330
331                // check for self
332                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                        // Pinned
360                        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            // Add unsafe: The function are not safe to call unless the self parameter is of the correct type
382            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                // Check if this is a constructor functions
394                if let ReturnType::Type(_, ret) = &f.output
395                    && match_generic_type(ret, "VBox", &vtable_name)
396                {
397                    // Change VBox<VTable> to Self
398                    sig.output = parse_str("-> Self").unwrap();
399                    wrap_trait_call = Some(quote! {
400                        let wrap_trait_call = |x| unsafe {
401                            // Put the object on the heap and get a pointer to it
402                            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                            // Safety: The vtable is valid and inner is a type corresponding to the vtable,
424                            // which was allocated such that drop is expected.
425                            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                            // Put the object on the heap and get a pointer to it
431                            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                            // Safety: The vtable is valid and ptr is a type corresponding to the vtable,
455                            (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                        // Safety: this rely on the vtable being valid, and the ptr being a valid instance for this vtable
493                        #[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                    // This should never happen: nobody should be able to access the Trait Object directly.
505                    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                        // Safety: this rely on the vtable being valid, and the ptr being a valid instance for this vtable
528                        #[allow(unsafe_code)]
529                        unsafe { (self.#ident)(#call_code) }
530                    }),
531                });
532
533                vtable_ctor.push(quote!(#ident: {
534                    #sig_extern {
535                        // This is safe since the self must be a instance of our type
536                        #[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                    // If the return type contains a implicit lifetime, it is safe to erase it while returning it
547                    // because a sound implementation of the trait wouldn't allow unsound things here
548                    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                        // This is safe since the self must be a instance of our type
555                        #[allow(unsafe_code)]
556                        unsafe { #erase_return_type_lifetime(T::#ident(#self_call #forward_code)) }
557                    }
558                    #ident::<T>
559                },));
560            }
561        } else {
562            // associated constant
563
564            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                // add `: Sized` to the trait in case it does not have it
598                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        /// This private module is generated by the `vtable` macro
694        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                // unfortunately cannot be const in stable rust because of the bounds (depends on rfc 2632)
704                /// Create a vtable suitable for a given type implementing the trait.
705                pub /*const*/ 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            /// Invariant, same as vtable::Inner: vtable and ptr has to be valid and ptr an instance matching the vtable
717            #[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                /// Returns a reference to the VTable
727                pub fn get_vtable(&self) -> &#vtable_name {
728                    unsafe { self.vtable.as_ref() }
729                }
730
731                /// Return a raw pointer to the object
732                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    //println!("{}", result);
767    result.into()
768}