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: &TypePath) = ty {
20 if let Some(seg: &PathSegment) = pat.path.segments.last() {
21 if seg.ident != container {
22 return false;
23 }
24 if let PathArguments::AngleBracketed(args: &AngleBracketedGenericArguments) = &seg.arguments {
25 if let Some(GenericArgument::Type(Type::Path(arg: &TypePath))) = 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: &TypePath) = ty {
37 if let Some(seg: &PathSegment) = pat.path.segments.last() {
38 if seg.ident != "Pin" {
39 return None;
40 }
41 if let PathArguments::AngleBracketed(args: &AngleBracketedGenericArguments) = &seg.arguments {
42 if let Some(GenericArgument::Type(t: &Type)) = 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 - The ABI of each functions is changed to `extern "C"`
72 - `unsafe` is added to the signature, since it is unsafe to call these functions directly from
73 the vtable without having a valid pointer to the actual object. But if the original function was
74 marked unsafe, the unsafety is forwarded to the trait.
75 - If a field is called `drop`, then it is understood that this is the destructor for a VBox.
76 It must have the type `fn(VRefMut<MyVTable>)`
77 - If two fields called `drop_in_place` and `dealloc` are present, then they are understood to be
78 in-place destructors and deallocation functions. `drop_in_place` must have the signature
79 `fn(VRefMut<MyVTable> -> Layout`, and `dealloc` must have the signature
80 `fn(&MyVTable, ptr: *mut u8, layout: Layout)`.
81 `drop_in_place` is responsible for destructing the object and returning the memory layout that
82 was used for the initial allocation. It will be passed to `dealloc`, which is responsible for releasing
83 the memory. These two functions are used to enable the use of `VRc` and `VWeak`.
84 - If the first argument of the function is `VRef<MyVTable>` or `VRefMut<MyVTable>`, then it is
85 understood as a `&self` or `&mut self` argument in the trait.
86 - Similarly, if it is a `Pin<VRef<MyVTable>>` or `Pin<VRefMut<MyVTable>>`, self is mapped
87 to `Pin<&Self>` or `Pin<&mut Self>`
88
89For the other fields:
90 - They are considered associated constants of the MyTraitConsts trait.
91 - If they are annotated with the `#[field_offset(FieldType)]` attribute, the type of the field must be `usize`,
92 and the associated const in the trait will be of type `FieldOffset<Self, FieldType>`, and an accessor to
93 the field reference and reference mut will be added to the Target of VRef and VRefMut.
94
95The VRef/VRefMut/VBox structure will dereference to a type which has the following associated items:
96 - The functions from the vtable that have a VRef or VRefMut first parameter for self.
97 - For each `#[field_offset]` attributes, a corresponding getter returns a reference
98 to that field, and mutable accessor that ends with `_mut` returns a mutable reference.
99 - `as_ptr` returns a `*mut u8`
100 - `get_vtable` Return a reference to the VTable so one can access the associated consts.
101
102The VTable struct gets a `new` associated function that creates a vtable for any type
103that implements the generated traits.
104
105## Example
106
107
108```
109use vtable::*;
110// we are going to declare a VTable structure for an Animal trait
111#[vtable]
112#[repr(C)]
113struct AnimalVTable {
114 /// Pointer to a function that make noise.
115 /// `unsafe` and `extern "C"` will automatically be added
116 make_noise: fn(VRef<AnimalVTable>, i32) -> i32,
117
118 /// if there is a 'drop' member, it is considered as the destructor
119 drop: fn(VRefMut<AnimalVTable>),
120
121 /// Associated constant.
122 LEG_NUMBER: i8,
123
124 /// There exist a `bool` field in the structure and this is an offset
125 #[field_offset(bool)]
126 IS_HUNGRY: usize,
127
128}
129
130#[repr(C)]
131struct Dog{ strength: i32, is_hungry: bool };
132
133// The #[vtable] macro created the Animal Trait
134impl Animal for Dog {
135 fn make_noise(&self, intensity: i32) -> i32 {
136 println!("Wof!");
137 return self.strength * intensity;
138 }
139}
140
141// The #[vtable] macro created the AnimalConsts Trait
142impl AnimalConsts for Dog {
143 const LEG_NUMBER: i8 = 4;
144 const IS_HUNGRY: vtable::FieldOffset<Self, bool> = unsafe { vtable::FieldOffset::new_from_offset(4) };
145}
146
147
148// The #[vtable] macro also exposed a macro to create a vtable
149AnimalVTable_static!(static DOG_VT for Dog);
150
151// with that, it is possible to instantiate a vtable::VRefMut
152let mut dog = Dog { strength: 100, is_hungry: false };
153{
154 let mut animal_vref = VRefMut::<AnimalVTable>::new(&mut dog);
155
156 // access to the vtable through the get_vtable() function
157 assert_eq!(animal_vref.get_vtable().LEG_NUMBER, 4);
158 // functions are also added for the #[field_offset] member
159 assert_eq!(*animal_vref.IS_HUNGRY(), false);
160 *animal_vref.IS_HUNGRY_mut() = true;
161}
162assert_eq!(dog.is_hungry, true);
163```
164
165
166*/
167#[proc_macro_attribute]
168pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream {
169 let mut input = parse_macro_input!(item as ItemStruct);
170
171 let fields = if let Fields::Named(fields) = &mut input.fields {
172 fields
173 } else {
174 return Error::new(
175 proc_macro2::Span::call_site(),
176 "Only supported for structure with named fields",
177 )
178 .to_compile_error()
179 .into();
180 };
181
182 let vtable_name = input.ident.to_string();
183 if !vtable_name.ends_with("VTable") {
184 return Error::new(input.ident.span(), "The structure does not ends in 'VTable'")
185 .to_compile_error()
186 .into();
187 }
188
189 let trait_name = Ident::new(&vtable_name[..vtable_name.len() - 6], input.ident.span());
190 let to_name = quote::format_ident!("{}TO", trait_name);
191 let module_name = quote::format_ident!("{}_vtable_mod", trait_name);
192 let static_vtable_macro_name = quote::format_ident!("{}_static", vtable_name);
193
194 let vtable_name = input.ident.clone();
195
196 let mut drop_impls = vec![];
197
198 let mut generated_trait = ItemTrait {
199 attrs: input
200 .attrs
201 .iter()
202 .filter(|a| a.path().get_ident().as_ref().map(|i| *i == "doc").unwrap_or(false))
203 .cloned()
204 .collect(),
205 vis: Visibility::Public(Default::default()),
206 unsafety: None,
207 auto_token: None,
208 trait_token: Default::default(),
209 ident: trait_name.clone(),
210 generics: Generics::default(),
211 colon_token: None,
212 supertraits: Default::default(),
213 brace_token: Default::default(),
214 items: Default::default(),
215 restriction: Default::default(),
216 };
217
218 let additional_doc =
219 format!("\nNote: Was generated from the [`#[vtable]`](vtable) macro on [`{vtable_name}`]");
220 generated_trait
221 .attrs
222 .append(&mut Attribute::parse_outer.parse2(quote!(#[doc = #additional_doc])).unwrap());
223
224 let mut generated_trait_assoc_const = None;
225
226 let mut generated_to_fn_trait = vec![];
227 let mut generated_type_assoc_fn = vec![];
228 let mut vtable_ctor = vec![];
229
230 for field in &mut fields.named {
231 // The vtable can only be accessed in unsafe code, so it is ok if all its fields are Public
232 field.vis = Visibility::Public(Default::default());
233
234 let ident = field.ident.as_ref().unwrap();
235 let mut some = None;
236
237 let func_ty = if let Type::BareFn(f) = &mut field.ty {
238 Some(f)
239 } else if let Type::Path(pat) = &mut field.ty {
240 pat.path.segments.last_mut().and_then(|seg| {
241 if seg.ident == "Option" {
242 some = Some(quote!(Some));
243 if let PathArguments::AngleBracketed(args) = &mut seg.arguments {
244 if let Some(GenericArgument::Type(Type::BareFn(f))) = args.args.first_mut()
245 {
246 Some(f)
247 } else {
248 None
249 }
250 } else {
251 None
252 }
253 } else {
254 None
255 }
256 })
257 } else {
258 None
259 };
260
261 if let Some(f) = func_ty {
262 let mut sig = Signature {
263 constness: None,
264 asyncness: None,
265 unsafety: f.unsafety,
266 abi: None,
267 fn_token: f.fn_token,
268 ident: ident.clone(),
269 generics: Default::default(),
270 paren_token: f.paren_token,
271 inputs: Default::default(),
272 variadic: None,
273 output: f.output.clone(),
274 };
275
276 let mut sig_extern = sig.clone();
277 sig_extern.abi = Some(parse_str("extern \"C\"").unwrap());
278 sig_extern.generics = parse_str(&format!("<T : {trait_name}>")).unwrap();
279
280 // check parameters
281 let mut call_code = None;
282 let mut self_call = None;
283 let mut forward_code = None;
284
285 let mut has_self = false;
286
287 for param in &f.inputs {
288 let arg_name = quote::format_ident!("_{}", sig_extern.inputs.len());
289 let typed_arg = FnArg::Typed(PatType {
290 attrs: param.attrs.clone(),
291 pat: Box::new(Pat::Path(syn::PatPath {
292 attrs: Default::default(),
293 qself: None,
294 path: arg_name.clone().into(),
295 })),
296 colon_token: Default::default(),
297 ty: Box::new(param.ty.clone()),
298 });
299 sig_extern.inputs.push(typed_arg.clone());
300
301 // check for the vtable
302 if let Type::Ptr(TypePtr { mutability, elem, .. })
303 | Type::Reference(TypeReference { mutability, elem, .. }) = &param.ty
304 {
305 if let Type::Path(p) = &**elem {
306 if let Some(pointer_to) = p.path.get_ident() {
307 if pointer_to == &vtable_name {
308 if mutability.is_some() {
309 return Error::new(p.span(), "VTable cannot be mutable")
310 .to_compile_error()
311 .into();
312 }
313 if call_code.is_some() || !sig.inputs.is_empty() {
314 return Error::new(
315 p.span(),
316 "VTable pointer need to be the first",
317 )
318 .to_compile_error()
319 .into();
320 }
321 call_code = Some(quote!(vtable as _,));
322 continue;
323 }
324 }
325 }
326 }
327
328 let (is_pin, self_ty) = match is_pin(&param.ty) {
329 Some(t) => (true, t),
330 None => (false, &param.ty),
331 };
332
333 // check for self
334 if let (true, mutability) = if match_generic_type(self_ty, "VRef", &vtable_name) {
335 (true, None)
336 } else if match_generic_type(self_ty, "VRefMut", &vtable_name) {
337 (true, Some(Default::default()))
338 } else {
339 (false, None)
340 } {
341 if !sig.inputs.is_empty() {
342 return Error::new(param.span(), "Self pointer need to be the first")
343 .to_compile_error()
344 .into();
345 }
346
347 let const_or_mut = mutability.map_or_else(|| quote!(const), |x| quote!(#x));
348 has_self = true;
349 if !is_pin {
350 sig.inputs.push(FnArg::Receiver(Receiver {
351 attrs: param.attrs.clone(),
352 reference: Some(Default::default()),
353 mutability,
354 self_token: Default::default(),
355 colon_token: None,
356 ty: Box::new(parse_quote!(& #mutability Self)),
357 }));
358 call_code =
359 Some(quote!(#call_code <#self_ty>::from_raw(self.vtable, self.ptr),));
360 self_call =
361 Some(quote!(&#mutability (*(#arg_name.as_ptr() as *#const_or_mut T)),));
362 } else {
363 // Pinned
364 sig.inputs.push(FnArg::Typed(PatType {
365 attrs: param.attrs.clone(),
366 pat: Box::new(Pat::parse_single.parse2(quote!(self)).unwrap()),
367 colon_token: Default::default(),
368 ty: parse_quote!(core::pin::Pin<& #mutability Self>),
369 }));
370
371 call_code = Some(
372 quote!(#call_code core::pin::Pin::new_unchecked(<#self_ty>::from_raw(self.vtable, self.ptr)),),
373 );
374 self_call = Some(
375 quote!(core::pin::Pin::new_unchecked(&#mutability (*(#arg_name.as_ptr() as *#const_or_mut T))),),
376 );
377 }
378 continue;
379 }
380 sig.inputs.push(typed_arg);
381 call_code = Some(quote!(#call_code #arg_name,));
382 forward_code = Some(quote!(#forward_code #arg_name,));
383 }
384
385 // Add unsafe: The function are not safe to call unless the self parameter is of the correct type
386 f.unsafety = Some(Default::default());
387
388 // Add extern "C" if it isn't there
389 if let Some(a) = &f.abi {
390 if !a.name.as_ref().map(|s| s.value() == "C").unwrap_or(false) {
391 return Error::new(a.span(), "invalid ABI").to_compile_error().into();
392 }
393 } else {
394 f.abi.clone_from(&sig_extern.abi);
395 }
396
397 let mut wrap_trait_call = None;
398 if !has_self {
399 sig.generics = Generics {
400 where_clause: Some(parse_str("where Self : Sized").unwrap()),
401 ..Default::default()
402 };
403
404 // Check if this is a constructor functions
405 if let ReturnType::Type(_, ret) = &f.output {
406 if match_generic_type(ret, "VBox", &vtable_name) {
407 // Change VBox<VTable> to Self
408 sig.output = parse_str("-> Self").unwrap();
409 wrap_trait_call = Some(quote! {
410 let wrap_trait_call = |x| unsafe {
411 // Put the object on the heap and get a pointer to it
412 let ptr = core::ptr::NonNull::from(Box::leak(Box::new(x)));
413 VBox::<#vtable_name>::from_raw(vtable, ptr.cast())
414 };
415 wrap_trait_call
416 });
417 }
418 }
419 }
420
421 if ident == "drop" {
422 vtable_ctor.push(quote!(#ident: {
423 #sig_extern {
424 unsafe {
425 ::core::mem::drop(Box::from_raw((#self_call).0 as *mut _));
426 }
427 }
428 #ident::<T>
429 },));
430
431 drop_impls.push(quote! {
432 unsafe impl VTableMetaDrop for #vtable_name {
433 unsafe fn drop(ptr: *mut #to_name) {
434 // Safety: The vtable is valid and inner is a type corresponding to the vtable,
435 // which was allocated such that drop is expected.
436 unsafe {
437 let (vtable, ptr) = ((*ptr).vtable, (*ptr).ptr);
438 (vtable.as_ref().#ident)(VRefMut::from_raw(vtable, ptr)) }
439 }
440 fn new_box<X: HasStaticVTable<#vtable_name>>(value: X) -> VBox<#vtable_name> {
441 // Put the object on the heap and get a pointer to it
442 let ptr = core::ptr::NonNull::from(Box::leak(Box::new(value)));
443 unsafe { VBox::from_raw(core::ptr::NonNull::from(X::static_vtable()), ptr.cast()) }
444 }
445 }
446 });
447 continue;
448 }
449
450 if ident == "drop_in_place" {
451 vtable_ctor.push(quote!(#ident: {
452 #[allow(unsafe_code)]
453 #sig_extern {
454 #[allow(unused_unsafe)]
455 unsafe { ::core::ptr::drop_in_place((#self_call).0 as *mut T) };
456 ::core::alloc::Layout::new::<T>().into()
457 }
458 #ident::<T>
459 },));
460
461 drop_impls.push(quote! {
462 #[allow(unsafe_code)]
463 unsafe impl VTableMetaDropInPlace for #vtable_name {
464 unsafe fn #ident(vtable: &Self::VTable, ptr: *mut u8) -> vtable::Layout {
465 // Safety: The vtable is valid and ptr is a type corresponding to the vtable,
466 (vtable.#ident)(VRefMut::from_raw(core::ptr::NonNull::from(vtable), core::ptr::NonNull::new_unchecked(ptr).cast()))
467 }
468 unsafe fn dealloc(vtable: &Self::VTable, ptr: *mut u8, layout: vtable::Layout) {
469 (vtable.dealloc)(vtable, ptr, layout)
470 }
471 }
472 });
473 continue;
474 }
475 if ident == "dealloc" {
476 vtable_ctor.push(quote!(#ident: {
477 #[allow(unsafe_code)]
478 unsafe extern "C" fn #ident(_: &#vtable_name, ptr: *mut u8, layout: vtable::Layout) {
479 use ::core::convert::TryInto;
480 vtable::internal::dealloc(ptr, layout.try_into().unwrap())
481 }
482 #ident
483 },));
484 continue;
485 }
486
487 generated_trait.items.push(TraitItem::Fn(TraitItemFn {
488 attrs: field.attrs.clone(),
489 sig: sig.clone(),
490 default: None,
491 semi_token: Some(Default::default()),
492 }));
493
494 generated_to_fn_trait.push(ImplItemFn {
495 attrs: field.attrs.clone(),
496 vis: Visibility::Public(Default::default()),
497 defaultness: None,
498 sig: sig.clone(),
499 block: if has_self {
500 parse_quote!({
501 // Safety: this rely on the vtable being valid, and the ptr being a valid instance for this vtable
502 #[allow(unsafe_code)]
503 unsafe {
504 let vtable = self.vtable.as_ref();
505 if let #some(func) = vtable.#ident {
506 func (#call_code)
507 } else {
508 panic!("Called a not-implemented method")
509 }
510 }
511 })
512 } else {
513 // This should never happen: nobody should be able to access the Trait Object directly.
514 parse_quote!({ panic!("Calling Sized method on a Trait Object") })
515 },
516 });
517
518 if !has_self {
519 sig.inputs.insert(
520 0,
521 FnArg::Receiver(Receiver {
522 attrs: Default::default(),
523 reference: Some(Default::default()),
524 mutability: None,
525 self_token: Default::default(),
526 colon_token: None,
527 ty: Box::new(parse_quote!(&Self)),
528 }),
529 );
530 sig.output = sig_extern.output.clone();
531 generated_type_assoc_fn.push(ImplItemFn {
532 attrs: field.attrs.clone(),
533 vis: generated_trait.vis.clone(),
534 defaultness: None,
535 sig,
536 block: parse_quote!({
537 let vtable = self;
538 // Safety: this rely on the vtable being valid, and the ptr being a valid instance for this vtable
539 #[allow(unsafe_code)]
540 unsafe { (self.#ident)(#call_code) }
541 }),
542 });
543
544 vtable_ctor.push(quote!(#ident: {
545 #sig_extern {
546 // This is safe since the self must be a instance of our type
547 #[allow(unused)]
548 #[allow(unsafe_code)]
549 let vtable = unsafe { core::ptr::NonNull::from(&*_0) };
550 #wrap_trait_call(T::#ident(#self_call #forward_code))
551 }
552 #some(#ident::<T>)
553 },));
554 } else {
555 let erase_return_type_lifetime = match &sig_extern.output {
556 ReturnType::Default => quote!(),
557 // If the return type contains a implicit lifetime, it is safe to erase it while returning it
558 // because a sound implementation of the trait wouldn't allow unsound things here
559 ReturnType::Type(_, r) => {
560 quote!(#[allow(clippy::useless_transmute)] core::mem::transmute::<#r, #r>)
561 }
562 };
563 vtable_ctor.push(quote!(#ident: {
564 #sig_extern {
565 // This is safe since the self must be a instance of our type
566 #[allow(unsafe_code)]
567 unsafe { #erase_return_type_lifetime(T::#ident(#self_call #forward_code)) }
568 }
569 #ident::<T>
570 },));
571 }
572 } else {
573 // associated constant
574
575 let generated_trait_assoc_const =
576 generated_trait_assoc_const.get_or_insert_with(|| ItemTrait {
577 attrs: Attribute::parse_outer.parse_str(&format!(
578 "/** Trait containing the associated constant relative to the trait {trait_name}.\n{additional_doc} */",
579 )).unwrap(),
580 ident: quote::format_ident!("{}Consts", trait_name),
581 items: vec![],
582 ..generated_trait.clone()
583 });
584
585 let const_type = if let Some(o) = field
586 .attrs
587 .iter()
588 .position(|a| a.path().get_ident().map(|a| a == "field_offset").unwrap_or(false))
589 {
590 let a = field.attrs.remove(o);
591 let member_type = match a.parse_args::<Type>() {
592 Err(e) => return e.to_compile_error().into(),
593 Ok(ty) => ty,
594 };
595
596 match &field.ty {
597 Type::Path(p) if p.path.get_ident().map(|i| i == "usize").unwrap_or(false) => {}
598 ty => {
599 return Error::new(
600 ty.span(),
601 "The type of an #[field_offset] member in the vtable must be 'usize'",
602 )
603 .to_compile_error()
604 .into()
605 }
606 }
607
608 // add `: Sized` to the trait in case it does not have it
609 if generated_trait_assoc_const.supertraits.is_empty() {
610 generated_trait_assoc_const.colon_token = Some(Default::default());
611 generated_trait_assoc_const.supertraits.push(parse_quote!(Sized));
612 }
613
614 let offset_type: Type = parse_quote!(vtable::FieldOffset<Self, #member_type>);
615
616 vtable_ctor.push(quote!(#ident: T::#ident.get_byte_offset(),));
617
618 let attrs = &field.attrs;
619
620 let vis = &field.vis;
621 generated_to_fn_trait.push(
622 parse_quote! {
623 #(#attrs)*
624 #vis fn #ident(&self) -> &#member_type {
625 unsafe {
626 &*(self.ptr.as_ptr().add(self.vtable.as_ref().#ident) as *const #member_type)
627 }
628 }
629 },
630 );
631 let ident_mut = quote::format_ident!("{}_mut", ident);
632 generated_to_fn_trait.push(
633 parse_quote! {
634 #(#attrs)*
635 #vis fn #ident_mut(&mut self) -> &mut #member_type {
636 unsafe {
637 &mut *(self.ptr.as_ptr().add(self.vtable.as_ref().#ident) as *mut #member_type)
638 }
639 }
640 },
641 );
642
643 offset_type
644 } else {
645 vtable_ctor.push(quote!(#ident: T::#ident,));
646 field.ty.clone()
647 };
648
649 generated_trait_assoc_const.items.push(TraitItem::Const(TraitItemConst {
650 attrs: field.attrs.clone(),
651 const_token: Default::default(),
652 ident: ident.clone(),
653 colon_token: Default::default(),
654 ty: const_type,
655 default: None,
656 semi_token: Default::default(),
657 generics: Default::default(),
658 }));
659 };
660 }
661
662 let vis = input.vis;
663 input.vis = Visibility::Public(Default::default());
664
665 let new_trait_extra = generated_trait_assoc_const.as_ref().map(|x| {
666 let i = &x.ident;
667 quote!(+ #i)
668 });
669
670 let static_vtable_macro_doc = format!(
671 r"Instantiate a static {vtable} for a given type and implements `vtable::HasStaticVTable<{vtable}>` for it.
672
673```ignore
674// The preview above is misleading because of rust-lang/rust#45939, so it is reproduced below
675macro_rules! {macro} {{
676 ($(#[$meta:meta])* $vis:vis static $ident:ident for $ty:ty) => {{ ... }}
677}}
678```
679
680Given a type `MyType` that implements the trait `{trait} {trait_extra}`,
681create a static variable of type {vtable},
682and implements HasStaticVTable for it.
683
684```ignore
685 struct Foo {{ ... }}
686 impl {trait} for Foo {{ ... }}
687 {macro}!(static FOO_VTABLE for Foo);
688 // now VBox::new can be called
689 let vbox = VBox::new(Foo{{ ... }});
690```
691
692 {extra}",
693 vtable = vtable_name,
694 trait = trait_name,
695 trait_extra = new_trait_extra.as_ref().map(|x| x.to_string()).unwrap_or_default(),
696 macro = static_vtable_macro_name,
697 extra = additional_doc,
698 );
699
700 let result = quote!(
701 #[allow(non_snake_case)]
702 #[macro_use]
703 /// This private module is generated by the `vtable` macro
704 mod #module_name {
705 #![allow(unused_parens)]
706 #[allow(unused)]
707 use super::*;
708 use ::vtable::*;
709 use ::vtable::internal::*;
710 #input
711
712 impl #vtable_name {
713 // unfortunately cannot be const in stable rust because of the bounds (depends on rfc 2632)
714 /// Create a vtable suitable for a given type implementing the trait.
715 pub /*const*/ fn new<T: #trait_name #new_trait_extra>() -> Self {
716 Self {
717 #(#vtable_ctor)*
718 }
719 }
720 #(#generated_type_assoc_fn)*
721 }
722
723 #generated_trait
724 #generated_trait_assoc_const
725
726 /// Invariant, same as vtable::Inner: vtable and ptr has to be valid and ptr an instance matching the vtable
727 #[doc(hidden)]
728 #[repr(C)]
729 pub struct #to_name {
730 vtable: core::ptr::NonNull<#vtable_name>,
731 ptr: core::ptr::NonNull<u8>,
732 }
733 impl #to_name {
734 #(#generated_to_fn_trait)*
735
736 /// Returns a reference to the VTable
737 pub fn get_vtable(&self) -> &#vtable_name {
738 unsafe { self.vtable.as_ref() }
739 }
740
741 /// Return a raw pointer to the object
742 pub fn as_ptr(&self) -> *const u8 {
743 self.ptr.as_ptr()
744 }
745 }
746
747 unsafe impl VTableMeta for #vtable_name {
748 type VTable = #vtable_name;
749 type Target = #to_name;
750 }
751
752 #(#drop_impls)*
753
754 #[macro_export]
755 #[doc = #static_vtable_macro_doc]
756 macro_rules! #static_vtable_macro_name {
757 ($(#[$meta:meta])* $vis:vis static $ident:ident for $ty:ty) => {
758 $(#[$meta])* $vis static $ident : #vtable_name = {
759 use vtable::*;
760 type T = $ty;
761 #vtable_name {
762 #(#vtable_ctor)*
763 }
764 };
765 #[allow(unsafe_code)]
766 unsafe impl vtable::HasStaticVTable<#vtable_name> for $ty {
767 fn static_vtable() -> &'static #vtable_name {
768 &$ident
769 }
770 }
771 }
772 }
773 }
774 #[doc(inline)]
775 #[macro_use]
776 #vis use #module_name::*;
777 );
778 //println!("{}", result);
779 result.into()
780}
781