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        unsafety: None,
206        auto_token: None,
207        trait_token: Default::default(),
208        ident: trait_name.clone(),
209        generics: Generics::default(),
210        colon_token: None,
211        supertraits: Default::default(),
212        brace_token: Default::default(),
213        items: Default::default(),
214        restriction: Default::default(),
215    };
216
217    let additional_doc =
218        format!("\nNote: Was generated from the [`#[vtable]`](vtable) macro on [`{vtable_name}`]");
219    generated_trait
220        .attrs
221        .append(&mut Attribute::parse_outer.parse2(quote!(#[doc = #additional_doc])).unwrap());
222
223    let mut generated_trait_assoc_const = None;
224
225    let mut generated_to_fn_trait = Vec::new();
226    let mut generated_type_assoc_fn = Vec::new();
227    let mut vtable_ctor = Vec::new();
228
229    for field in &mut fields.named {
230        // 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                    && let Type::Path(p) = &**elem
303                    && let Some(pointer_to) = p.path.get_ident()
304                    && pointer_to == &vtable_name
305                {
306                    if mutability.is_some() {
307                        return Error::new(p.span(), "VTable cannot be mutable")
308                            .to_compile_error()
309                            .into();
310                    }
311                    if call_code.is_some() || !sig.inputs.is_empty() {
312                        return Error::new(p.span(), "VTable pointer need to be the first")
313                            .to_compile_error()
314                            .into();
315                    }
316                    call_code = Some(quote!(vtable as _,));
317                    continue;
318                }
319
320                let (is_pin, self_ty) = match is_pin(&param.ty) {
321                    Some(t) => (true, t),
322                    None => (false, &param.ty),
323                };
324
325                // check for self
326                if let (true, mutability) = if match_generic_type(self_ty, "VRef", &vtable_name) {
327                    (true, None)
328                } else if match_generic_type(self_ty, "VRefMut", &vtable_name) {
329                    (true, Some(Default::default()))
330                } else {
331                    (false, None)
332                } {
333                    if !sig.inputs.is_empty() {
334                        return Error::new(param.span(), "Self pointer need to be the first")
335                            .to_compile_error()
336                            .into();
337                    }
338
339                    let const_or_mut = mutability.map_or_else(|| quote!(const), |x| quote!(#x));
340                    has_self = true;
341                    if !is_pin {
342                        sig.inputs.push(FnArg::Receiver(Receiver {
343                            attrs: param.attrs.clone(),
344                            reference: Some(Default::default()),
345                            mutability,
346                            self_token: Default::default(),
347                            colon_token: None,
348                            ty: Box::new(parse_quote!(& #mutability Self)),
349                        }));
350                        call_code =
351                            Some(quote!(#call_code <#self_ty>::from_raw(self.vtable, self.ptr),));
352                        self_call =
353                            Some(quote!(&#mutability (*(#arg_name.as_ptr() as *#const_or_mut T)),));
354                    } else {
355                        // Pinned
356                        sig.inputs.push(FnArg::Typed(PatType {
357                            attrs: param.attrs.clone(),
358                            pat: Box::new(Pat::parse_single.parse2(quote!(self)).unwrap()),
359                            colon_token: Default::default(),
360                            ty: parse_quote!(core::pin::Pin<& #mutability Self>),
361                        }));
362
363                        call_code = Some(
364                            quote!(#call_code ::core::pin::Pin::new_unchecked(<#self_ty>::from_raw(self.vtable, self.ptr)),),
365                        );
366                        self_call = Some(
367                            quote!(::core::pin::Pin::new_unchecked(&#mutability (*(#arg_name.as_ptr() as *#const_or_mut T))),),
368                        );
369                    }
370                    continue;
371                }
372                sig.inputs.push(typed_arg);
373                call_code = Some(quote!(#call_code #arg_name,));
374                forward_code = Some(quote!(#forward_code #arg_name,));
375            }
376
377            // Add unsafe: The function are not safe to call unless the self parameter is of the correct type
378            f.unsafety = Some(Default::default());
379
380            sig_extern.abi.clone_from(&f.abi);
381
382            let mut wrap_trait_call = None;
383            if !has_self {
384                sig.generics = Generics {
385                    where_clause: Some(parse_str("where Self : Sized").unwrap()),
386                    ..Default::default()
387                };
388
389                // Check if this is a constructor functions
390                if let ReturnType::Type(_, ret) = &f.output
391                    && match_generic_type(ret, "VBox", &vtable_name)
392                {
393                    // Change VBox<VTable> to Self
394                    sig.output = parse_str("-> Self").unwrap();
395                    wrap_trait_call = Some(quote! {
396                        let wrap_trait_call = |x| unsafe {
397                            // Put the object on the heap and get a pointer to it
398                            let ptr = ::core::ptr::NonNull::from(Box::leak(Box::new(x)));
399                            VBox::<#vtable_name>::from_raw(vtable, ptr.cast())
400                        };
401                        wrap_trait_call
402                    });
403                }
404            }
405
406            if ident == "drop" {
407                vtable_ctor.push(quote!(#ident: {
408                    #sig_extern {
409                        unsafe {
410                            ::core::mem::drop(Box::from_raw((#self_call).0 as *mut _));
411                        }
412                    }
413                    #ident::<T>
414                },));
415
416                drop_impls.push(quote! {
417                    unsafe impl VTableMetaDrop for #vtable_name {
418                        unsafe fn drop(ptr: *mut #to_name) {
419                            // Safety: The vtable is valid and inner is a type corresponding to the vtable,
420                            // which was allocated such that drop is expected.
421                            unsafe {
422                                let (vtable, ptr) = ((*ptr).vtable, (*ptr).ptr);
423                                (vtable.as_ref().#ident)(VRefMut::from_raw(vtable, ptr)) }
424                        }
425                        fn new_box<X: HasStaticVTable<#vtable_name>>(value: X) -> VBox<#vtable_name> {
426                            // Put the object on the heap and get a pointer to it
427                            let ptr = ::core::ptr::NonNull::from(Box::leak(Box::new(value)));
428                            unsafe { VBox::from_raw(core::ptr::NonNull::from(X::static_vtable()), ptr.cast()) }
429                        }
430                    }
431                });
432                continue;
433            }
434
435            if ident == "drop_in_place" {
436                vtable_ctor.push(quote!(#ident: {
437                    #[allow(unsafe_code)]
438                    #sig_extern {
439                        #[allow(unused_unsafe)]
440                        unsafe { ::core::ptr::drop_in_place((#self_call).0 as *mut T) };
441                        ::core::alloc::Layout::new::<T>().into()
442                    }
443                    #ident::<T>
444                },));
445
446                drop_impls.push(quote! {
447                    #[allow(unsafe_code)]
448                    unsafe impl VTableMetaDropInPlace for #vtable_name {
449                        unsafe fn #ident(vtable: &Self::VTable, ptr: *mut u8) -> vtable::Layout {
450                            // Safety: The vtable is valid and ptr is a type corresponding to the vtable,
451                            (vtable.#ident)(VRefMut::from_raw(core::ptr::NonNull::from(vtable), ::core::ptr::NonNull::new_unchecked(ptr).cast()))
452                        }
453                        unsafe fn dealloc(vtable: &Self::VTable, ptr: *mut u8, layout: vtable::Layout) {
454                            (vtable.dealloc)(vtable, ptr, layout)
455                        }
456                    }
457                });
458                continue;
459            }
460            if ident == "dealloc" {
461                let abi = &sig_extern.abi;
462                vtable_ctor.push(quote!(#ident: {
463                    #[allow(unsafe_code)]
464                    unsafe #abi fn #ident(_: &#vtable_name, ptr: *mut u8, layout: vtable::Layout) {
465                        use ::core::convert::TryInto;
466                        unsafe { vtable::internal::dealloc(ptr, layout.try_into().unwrap()) }
467                    }
468                    #ident
469                },));
470                continue;
471            }
472
473            generated_trait.items.push(TraitItem::Fn(TraitItemFn {
474                attrs: field.attrs.clone(),
475                sig: sig.clone(),
476                default: None,
477                semi_token: Some(Default::default()),
478            }));
479
480            generated_to_fn_trait.push(ImplItemFn {
481                attrs: field.attrs.clone(),
482                vis: Visibility::Public(Default::default()),
483                defaultness: None,
484                sig: sig.clone(),
485                block: if has_self {
486                    parse_quote!({
487                        // Safety: this rely on the vtable being valid, and the ptr being a valid instance for this vtable
488                        #[allow(unsafe_code)]
489                        unsafe {
490                            let vtable = self.vtable.as_ref();
491                            if let #some(func) = vtable.#ident {
492                                func (#call_code)
493                            } else {
494                                panic!("Called a not-implemented method")
495                            }
496                        }
497                    })
498                } else {
499                    // This should never happen: nobody should be able to access the Trait Object directly.
500                    parse_quote!({ panic!("Calling Sized method on a Trait Object") })
501                },
502            });
503
504            if !has_self {
505                sig.inputs.insert(
506                    0,
507                    FnArg::Receiver(Receiver {
508                        attrs: Default::default(),
509                        reference: Some(Default::default()),
510                        mutability: None,
511                        self_token: Default::default(),
512                        colon_token: None,
513                        ty: Box::new(parse_quote!(&Self)),
514                    }),
515                );
516                sig.output = sig_extern.output.clone();
517                generated_type_assoc_fn.push(ImplItemFn {
518                    attrs: field.attrs.clone(),
519                    vis: generated_trait.vis.clone(),
520                    defaultness: None,
521                    sig,
522                    block: parse_quote!({
523                        let vtable = self;
524                        // Safety: this rely on the vtable being valid, and the ptr being a valid instance for this vtable
525                        #[allow(unsafe_code)]
526                        unsafe { (self.#ident)(#call_code) }
527                    }),
528                });
529
530                vtable_ctor.push(quote!(#ident: {
531                    #sig_extern {
532                        // This is safe since the self must be a instance of our type
533                        #[allow(unused)]
534                        #[allow(unsafe_code)]
535                        let vtable = unsafe { ::core::ptr::NonNull::from(&*_0) };
536                        #wrap_trait_call(T::#ident(#self_call #forward_code))
537                    }
538                    #some(#ident::<T>)
539                },));
540            } else {
541                let erase_return_type_lifetime = match &sig_extern.output {
542                    ReturnType::Default => quote!(),
543                    // If the return type contains a implicit lifetime, it is safe to erase it while returning it
544                    // because a sound implementation of the trait wouldn't allow unsound things here
545                    ReturnType::Type(_, r) => {
546                        quote!(#[allow(clippy::useless_transmute)] ::core::mem::transmute::<#r, #r>)
547                    }
548                };
549                vtable_ctor.push(quote!(#ident: {
550                    #sig_extern {
551                        // This is safe since the self must be a instance of our type
552                        #[allow(unsafe_code)]
553                        unsafe { #erase_return_type_lifetime(T::#ident(#self_call #forward_code)) }
554                    }
555                    #ident::<T>
556                },));
557            }
558        } else {
559            // associated constant
560
561            let generated_trait_assoc_const =
562                generated_trait_assoc_const.get_or_insert_with(|| ItemTrait {
563                    attrs: Attribute::parse_outer.parse_str(&format!(
564                        "/** Trait containing the associated constant relative to the trait {trait_name}.\n{additional_doc} */",
565                    )).unwrap(),
566                    ident: quote::format_ident!("{}Consts", trait_name),
567                    items: Vec::new(),
568                    ..generated_trait.clone()
569                });
570
571            let const_type = if let Some(o) = field
572                .attrs
573                .iter()
574                .position(|a| a.path().get_ident().map(|a| a == "field_offset").unwrap_or(false))
575            {
576                let a = field.attrs.remove(o);
577                let member_type = match a.parse_args::<Type>() {
578                    Err(e) => return e.to_compile_error().into(),
579                    Ok(ty) => ty,
580                };
581
582                match &field.ty {
583                    Type::Path(p) if p.path.get_ident().map(|i| i == "usize").unwrap_or(false) => {}
584                    ty => {
585                        return Error::new(
586                            ty.span(),
587                            "The type of an #[field_offset] member in the vtable must be 'usize'",
588                        )
589                        .to_compile_error()
590                        .into();
591                    }
592                }
593
594                // add `: Sized` to the trait in case it does not have it
595                if generated_trait_assoc_const.supertraits.is_empty() {
596                    generated_trait_assoc_const.colon_token = Some(Default::default());
597                    generated_trait_assoc_const.supertraits.push(parse_quote!(Sized));
598                }
599
600                let offset_type: Type = parse_quote!(vtable::FieldOffset<Self, #member_type>);
601
602                vtable_ctor.push(quote!(#ident: T::#ident.get_byte_offset(),));
603
604                let attrs = &field.attrs;
605
606                let vis = &field.vis;
607                generated_to_fn_trait.push(
608                    parse_quote! {
609                        #(#attrs)*
610                        #vis fn #ident(&self) -> &#member_type {
611                            unsafe {
612                                &*(self.ptr.as_ptr().add(self.vtable.as_ref().#ident) as *const #member_type)
613                            }
614                        }
615                    },
616                );
617                let ident_mut = quote::format_ident!("{}_mut", ident);
618                generated_to_fn_trait.push(
619                    parse_quote! {
620                        #(#attrs)*
621                        #vis fn #ident_mut(&mut self) -> &mut #member_type {
622                            unsafe {
623                                &mut *(self.ptr.as_ptr().add(self.vtable.as_ref().#ident) as *mut #member_type)
624                            }
625                        }
626                    },
627                );
628
629                offset_type
630            } else {
631                vtable_ctor.push(quote!(#ident: T::#ident,));
632                field.ty.clone()
633            };
634
635            generated_trait_assoc_const.items.push(TraitItem::Const(TraitItemConst {
636                attrs: field.attrs.clone(),
637                const_token: Default::default(),
638                ident: ident.clone(),
639                colon_token: Default::default(),
640                ty: const_type,
641                default: None,
642                semi_token: Default::default(),
643                generics: Default::default(),
644            }));
645        };
646    }
647
648    let vis = input.vis;
649    input.vis = Visibility::Public(Default::default());
650
651    let new_trait_extra = generated_trait_assoc_const.as_ref().map(|x| {
652        let i = &x.ident;
653        quote!(+ #i)
654    });
655
656    let static_vtable_macro_doc = format!(
657        r"Instantiate a static {vtable} for a given type and implements `vtable::HasStaticVTable<{vtable}>` for it.
658
659```ignore
660// The preview above is misleading because of rust-lang/rust#45939, so it is reproduced below
661macro_rules! {macro} {{
662    ($(#[$meta:meta])* $vis:vis static $ident:ident for $ty:ty) => {{ ... }}
663}}
664```
665
666Given a type `MyType` that implements the trait `{trait} {trait_extra}`,
667create a static variable of type {vtable},
668and implements HasStaticVTable for it.
669
670```ignore
671    struct Foo {{ ... }}
672    impl {trait} for Foo {{ ... }}
673    {macro}!(static FOO_VTABLE for Foo);
674    // now VBox::new can be called
675    let vbox = VBox::new(Foo{{ ... }});
676```
677
678        {extra}",
679        vtable = vtable_name,
680        trait = trait_name,
681        trait_extra = new_trait_extra.as_ref().map(|x| x.to_string()).unwrap_or_default(),
682        macro = static_vtable_macro_name,
683        extra = additional_doc,
684    );
685
686    let result = quote!(
687        #[allow(non_snake_case)]
688        #[macro_use]
689        /// This private module is generated by the `vtable` macro
690        mod #module_name {
691            #![allow(unused_parens)]
692            #[allow(unused)]
693            use super::*;
694            use ::vtable::*;
695            use ::vtable::internal::*;
696            #input
697
698            impl #vtable_name {
699                // unfortunately cannot be const in stable rust because of the bounds (depends on rfc 2632)
700                /// Create a vtable suitable for a given type implementing the trait.
701                pub /*const*/ fn new<T: #trait_name #new_trait_extra>() -> Self {
702                    Self {
703                        #(#vtable_ctor)*
704                    }
705                }
706                #(#generated_type_assoc_fn)*
707            }
708
709            #generated_trait
710            #generated_trait_assoc_const
711
712            /// Invariant, same as vtable::Inner: vtable and ptr has to be valid and ptr an instance matching the vtable
713            #[doc(hidden)]
714            #[repr(C)]
715            pub struct #to_name {
716                vtable: ::core::ptr::NonNull<#vtable_name>,
717                ptr: ::core::ptr::NonNull<u8>,
718            }
719            impl #to_name {
720                #(#generated_to_fn_trait)*
721
722                /// Returns a reference to the VTable
723                pub fn get_vtable(&self) -> &#vtable_name {
724                    unsafe { self.vtable.as_ref() }
725                }
726
727                /// Return a raw pointer to the object
728                pub fn as_ptr(&self) -> *const u8 {
729                    self.ptr.as_ptr()
730                }
731            }
732
733            unsafe impl VTableMeta for #vtable_name {
734                type VTable = #vtable_name;
735                type Target = #to_name;
736            }
737
738            #(#drop_impls)*
739
740            #[macro_export]
741            #[doc = #static_vtable_macro_doc]
742            macro_rules! #static_vtable_macro_name {
743                ($(#[$meta:meta])* $vis:vis static $ident:ident for $ty:ty) => {
744                    $(#[$meta])* $vis static $ident : #vtable_name = {
745                        use vtable::*;
746                        type T = $ty;
747                        #vtable_name {
748                            #(#vtable_ctor)*
749                        }
750                    };
751                    #[allow(unsafe_code)]
752                    unsafe impl vtable::HasStaticVTable<#vtable_name> for $ty {
753                        fn static_vtable() -> &'static #vtable_name {
754                            &$ident
755                        }
756                    }
757                }
758            }
759        }
760        #[doc(inline)]
761        #[macro_use]
762        #vis use #module_name::*;
763    );
764    //println!("{}", result);
765    result.into()
766}