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        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
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        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/**
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![];
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        // The vtable can only be accessed in unsafe code, so it is ok if all its fields are Public
231        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            // check parameters
279            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                // check for the vtable
300                if let Type::Ptr(TypePtr { mutability, elem, .. })
301                | Type::Reference(TypeReference { mutability, elem, .. }) = &param.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(&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                            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                        // Pinned
362                        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            // Add unsafe: The function are not safe to call unless the self parameter is of the correct type
384            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                // Check if this is a constructor functions
396                if let ReturnType::Type(_, ret) = &f.output {
397                    if match_generic_type(ret, "VBox", &vtable_name) {
398                        // Change VBox<VTable> to Self
399                        sig.output = parse_str("-> Self").unwrap();
400                        wrap_trait_call = Some(quote! {
401                            let wrap_trait_call = |x| unsafe {
402                                // Put the object on the heap and get a pointer to it
403                                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                            // Safety: The vtable is valid and inner is a type corresponding to the vtable,
426                            // which was allocated such that drop is expected.
427                            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                            // Put the object on the heap and get a pointer to it
433                            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                            // Safety: The vtable is valid and ptr is a type corresponding to the vtable,
457                            (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                        // Safety: this rely on the vtable being valid, and the ptr being a valid instance for this vtable
494                        #[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                    // This should never happen: nobody should be able to access the Trait Object directly.
506                    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                        // Safety: this rely on the vtable being valid, and the ptr being a valid instance for this vtable
531                        #[allow(unsafe_code)]
532                        unsafe { (self.#ident)(#call_code) }
533                    }),
534                });
535
536                vtable_ctor.push(quote!(#ident: {
537                    #sig_extern {
538                        // This is safe since the self must be a instance of our type
539                        #[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                    // If the return type contains a implicit lifetime, it is safe to erase it while returning it
550                    // because a sound implementation of the trait wouldn't allow unsound things here
551                    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                        // This is safe since the self must be a instance of our type
558                        #[allow(unsafe_code)]
559                        unsafe { #erase_return_type_lifetime(T::#ident(#self_call #forward_code)) }
560                    }
561                    #ident::<T>
562                },));
563            }
564        } else {
565            // associated constant
566
567            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                // add `: Sized` to the trait in case it does not have it
601                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        /// This private module is generated by the `vtable` macro
696        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                // unfortunately cannot be const in stable rust because of the bounds (depends on rfc 2632)
706                /// Create a vtable suitable for a given type implementing the trait.
707                pub /*const*/ 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            /// Invariant, same as vtable::Inner: vtable and ptr has to be valid and ptr an instance matching the vtable
719            #[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                /// Returns a reference to the VTable
729                pub fn get_vtable(&self) -> &#vtable_name {
730                    unsafe { self.vtable.as_ref() }
731                }
732
733                /// Return a raw pointer to the object
734                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    //println!("{}", result);
771    result.into()
772}