derives/
lib.rs

1use proc_macro::TokenStream;
2use proc_macro2::Span;
3use quote::*;
4use syn::{parse_macro_input, spanned::Spanned, Expr};
5
6macro_rules! try_compile_error {
7    ($x: expr) => {
8        match $x {
9            Ok(x) => x,
10            Err(e) => return e.into_compile_error().into(),
11        }
12    };
13}
14
15mod promote;
16mod vulkan_structure;
17
18fn find_vkhandle_source(fields: &syn::Fields) -> (proc_macro2::TokenStream, &syn::Type) {
19    #[inline]
20    fn has_handle_attribute(f: &syn::Field) -> bool {
21        f.attrs
22            .iter()
23            .any(|a| matches!(a.meta, syn::Meta::Path(ref path) if path.is_ident("handle")))
24    }
25
26    match fields {
27        syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed, .. }) => match unnamed.iter().position(has_handle_attribute)
28        {
29            Some(n) => (quote! { self.#n }, &unnamed[n].ty),
30            None => (quote! { self.0 }, &unnamed[0].ty),
31        },
32        syn::Fields::Named(syn::FieldsNamed { named, .. }) => {
33            let Some(f) = named.iter().find(|&f| has_handle_attribute(f)) else {
34                panic!("Named fields struct must have one field that marked by #[handle]");
35            };
36            let n = f.ident.as_ref().unwrap();
37
38            (quote! { self.#n }, &f.ty)
39        }
40        syn::Fields::Unit => panic!("Unit struct cannot auto-derive VkHandle"),
41    }
42}
43
44fn find_parent_field(fields: &syn::Fields) -> (usize, &syn::Field) {
45    #[inline]
46    fn has_parent_attribute(f: &syn::Field) -> bool {
47        f.attrs
48            .iter()
49            .any(|a| matches!(a.meta, syn::Meta::Path(ref path) if path.is_ident("parent")))
50    }
51
52    match fields {
53        syn::Fields::Named(ref n) => {
54            let mut parents = n.named.iter().enumerate().filter(|(_, n)| has_parent_attribute(n));
55
56            let Some(p) = parents.next() else {
57                panic!("could not find parent field");
58            };
59            if parents.next().is_some() {
60                panic!("too many parent fields");
61            }
62
63            p
64        }
65        syn::Fields::Unnamed(ref u) => {
66            let mut parents = u.unnamed.iter().enumerate().filter(|(_, n)| has_parent_attribute(n));
67
68            let Some(p) = parents.next() else {
69                panic!("could not find parent field");
70            };
71            if parents.next().is_some() {
72                panic!("too many parent fields");
73            }
74
75            p
76        }
77        syn::Fields::Unit => panic!("unit structure has no parent field"),
78    }
79}
80
81#[proc_macro_derive(VkHandle, attributes(handle))]
82pub fn derive_handle(tok: TokenStream) -> TokenStream {
83    let input = parse_macro_input!(tok as syn::DeriveInput);
84    let name = &input.ident;
85    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
86    let syn::Data::Struct(syn::DataStruct { ref fields, .. }) = input.data else {
87        panic!("AutoDerive VkHandle can only be applied for structs");
88    };
89    let (handle_field_ref, handle_ty) = find_vkhandle_source(fields);
90
91    quote! {
92        impl #impl_generics crate::VkHandle for #name #ty_generics #where_clause {
93            type Handle = #handle_ty;
94
95            #[inline(always)]
96            fn native_ptr(&self) -> Self::Handle {
97                #handle_field_ref
98            }
99        }
100        impl #impl_generics crate::VkHandleMut for #name #ty_generics #where_clause {
101            #[inline(always)]
102            fn native_ptr_mut(&mut self) -> Self::Handle {
103                #handle_field_ref
104            }
105        }
106    }
107    .into()
108}
109
110struct VkObjectAttributes {
111    span: Span,
112    r#type: Option<Expr>,
113}
114impl VkObjectAttributes {
115    #[inline]
116    pub fn find_and_parse(input: &syn::DeriveInput) -> syn::Result<Option<Self>> {
117        input
118            .attrs
119            .iter()
120            .find(|a| a.path().is_ident("VkObject"))
121            .map(Self::parse)
122            .transpose()
123    }
124
125    pub fn parse(attr: &syn::Attribute) -> syn::Result<Self> {
126        let mut r#type = None;
127        attr.parse_nested_meta(|meta| {
128            if meta.path.is_ident("type") {
129                r#type = Some(meta.value()?.parse::<Expr>()?);
130                return Ok(());
131            }
132
133            Err(meta.error("unkown attribute"))
134        })?;
135
136        Ok(Self {
137            span: attr.span(),
138            r#type,
139        })
140    }
141}
142
143#[proc_macro_derive(VkObject, attributes(VkObject))]
144pub fn derive_object(tok: TokenStream) -> TokenStream {
145    let input = parse_macro_input!(tok as syn::DeriveInput);
146    let name = &input.ident;
147    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
148
149    let object_attr = match VkObjectAttributes::find_and_parse(&input) {
150        Ok(Some(x)) => x,
151        Ok(None) => {
152            return syn::Error::new(Span::call_site(), "VkObject attribute required")
153                .into_compile_error()
154                .into()
155        }
156        Err(e) => return e.into_compile_error().into(),
157    };
158    let Some(object_type) = object_attr.r#type else {
159        return syn::Error::new(object_attr.span, "No type specified")
160            .into_compile_error()
161            .into();
162    };
163
164    quote! {
165        impl #impl_generics crate::VkObject for #name #ty_generics #where_clause {
166            const TYPE: crate::vk::VkObjectType = #object_type;
167        }
168    }
169    .into()
170}
171
172#[proc_macro_derive(InstanceChild, attributes(parent))]
173pub fn derive_instance_child(tok: TokenStream) -> TokenStream {
174    let input = parse_macro_input!(tok as syn::DeriveInput);
175    let name = &input.ident;
176    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
177
178    let syn::Data::Struct(ref s) = input.data else {
179        return syn::Error::new(Span::call_site(), "No type except structure can derive InstanceChild")
180            .into_compile_error()
181            .into();
182    };
183    let (parent_index, parent_field) = find_parent_field(&s.fields);
184    let parent_ty = &parent_field.ty;
185    let parent_field = match parent_field.ident {
186        Some(ref f) => quote! { &self.#f },
187        None => {
188            let x = syn::Index::from(parent_index);
189            quote! { &self.#x }
190        }
191    };
192
193    quote! {
194        impl #impl_generics crate::InstanceChild for #name #ty_generics #where_clause {
195            type ConcreteInstance = #parent_ty;
196
197            fn instance(&self) -> &Self::ConcreteInstance { #parent_field }
198        }
199    }
200    .into()
201}
202
203#[proc_macro_derive(InstanceChildTransferrable, attributes(parent))]
204pub fn derive_instance_child_transferrable(tok: TokenStream) -> TokenStream {
205    let input = parse_macro_input!(tok as syn::DeriveInput);
206    let name = &input.ident;
207    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
208    let syn::Data::Struct(ref s) = input.data else {
209        panic!("no type except structure can derive InstanceChild");
210    };
211    let (parent_index, parent_field) = find_parent_field(&s.fields);
212    let parent_field = match parent_field.ident {
213        Some(ref f) => quote! { self.#f },
214        None => {
215            let x = syn::Index::from(parent_index);
216            quote! { self.#x }
217        }
218    };
219
220    quote! {
221        impl #impl_generics crate::InstanceChildTransferrable for #name #ty_generics #where_clause {
222            fn transfer_instance(self) -> Self::ConcreteInstance { #parent_field }
223        }
224    }
225    .into()
226}
227
228#[proc_macro_derive(DeviceChild, attributes(parent))]
229pub fn derive_device_child(tok: TokenStream) -> TokenStream {
230    let input = parse_macro_input!(tok as syn::DeriveInput);
231    let name = &input.ident;
232    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
233    let syn::Data::Struct(ref s) = input.data else {
234        panic!("no type except structure can derive DeviceChild");
235    };
236    let (parent_index, parent_field) = find_parent_field(&s.fields);
237    let parent_ty = &parent_field.ty;
238    let parent_field = match parent_field.ident {
239        Some(ref f) => quote! { &self.#f },
240        None => {
241            let x = syn::Index::from(parent_index);
242            quote! { &self.#x }
243        }
244    };
245
246    quote! {
247        impl #impl_generics crate::DeviceChild for #name #ty_generics #where_clause {
248            type ConcreteDevice = #parent_ty;
249
250            fn device(&self) -> &Self::ConcreteDevice { #parent_field }
251        }
252    }
253    .into()
254}
255
256#[proc_macro_derive(DeviceChildTransferrable, attributes(parent))]
257pub fn derive_device_child_transferrable(tok: TokenStream) -> TokenStream {
258    let input = parse_macro_input!(tok as syn::DeriveInput);
259    let name = &input.ident;
260    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
261    let syn::Data::Struct(ref s) = input.data else {
262        panic!("no type except structure can derive DeviceChild");
263    };
264    let (parent_index, parent_field) = find_parent_field(&s.fields);
265    let parent_field = match parent_field.ident {
266        Some(ref f) => quote! { self.#f },
267        None => {
268            let x = syn::Index::from(parent_index);
269            quote! { self.#x }
270        }
271    };
272
273    quote! {
274        impl #impl_generics crate::DeviceChildTransferrable for #name #ty_generics #where_clause {
275            fn transfer_device(self) -> Self::ConcreteDevice { #parent_field }
276        }
277    }
278    .into()
279}
280
281#[proc_macro_derive(TypedVulkanStructure, attributes(VulkanStructure))]
282#[inline(always)]
283pub fn derive_vulkan_structure(input: TokenStream) -> TokenStream {
284    vulkan_structure::derive(input)
285}
286
287#[proc_macro_derive(TypedVulkanSinkStructure, attributes(VulkanSinkStructure))]
288#[inline(always)]
289pub fn derive_vulkan_sink_structure(input: TokenStream) -> TokenStream {
290    vulkan_structure::derive_sink(input)
291}
292
293#[proc_macro_attribute]
294#[inline(always)]
295pub fn promote_1_1(args: TokenStream, item: TokenStream) -> TokenStream {
296    promote::core(
297        parse_macro_input!(item as syn::Item),
298        args,
299        syn::LitStr::new("Allow1_1APIs", Span::call_site()),
300    )
301}
302#[proc_macro_attribute]
303#[inline(always)]
304pub fn promote_1_2(args: TokenStream, item: TokenStream) -> TokenStream {
305    promote::core(
306        parse_macro_input!(item as syn::Item),
307        args,
308        syn::LitStr::new("Allow1_2APIs", Span::call_site()),
309    )
310}
311#[proc_macro_attribute]
312#[inline(always)]
313pub fn promote_1_3(args: TokenStream, item: TokenStream) -> TokenStream {
314    promote::core(
315        parse_macro_input!(item as syn::Item),
316        args,
317        syn::LitStr::new("Allow1_3APIs", Span::call_site()),
318    )
319}
320#[proc_macro_attribute]
321#[inline(always)]
322pub fn promote_1_4(args: TokenStream, item: TokenStream) -> TokenStream {
323    promote::core(
324        parse_macro_input!(item as syn::Item),
325        args,
326        syn::LitStr::new("Allow1_4APIs", Span::call_site()),
327    )
328}
329
330#[inline]
331fn newtype_struct_org_type(d: &syn::DataStruct) -> syn::Result<&syn::Type> {
332    match d.fields {
333        syn::Fields::Unnamed(ref f) if f.unnamed.len() == 1 => Ok(&unsafe { f.unnamed.first().unwrap_unchecked() }.ty),
334        _ => Err(syn::Error::new_spanned(&d.fields, "not a newtype struct")),
335    }
336}
337
338#[proc_macro_attribute]
339pub fn vk_raw_handle(args: TokenStream, input: TokenStream) -> TokenStream {
340    let input = parse_macro_input!(input as syn::DeriveInput);
341    let name = &input.ident;
342    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
343    let syn::Data::Struct(ref s) = input.data else {
344        panic!("cannot derive VkRawHandle from this type");
345    };
346
347    let mut object_type = None::<Expr>;
348    let parser = syn::meta::parser(|p| {
349        if p.path.is_ident("object_type") {
350            object_type = Some(p.value()?.parse()?);
351            Ok(())
352        } else {
353            Err(p.error("unknown attr"))
354        }
355    });
356    parse_macro_input!(args with parser);
357
358    let dispatchable = matches!(try_compile_error!(newtype_struct_org_type(s)), syn::Type::Ptr(_));
359    let (null_def, raw_handle_conversion);
360    if dispatchable {
361        null_def = quote! { Self(std::ptr::null_mut()) };
362        raw_handle_conversion = quote! { self.0 as usize as _ };
363    } else {
364        null_def = quote! { Self(0) };
365        raw_handle_conversion = quote! { self.0 };
366    }
367
368    quote! {
369        #input
370        impl #impl_generics crate::handle::VkRawHandle for #name #ty_generics #where_clause {
371            const OBJECT_TYPE: VkObjectType = #object_type;
372            const NULL: Self = #null_def;
373
374            #[inline]
375            fn raw_handle_value(&self) -> u64 {
376                #raw_handle_conversion
377            }
378        }
379    }
380    .into()
381}
382
383#[proc_macro_derive(PFN, attributes(pfn_of))]
384pub fn derive_pfn(input: TokenStream) -> TokenStream {
385    let input = parse_macro_input!(input as syn::DeriveInput);
386    let impl_name = &input.ident;
387
388    let Some(org_attr) = input.attrs.iter().find_map(|a| match a.meta {
389        syn::Meta::List(ref ml) if ml.path.is_ident("pfn_of") => Some(ml),
390        _ => None,
391    }) else {
392        return syn::Error::new_spanned(&input, "no #[pfn_of(...)] found")
393            .into_compile_error()
394            .into();
395    };
396    let org_fn: syn::Path = try_compile_error!(org_attr.parse_args());
397    let Some(org_fn_name) = org_fn.get_ident().or_else(|| org_fn.segments.last().map(|l| &l.ident)) else {
398        return syn::Error::new_spanned(org_attr, "invalid pfn_of fn path")
399            .into_compile_error()
400            .into();
401    };
402    let org_fn_cstr = syn::LitCStr::new(
403        &unsafe { std::ffi::CString::from_vec_unchecked(org_fn_name.to_string().into_bytes()) },
404        org_fn.span(),
405    );
406
407    quote! {
408        unsafe impl crate::resolver::PFN for #impl_name {
409            const NAME_CSTR: &'static core::ffi::CStr = #org_fn_cstr;
410
411            #[inline(always)]
412            unsafe fn from_ptr(p: *const core::ffi::c_void) -> Self {
413                core::mem::transmute(p)
414            }
415            #[inline(always)]
416            unsafe fn from_void_fn(p: crate::vk::PFN_vkVoidFunction) -> Self {
417                core::mem::transmute(p)
418            }
419        }
420    }
421    .into()
422}
423
424#[proc_macro_derive(StaticCallable)]
425pub fn derive_static_callable(input: TokenStream) -> TokenStream {
426    let input = parse_macro_input!(input as syn::DeriveInput);
427    let impl_name = &input.ident;
428    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
429    let org_attr = &input
430        .attrs
431        .iter()
432        .filter_map(|a| a.meta.require_list().ok())
433        .find(|a| a.path.is_ident("pfn_of"))
434        .expect("no #[pfn_of] found");
435    let org_fn: syn::Path = org_attr.parse_args().expect("invalid arg for pfn_of");
436
437    quote! {
438        #[cfg(all(not(feature = "DynamicLoaded"), feature = "Implements"))]
439        impl #impl_generics crate::resolver::StaticCallable for #impl_name #ty_generics #where_clause {
440            const STATIC: Self = Self(#org_fn);
441        }
442    }
443    .into()
444}
445
446/// alias for `#[cfg(feature = "Implements")]`, with additional feature requirements
447#[proc_macro_attribute]
448pub fn implements(args: TokenStream, target: TokenStream) -> TokenStream {
449    let mut out: TokenStream = quote! { #[cfg(feature = "Implements")] }.into();
450
451    if !args.is_empty() {
452        let extra_feature_requirements =
453            parse_macro_input!(args with syn::punctuated::Punctuated<syn::LitStr, syn::Token![,]>::parse_terminated);
454
455        out.extend(
456            extra_feature_requirements
457                .into_iter()
458                .flat_map(|x| TokenStream::from(quote! { #[cfg(feature = #x)] })),
459        );
460    }
461
462    out.extend(target);
463    out
464}
465
466#[proc_macro_attribute]
467pub fn bitflags_newtype(_args: TokenStream, target: TokenStream) -> TokenStream {
468    let input = parse_macro_input!(target as syn::DeriveInput);
469    let name = &input.ident;
470    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
471
472    let syn::Data::Struct(ref s) = input.data else {
473        return syn::Error::new(input.span(), "cannot use as bitflags struct")
474            .into_compile_error()
475            .into();
476    };
477    let org_type = try_compile_error!(newtype_struct_org_type(s));
478
479    quote! {
480        #[repr(transparent)]
481        #input
482        impl #impl_generics core::ops::BitOr for #name #ty_generics #where_clause {
483            type Output = Self;
484
485            #[inline(always)]
486            fn bitor(self, rhs: Self) -> Self {
487                Self(self.0 | rhs.0)
488            }
489        }
490        impl #impl_generics core::ops::BitAnd for #name #ty_generics #where_clause {
491            type Output = Self;
492
493            #[inline(always)]
494            fn bitand(self, rhs: Self) -> Self {
495                Self(self.0 & rhs.0)
496            }
497        }
498        impl #impl_generics core::ops::Not for #name #ty_generics #where_clause {
499            type Output = Self;
500
501            #[inline(always)]
502            fn not(self) -> Self {
503                Self(!self.0)
504            }
505        }
506        impl #impl_generics core::ops::BitOrAssign for #name #ty_generics #where_clause {
507            #[inline(always)]
508            fn bitor_assign(&mut self, rhs: Self) {
509                self.0 |= rhs.0;
510            }
511        }
512        impl #impl_generics core::ops::BitAndAssign for #name #ty_generics #where_clause {
513            #[inline(always)]
514            fn bitand_assign(&mut self, rhs: Self) {
515                self.0 &= rhs.0;
516            }
517        }
518
519        impl #impl_generics From<#name #ty_generics> for #org_type #where_clause {
520            #[inline(always)]
521            fn from(value: #name #ty_generics) -> Self {
522                value.0
523            }
524        }
525
526        impl #impl_generics #name #ty_generics #where_clause {
527            /// Returns bits in this flags
528            #[inline(always)]
529            pub const fn bits(&self) -> #org_type {
530                self.0
531            }
532
533            /// Returns true if any of specified bits are contained in this flag.
534            #[inline(always)]
535            pub const fn has_any(self, other: Self) -> bool {
536                (self.0 & other.0) != 0
537            }
538
539            /// Returns true if all of specified bits are contained in this flag.
540            #[inline(always)]
541            pub const fn has_all(self, other: Self) -> bool {
542                (self.0 & other.0) == other.0
543            }
544
545            /// merge two flags (const alias of BitOr)
546            #[inline(always)]
547            pub const fn merge(self, other: Self) -> Self {
548                Self(self.0 | other.0)
549            }
550        }
551    }
552    .into()
553}
554
555struct VkExtCommandInput {
556    span: Span,
557    base_define: syn::ForeignItemFn,
558    suffix: Option<syn::LitStr>,
559    promote: Option<syn::LitStr>,
560    static_callable: bool,
561}
562impl syn::parse::Parse for VkExtCommandInput {
563    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
564        let span = input.span();
565        let base_define = syn::ForeignItemFn::parse(input)?;
566
567        let (mut suffix, mut promote, mut static_callable) = (None, None, false);
568        while input.peek(syn::Ident) {
569            let extra_ident = syn::Ident::parse(input)?;
570
571            match &extra_ident.to_string() as &str {
572                "suffix" => {
573                    if suffix.is_some() {
574                        return Err(syn::Error::new(input.span(), "duplicated suffix extra"));
575                    }
576
577                    input.parse::<syn::Token![=]>()?;
578                    suffix = Some(input.parse()?);
579                    input.parse::<syn::Token![;]>()?;
580                }
581                "promote" => {
582                    if promote.is_some() {
583                        return Err(syn::Error::new(input.span(), "duplicated promote extra"));
584                    }
585
586                    input.parse::<syn::Token![=]>()?;
587                    promote = Some(input.parse()?);
588                    input.parse::<syn::Token![;]>()?;
589                }
590                "static_callable" => {
591                    input.parse::<syn::Token![;]>()?;
592
593                    static_callable = true;
594                }
595                unknown => return Err(syn::Error::new(input.span(), &format!("unknown extra: {unknown}"))),
596            }
597        }
598
599        Ok(Self {
600            span,
601            base_define,
602            suffix,
603            promote,
604            static_callable,
605        })
606    }
607}
608
609#[proc_macro]
610pub fn vk_ext_command(input: TokenStream) -> TokenStream {
611    let input = parse_macro_input!(input as VkExtCommandInput);
612
613    let base_vis = &input.base_define.vis;
614    let pfn_name = syn::Ident::new(
615        &format!("PFN_{}", input.base_define.sig.ident.to_string()),
616        input.base_define.sig.ident.span(),
617    );
618    let pfn_ty = syn::TypeBareFn {
619        lifetimes: None,
620        unsafety: Some(syn::Token![unsafe](Span::call_site())),
621        abi: Some(syn::Abi {
622            extern_token: syn::Token![extern](Span::call_site()),
623            name: Some(syn::LitStr::new("system", Span::call_site())),
624        }),
625        fn_token: input.base_define.sig.fn_token.clone(),
626        paren_token: input.base_define.sig.paren_token.clone(),
627        inputs: input
628            .base_define
629            .sig
630            .inputs
631            .iter()
632            .map(|a| match a {
633                syn::FnArg::Receiver(_) => unreachable!("vk command cannot have receivers"),
634                syn::FnArg::Typed(t) => syn::BareFnArg {
635                    attrs: t.attrs.clone(),
636                    name: match *t.pat {
637                        syn::Pat::Ident(ref x) => Some((x.ident.clone(), t.colon_token.clone())),
638                        _ => None,
639                    },
640                    ty: *t.ty.clone(),
641                },
642            })
643            .collect(),
644        variadic: input.base_define.sig.variadic.as_ref().map(|v| syn::BareVariadic {
645            attrs: v.attrs.clone(),
646            name: match v.pat {
647                Some((ref p, c)) => match &**p {
648                    &syn::Pat::Ident(ref x) => Some((x.ident.clone(), c.clone())),
649                    _ => None,
650                },
651                _ => None,
652            },
653            dots: v.dots.clone(),
654            comma: v.comma.clone(),
655        }),
656        output: input.base_define.sig.output.clone(),
657    };
658    let fname_cstr = syn::LitCStr::new(
659        &unsafe { std::ffi::CString::from_vec_unchecked(input.base_define.sig.ident.to_string().into_bytes()) },
660        input.base_define.sig.ident.span(),
661    );
662
663    let ext_static_link_impl = if input.static_callable {
664        let fn_ident = &input.base_define.sig.ident;
665        let define = &input.base_define;
666
667        Some(quote! {
668            #[cfg(all(feature = "Implements", not(feature = "DynamicLoaded")))]
669            impl crate::resolver::StaticCallable for #pfn_name {
670                const STATIC: Self = Self(#fn_ident);
671            }
672
673            #[cfg(all(feature = "Implements", not(feature = "DynamicLoaded")))]
674            extern "system" {
675                #define
676            }
677        })
678    } else {
679        None
680    };
681
682    let promoted_pfn_impl = if let Some(ref p) = input.promote {
683        let base_fn_name = input.base_define.sig.ident.to_string();
684        let suffix = try_compile_error!(input
685            .suffix
686            .ok_or_else(|| syn::Error::new(input.span, "suffix extra required for promote")));
687        let Some(promoted_fn_name) = base_fn_name.strip_suffix(&suffix.value()) else {
688            return syn::Error::new_spanned(&input.base_define.sig.ident, "not suffixed")
689                .into_compile_error()
690                .into();
691        };
692        let promoted_fn_ident = syn::Ident::new(promoted_fn_name, input.base_define.sig.ident.span());
693        let pfn_name = syn::Ident::new(&format!("PFN_{promoted_fn_name}"), input.base_define.sig.ident.span());
694        let promoted_fn_cstr = syn::LitCStr::new(
695            &unsafe { std::ffi::CString::from_vec_unchecked(promoted_fn_name.as_bytes().to_vec()) },
696            input.base_define.sig.ident.span(),
697        );
698        let promoted_fn = syn::ForeignItemFn {
699            sig: syn::Signature {
700                ident: promoted_fn_ident.clone(),
701                ..input.base_define.sig.clone()
702            },
703            ..input.base_define.clone()
704        };
705
706        let promote_feature_name = syn::LitStr::new(&format!("Allow{}APIs", p.value().replace('.', "_")), p.span());
707
708        Some(quote! {
709            #[cfg(feature = #promote_feature_name)]
710            #[repr(transparent)]
711            #[derive(Clone, Copy, Debug, PartialEq, Eq)]
712            #base_vis struct #pfn_name(pub #pfn_ty);
713            #[cfg(feature = #promote_feature_name)]
714            unsafe impl crate::resolver::PFN for #pfn_name {
715                const NAME_CSTR: &'static core::ffi::CStr = #promoted_fn_cstr;
716
717                #[inline(always)]
718                unsafe fn from_ptr(p: *const core::ffi::c_void) -> Self {
719                    core::mem::transmute(p)
720                }
721                #[inline(always)]
722                unsafe fn from_void_fn(p: crate::vk::PFN_vkVoidFunction) -> Self {
723                    core::mem::transmute(p)
724                }
725            }
726
727            #[cfg(all(feature = "Implements", feature = #promote_feature_name, not(feature = "DynamicLoaded")))]
728            impl crate::resolver::StaticCallable for #pfn_name {
729                const STATIC: Self = Self(#promoted_fn_ident);
730            }
731
732            #[cfg(all(feature = "Implements", feature = #promote_feature_name, not(feature = "DynamicLoaded")))]
733            extern "system" {
734                #promoted_fn
735            }
736        })
737    } else {
738        None
739    };
740
741    quote! {
742        #[repr(transparent)]
743        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
744        #base_vis struct #pfn_name(pub #pfn_ty);
745        unsafe impl crate::resolver::PFN for #pfn_name {
746            const NAME_CSTR: &'static core::ffi::CStr = #fname_cstr;
747
748            #[inline(always)]
749            unsafe fn from_ptr(p: *const core::ffi::c_void) -> Self {
750                core::mem::transmute(p)
751            }
752            #[inline(always)]
753            unsafe fn from_void_fn(p: crate::vk::PFN_vkVoidFunction) -> Self {
754                core::mem::transmute(p)
755            }
756        }
757
758        #ext_static_link_impl
759        #promoted_pfn_impl
760    }
761    .into()
762}
763
764/// Provides safe implementation for [`SpecializationConstants`] by deriving from structs.
765#[proc_macro_derive(SpecializationConstants, attributes(constant_id))]
766pub fn safe_derive_spec_constant(tok: TokenStream) -> TokenStream {
767    let input = parse_macro_input!(tok as syn::DeriveInput);
768    let name = &input.ident;
769    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
770
771    match input.data {
772        syn::Data::Struct(syn::DataStruct { ref fields, .. }) => {
773            let mut entries = Vec::with_capacity(fields.len());
774            for f in fields {
775                let mut constant_ids = f.attrs.iter().filter_map(|a| match a {
776                    syn::Attribute {
777                        style: syn::AttrStyle::Outer,
778                        meta: syn::Meta::NameValue(ref nv),
779                        ..
780                    } if nv.path.is_ident("constant_id") => Some(Ok(nv.value.clone())),
781                    syn::Attribute {
782                        style: syn::AttrStyle::Outer,
783                        meta: syn::Meta::List(ref ml),
784                        ..
785                    } if ml.path.is_ident("constant_id") => Some(ml.parse_args::<syn::Expr>()),
786                    _ => None,
787                });
788                let Some(first_cid) = constant_ids.next() else {
789                    return syn::Error::new_spanned(f, "Missing constant_id attribute")
790                        .into_compile_error()
791                        .into();
792                };
793                if constant_ids.next().is_some() {
794                    return syn::Error::new_spanned(f, "One or more constant_id attributes found on same field")
795                        .into_compile_error()
796                        .into();
797                }
798                let constant_id = try_compile_error!(first_cid);
799
800                let ty = &f.ty;
801                let ident = &f.ident;
802                entries.push(quote! { bedrock::SpecializationMapEntry {
803                    constantID: #constant_id,
804                    offset: core::mem::offset_of!(Self, #ident) as _,
805                    size: core::mem::size_of::<#ty>(),
806                } });
807            }
808
809            quote! {
810                unsafe impl<#impl_generics> bedrock::SpecializationConstants for #name #ty_generics #where_clause {
811                    const ENTRIES: &'static [bedrock::SpecializationMapEntry] = &[#(#entries),*];
812
813                    #[inline(always)]
814                    fn as_ptr(&self) -> *const core::ffi::c_void {
815                        self as *const _ as _
816                    }
817                }
818            }
819            .into()
820        }
821        _ => unimplemented!("unsupported"),
822    }
823}