From 9c80c7854f24853c1751465f805027e65b524e7e Mon Sep 17 00:00:00 2001 From: lilly-lizard Date: Sat, 6 Jan 2024 21:59:56 +1300 Subject: [PATCH] match generator code to master branch (except for one push_next edit) --- generator/src/lib.rs | 810 ++++++++++++++++++++++++++----------------- 1 file changed, 485 insertions(+), 325 deletions(-) diff --git a/generator/src/lib.rs b/generator/src/lib.rs index a48119d73..174ebbac7 100644 --- a/generator/src/lib.rs +++ b/generator/src/lib.rs @@ -1,5 +1,12 @@ #![recursion_limit = "256"] -#![warn(trivial_casts, trivial_numeric_casts)] +#![warn( + clippy::use_self, + deprecated_in_future, + rust_2018_idioms, + trivial_casts, + trivial_numeric_casts, + unused_qualifications +)] use heck::{ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase}; use itertools::Itertools; @@ -22,6 +29,7 @@ use std::{ borrow::Cow, collections::{BTreeMap, HashMap, HashSet}, fmt::Display, + ops::Not, path::Path, }; use syn::Ident; @@ -197,7 +205,7 @@ struct CParameterType<'a> { reference_type: CReferenceType, } -fn parse_c_type(i: &str) -> IResult<&str, CParameterType> { +fn parse_c_type(i: &str) -> IResult<&str, CParameterType<'_>> { (map( separated_pair( tuple(( @@ -245,7 +253,7 @@ struct CParameter<'a> { /// ```c /// VkSparseImageMemoryRequirements2* pSparseMemoryRequirements /// ``` -fn parse_c_parameter(i: &str) -> IResult<&str, CParameter> { +fn parse_c_parameter(i: &str) -> IResult<&str, CParameter<'_>> { (map( separated_pair( parse_c_type, @@ -294,7 +302,7 @@ pub enum ConstVal { impl ConstVal { pub fn bits(&self) -> u64 { match self { - ConstVal::U64(n) => *n, + Self::U64(n) => *n, _ => panic!("Constval not supported"), } } @@ -304,7 +312,7 @@ pub trait ConstantExt { fn variant_ident(&self, enum_name: &str) -> Ident; fn notation(&self) -> Option<&str>; fn formatted_notation(&self) -> Option> { - static DOC_LINK: Lazy = Lazy::new(|| Regex::new(r#"<<([\w-]+)>>"#).unwrap()); + static DOC_LINK: Lazy = Lazy::new(|| Regex::new(r"<<([\w-]+)>>").unwrap()); self.notation().map(|n| { DOC_LINK.replace( n, @@ -384,27 +392,27 @@ pub enum Constant { impl quote::ToTokens for Constant { fn to_tokens(&self, tokens: &mut TokenStream) { match *self { - Constant::Number(n) => { + Self::Number(n) => { let number = interleave_number('_', 3, &n.to_string()); syn::LitInt::new(&number, Span::call_site()).to_tokens(tokens); } - Constant::Hex(ref s) => { + Self::Hex(ref s) => { let number = interleave_number('_', 4, s); syn::LitInt::new(&format!("0x{number}"), Span::call_site()).to_tokens(tokens); } - Constant::Text(ref text) => text.to_tokens(tokens), - Constant::CExpr(ref expr) => { + Self::Text(ref text) => text.to_tokens(tokens), + Self::CExpr(ref expr) => { let (rem, (_, rexpr)) = parse_cexpr(expr).expect("Unable to parse cexpr"); assert!(rem.is_empty()); tokens.extend(rexpr.parse::()); } - Constant::BitPos(pos) => { + Self::BitPos(pos) => { let value = 1u64 << pos; let bit_string = format!("{value:b}"); let bit_string = interleave_number('_', 4, &bit_string); syn::LitInt::new(&format!("0b{bit_string}"), Span::call_site()).to_tokens(tokens); } - Constant::Alias(ref value) => tokens.extend(quote!(Self::#value)), + Self::Alias(ref value) => tokens.extend(quote!(Self::#value)), } } } @@ -412,9 +420,9 @@ impl quote::ToTokens for Constant { impl quote::ToTokens for ConstVal { fn to_tokens(&self, tokens: &mut TokenStream) { match self { - ConstVal::U32(n) => n.to_tokens(tokens), - ConstVal::U64(n) => n.to_tokens(tokens), - ConstVal::Float(f) => f.to_tokens(tokens), + Self::U32(n) => n.to_tokens(tokens), + Self::U64(n) => n.to_tokens(tokens), + Self::Float(f) => f.to_tokens(tokens), } } } @@ -437,17 +445,17 @@ fn interleave_number(symbol: char, count: usize, n: &str) -> String { impl Constant { pub fn value(&self) -> Option { match *self { - Constant::Number(n) => Some(ConstVal::U64(n as u64)), - Constant::Hex(ref hex) => u64::from_str_radix(hex, 16).ok().map(ConstVal::U64), - Constant::BitPos(pos) => Some(ConstVal::U64(1u64 << pos)), + Self::Number(n) => Some(ConstVal::U64(n as u64)), + Self::Hex(ref hex) => u64::from_str_radix(hex, 16).ok().map(ConstVal::U64), + Self::BitPos(pos) => Some(ConstVal::U64(1u64 << pos)), _ => None, } } pub fn ty(&self) -> CType { match self { - Constant::Number(_) | Constant::Hex(_) => CType::USize, - Constant::CExpr(expr) => { + Self::Number(_) | Self::Hex(_) => CType::USize, + Self::CExpr(expr) => { let (rem, (ty, _)) = parse_cexpr(expr).expect("Unable to parse cexpr"); assert!(rem.is_empty()); ty @@ -458,23 +466,23 @@ impl Constant { pub fn from_extension_enum(constant: &vkxml::ExtensionEnum) -> Option { let number = constant.number.map(Constant::Number); - let hex = constant.hex.as_ref().map(|hex| Constant::Hex(hex.clone())); + let hex = constant.hex.as_ref().map(|hex| Self::Hex(hex.clone())); let bitpos = constant.bitpos.map(Constant::BitPos); let expr = constant .c_expression .as_ref() - .map(|e| Constant::CExpr(e.clone())); + .map(|e| Self::CExpr(e.clone())); number.or(hex).or(bitpos).or(expr) } pub fn from_constant(constant: &vkxml::Constant) -> Self { let number = constant.number.map(Constant::Number); - let hex = constant.hex.as_ref().map(|hex| Constant::Hex(hex.clone())); + let hex = constant.hex.as_ref().map(|hex| Self::Hex(hex.clone())); let bitpos = constant.bitpos.map(Constant::BitPos); let expr = constant .c_expression .as_ref() - .map(|e| Constant::CExpr(e.clone())); + .map(|e| Self::CExpr(e.clone())); number.or(hex).or(bitpos).or(expr).expect("") } @@ -592,12 +600,17 @@ pub trait FieldExt { /// Returns reference-types wrapped in their safe variant. (Dynamic) arrays become /// slices, pointers become Rust references. - fn safe_type_tokens(&self, lifetime: TokenStream, inner_length: Option) -> TokenStream; + fn safe_type_tokens( + &self, + lifetime: TokenStream, + type_lifetime: Option, + inner_length: Option, + ) -> TokenStream; /// Returns the basetype ident and removes the 'Vk' prefix. When `is_ffi_param` is `true` /// array types (e.g. `[f32; 3]`) will be converted to pointer types (e.g. `&[f32; 3]`), /// which is needed for `C` function parameters. Set to `false` for struct definitions. - fn type_tokens(&self, is_ffi_param: bool) -> TokenStream; + fn type_tokens(&self, is_ffi_param: bool, type_lifetime: Option) -> TokenStream; /// Whether this is C's `void` type (not to be mistaken with a void _pointer_!) fn is_void(&self) -> bool; @@ -620,9 +633,9 @@ impl ToTokens for vkxml::ReferenceType { quote!(*mut) }; match self { - vkxml::ReferenceType::Pointer => quote!(#r), - vkxml::ReferenceType::PointerToPointer => quote!(#r *mut), - vkxml::ReferenceType::PointerToConstPointer => quote!(#r *const), + Self::Pointer => quote!(#r), + Self::PointerToPointer => quote!(#r *mut), + Self::PointerToConstPointer => quote!(#r *const), } } @@ -633,9 +646,9 @@ impl ToTokens for vkxml::ReferenceType { quote!(&#lifetime mut) }; match self { - vkxml::ReferenceType::Pointer => quote!(#r), - vkxml::ReferenceType::PointerToPointer => quote!(#r *mut), - vkxml::ReferenceType::PointerToConstPointer => quote!(#r *const), + Self::Pointer => quote!(#r), + Self::PointerToPointer => quote!(#r *mut), + Self::PointerToConstPointer => quote!(#r *const), } } } @@ -733,7 +746,7 @@ fn discard_outmost_delimiter(stream: TokenStream) -> TokenStream { impl FieldExt for vkxml::Field { fn param_ident(&self) -> Ident { - let name = self.name.as_deref().unwrap_or("field"); + let name = self.name.as_deref().unwrap(); let name_corrected = match name { "type" => "ty", _ => name, @@ -762,14 +775,19 @@ impl FieldExt for vkxml::Field { } } - fn safe_type_tokens(&self, lifetime: TokenStream, inner_length: Option) -> TokenStream { + fn safe_type_tokens( + &self, + lifetime: TokenStream, + type_lifetime: Option, + inner_length: Option, + ) -> TokenStream { assert!(!self.is_void()); match self.array { // The outer type fn type_tokens() returns is [], which fits our "safe" prescription - Some(vkxml::ArrayType::Static) => self.type_tokens(false), + Some(vkxml::ArrayType::Static) => self.type_tokens(false, type_lifetime), Some(vkxml::ArrayType::Dynamic) => { let ty = self.inner_type_tokens(Some(lifetime), inner_length); - quote!([#ty]) + quote!([#ty #type_lifetime]) } None => { let ty = name_to_tokens(&self.basetype); @@ -777,12 +795,12 @@ impl FieldExt for vkxml::Field { .reference .as_ref() .map(|r| r.to_safe_tokens(self.is_const, lifetime)); - quote!(#pointer #ty) + quote!(#pointer #ty #type_lifetime) } } } - fn type_tokens(&self, is_ffi_param: bool) -> TokenStream { + fn type_tokens(&self, is_ffi_param: bool, type_lifetime: Option) -> TokenStream { assert!(!self.is_void()); let ty = name_to_tokens(&self.basetype); @@ -801,7 +819,7 @@ impl FieldExt for vkxml::Field { if is_ffi_param { quote!(*const [#ty; #size]) } else { - quote!([#ty; #size]) + quote!([#ty #type_lifetime; #size]) } } _ => { @@ -809,9 +827,9 @@ impl FieldExt for vkxml::Field { if self.is_pointer_to_static_sized_array() { let size = self.c_size.as_ref().expect("Should have c_size"); let size = convert_c_expression(size, &BTreeMap::new()); - quote!(#pointer [#ty; #size]) + quote!(#pointer [#ty #type_lifetime; #size]) } else { - quote!(#pointer #ty) + quote!(#pointer #ty #type_lifetime) } } } @@ -848,16 +866,18 @@ impl FieldExt for vk_parse::CommandParam { fn safe_type_tokens( &self, _lifetime: TokenStream, + _type_lifetime: Option, _inner_length: Option, ) -> TokenStream { unimplemented!() } - fn type_tokens(&self, is_ffi_param: bool) -> TokenStream { + fn type_tokens(&self, is_ffi_param: bool, type_lifetime: Option) -> TokenStream { assert!(!self.is_void(), "{:?}", self); let (rem, ty) = parse_c_parameter(&self.definition.code).unwrap(); assert!(rem.is_empty()); let type_name = name_to_tokens(ty.type_.name); + let type_name = quote!(#type_name #type_lifetime); let inner_ty = match ty.type_.reference_type { CReferenceType::Value => quote!(#type_name), CReferenceType::Pointer => { @@ -895,6 +915,7 @@ fn generate_function_pointers<'a>( commands: &[&'a vk_parse::CommandDefinition], rename_commands: &HashMap<&'a str, &'a str>, fn_cache: &mut HashSet<&'a str>, + has_lifetimes: &HashSet, ) -> TokenStream { // Commands can have duplicates inside them because they are declared per features. But we only // really want to generate one function pointer. @@ -938,7 +959,12 @@ fn generate_function_pointers<'a>( .clone() .map(|param| { let name = param.param_ident(); - let ty = param.type_tokens(true); + let type_lifetime = has_lifetimes + .contains(&name_to_tokens( + param.definition.type_name.as_ref().unwrap(), + )) + .then(|| quote!(<'_>)); + let ty = param.type_tokens(true, type_lifetime); (name, ty) }) .collect(); @@ -1014,7 +1040,7 @@ fn generate_function_pointers<'a>( for validstruct in validstructs { let structname = name_to_tokens(validstruct); - quote!(unsafe impl #param_trait_name for #structname {}).to_tokens(tokens); + quote!(unsafe impl #param_trait_name for #structname<'_> {}).to_tokens(tokens); } } } @@ -1077,6 +1103,21 @@ fn generate_function_pointers<'a>( .to_tokens(tokens) } } + let loaders = commands.iter().map(CommandToLoader); + + let loader = commands.is_empty().not().then(|| { + quote! { + impl #ident { + pub fn load(mut _f: F) -> Self + where F: FnMut(&::std::ffi::CStr) -> *const c_void + { + Self { + #(#loaders,)* + } + } + } + } + }); let param_traits = commands.iter().map(CommandToParamTraits); let pfn_typedefs = commands @@ -1084,29 +1125,28 @@ fn generate_function_pointers<'a>( .filter(|pfn| pfn.type_needs_defining) .map(CommandToType); let members = commands.iter().map(CommandToMember); - let loaders = commands.iter().map(CommandToLoader); + + let struct_contents = if commands.is_empty() { + quote! { pub struct #ident; } + } else { + quote! { + pub struct #ident { + #(#members,)* + } + + unsafe impl Send for #ident {} + unsafe impl Sync for #ident {} + } + }; quote! { #(#param_traits)* #(#pfn_typedefs)* #[derive(Clone)] - pub struct #ident { - #(#members,)* - } - - unsafe impl Send for #ident {} - unsafe impl Sync for #ident {} + #struct_contents - impl #ident { - pub fn load(mut _f: F) -> Self - where F: FnMut(&::std::ffi::CStr) -> *const c_void - { - Self { - #(#loaders,)* - } - } - } + #loader } } pub struct ExtensionConstant<'a> { @@ -1146,7 +1186,7 @@ pub fn generate_extension_constants<'a>( .filter(|(api, _items)| matches!(api.as_deref(), None | Some(DESIRED_API))) .flat_map(|(_api, items)| items); - let mut extended_enums = BTreeMap::>::new(); + let mut extended_enums = BTreeMap::>>::new(); for item in items { if let vk_parse::InterfaceItem::Enum(enum_) = item { @@ -1158,10 +1198,7 @@ pub fn generate_extension_constants<'a>( continue; } - if enum_.deprecated.is_some() - // TODO: Remove deprecated alias on next breaking release - && enum_.name != "VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE_KHR" - { + if enum_.deprecated.is_some() { continue; } @@ -1216,6 +1253,7 @@ pub fn generate_extension_commands<'a>( cmd_map: &CommandMap<'a>, cmd_aliases: &HashMap<&'a str, &'a str>, fn_cache: &mut HashSet<&'a str>, + has_lifetimes: &HashSet, ) -> TokenStream { let mut commands = Vec::new(); let mut rename_commands = HashMap::new(); @@ -1249,7 +1287,13 @@ pub fn generate_extension_commands<'a>( .strip_prefix("Vk") .unwrap() ); - let fp = generate_function_pointers(ident.clone(), &commands, &rename_commands, fn_cache); + let fp = generate_function_pointers( + ident.clone(), + &commands, + &rename_commands, + fn_cache, + has_lifetimes, + ); let spec_version = items .iter() @@ -1269,10 +1313,9 @@ pub fn generate_extension_commands<'a>( let byte_name_ident = Literal::byte_string(format!("{extension_name}\0").as_bytes()); let extension_cstr = quote! { impl #ident { - #[inline] - pub const fn name() -> &'static ::std::ffi::CStr { - unsafe { ::std::ffi::CStr::from_bytes_with_nul_unchecked(#byte_name_ident) } - } + pub const NAME: &'static ::std::ffi::CStr = unsafe { + ::std::ffi::CStr::from_bytes_with_nul_unchecked(#byte_name_ident) + }; #spec_version } }; @@ -1288,6 +1331,7 @@ pub fn generate_extension<'a>( const_values: &mut BTreeMap, cmd_aliases: &HashMap<&'a str, &'a str>, fn_cache: &mut HashSet<&'a str>, + has_lifetimes: &HashSet, ) -> Option { let extension_tokens = generate_extension_constants( &extension.name, @@ -1302,6 +1346,7 @@ pub fn generate_extension<'a>( cmd_map, cmd_aliases, fn_cache, + has_lifetimes, ); let q = quote! { #fp @@ -1452,6 +1497,7 @@ pub fn variant_ident(enum_name: &str, variant_name: &str) -> Ident { "_KHX", "_LUNARG", "_MESA", + "_MSFT", "_MVK", "_NN", "_NV", @@ -1513,19 +1559,10 @@ pub fn bitflags_impl_block( ) -> TokenStream { let variants = constants .iter() - .filter(|constant| { - !constant.is_deprecated() - // TODO: Remove deprecated alias on next breaking release - || constant.variant_ident(enum_name) == "MIRROR_CLAMP_TO_EDGE_KHR" - }) + .filter(|constant| !constant.is_deprecated()) .map(|constant| { let variant_ident = constant.variant_ident(enum_name); - let notation = if variant_ident == "MIRROR_CLAMP_TO_EDGE_KHR" { - let comment = constant.formatted_notation(); - Some(quote!(#[deprecated = #comment])) - } else { - constant.doc_attribute() - }; + let notation = constant.doc_attribute(); let constant = constant.constant(enum_name); let value = if let Constant::Alias(_) = &constant { quote!(#constant) @@ -1635,7 +1672,7 @@ pub fn generate_enum<'a>( } } -pub fn generate_result(ident: Ident, enum_: &vk_parse::Enums) -> TokenStream { +fn generate_result(ident: Ident, enum_: &vk_parse::Enums) -> TokenStream { let notation = enum_.children.iter().filter_map(|elem| { let (variant_name, notation) = match elem { vk_parse::EnumsChild::Enum(constant) => ( @@ -1656,7 +1693,7 @@ pub fn generate_result(ident: Ident, enum_: &vk_parse::Enums) -> TokenStream { quote! { impl ::std::error::Error for #ident {} impl fmt::Display for #ident { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let name = match *self { #(#notation),*, _ => None, @@ -1676,9 +1713,11 @@ pub fn generate_result(ident: Ident, enum_: &vk_parse::Enums) -> TokenStream { fn is_static_array(field: &vkxml::Field) -> bool { matches!(field.array, Some(vkxml::ArrayType::Static)) } -pub fn derive_default( + +fn derive_default( struct_: &vkxml::Struct, - members: &[(&vkxml::Field, Option)], + members: &[PreprocessedMember<'_>], + has_lifetime: bool, ) -> Option { let name = name_to_tokens(&struct_.name); let is_structure_type = |field: &vkxml::Field| field.basetype == "VkStructureType"; @@ -1700,67 +1739,70 @@ pub fn derive_default( ]; let contains_ptr = members .iter() - .cloned() - .any(|(field, _)| field.reference.is_some()); - let contains_structure_type = members.iter().map(|(f, _)| *f).any(is_structure_type); - let contains_static_array = members.iter().map(|(f, _)| *f).any(is_static_array); - let contains_deprecated = members.iter().any(|(_, d)| d.is_some()); + .any(|member| member.vkxml_field.reference.is_some()); + let contains_structure_type = members + .iter() + .any(|member| is_structure_type(member.vkxml_field)); + let contains_static_array = members + .iter() + .any(|member| is_static_array(member.vkxml_field)); + let contains_deprecated = members.iter().any(|member| member.deprecated.is_some()); let allow_deprecated = contains_deprecated.then(|| quote!(#[allow(deprecated)])); if !(contains_ptr || contains_structure_type || contains_static_array) { return None; }; - let default_fields = members.iter().map(|(field, _)| { - let param_ident = field.param_ident(); - if is_structure_type(field) { - if field.type_enums.is_some() { - quote! { - #param_ident: Self::STRUCTURE_TYPE - } + let default_fields = members.iter().map(|member| { + let param_ident = member.vkxml_field.param_ident(); + if is_structure_type(member.vkxml_field) { + if member.vkxml_field.type_enums.is_some() { + quote!(#param_ident: Self::STRUCTURE_TYPE) } else { - quote! { - #param_ident: unsafe { ::std::mem::zeroed() } - } + quote!(#param_ident: unsafe { ::std::mem::zeroed() }) } - } else if field.reference.is_some() { - if field.is_const { + } else if member.vkxml_field.reference.is_some() { + if member.vkxml_field.is_const { quote!(#param_ident: ::std::ptr::null()) } else { quote!(#param_ident: ::std::ptr::null_mut()) } - } else if is_static_array(field) || handles.contains(&field.basetype.as_str()) { - quote! { - #param_ident: unsafe { ::std::mem::zeroed() } - } + } else if is_static_array(member.vkxml_field) + || handles.contains(&member.vkxml_field.basetype.as_str()) + { + quote!(#param_ident: unsafe { ::std::mem::zeroed() }) } else { - let ty = field.type_tokens(false); - quote! { - #param_ident: #ty::default() - } + let ty = member.vkxml_field.type_tokens(false, None); + quote!(#param_ident: #ty::default()) } }); + let lifetime = has_lifetime.then(|| quote!(<'_>)); + let marker = has_lifetime.then(|| quote!(_marker: PhantomData,)); let q = quote! { - impl ::std::default::Default for #name { + impl ::std::default::Default for #name #lifetime { #[inline] fn default() -> Self { #allow_deprecated Self { #( - #default_fields - ),* + #default_fields, + )* + #marker } } } }; Some(q) } -pub fn derive_debug( + +fn derive_debug( struct_: &vkxml::Struct, - members: &[(&vkxml::Field, Option)], + members: &[PreprocessedMember<'_>], union_types: &HashSet<&str>, + has_lifetime: bool, ) -> Option { let name = name_to_tokens(&struct_.name); - let contains_pfn = members.iter().any(|(field, _)| { - field + let contains_pfn = members.iter().any(|member| { + member + .vkxml_field .name .as_ref() .map(|n| n.contains("pfn")) @@ -1768,42 +1810,35 @@ pub fn derive_debug( }); let contains_static_array = members .iter() - .any(|(x, _)| is_static_array(x) && x.basetype == "char"); + .any(|member| is_static_array(member.vkxml_field) && member.vkxml_field.basetype == "char"); let contains_union = members .iter() - .any(|(field, _)| union_types.contains(field.basetype.as_str())); + .any(|member| union_types.contains(member.vkxml_field.basetype.as_str())); if !(contains_union || contains_static_array || contains_pfn) { return None; } - let debug_fields = members.iter().map(|(field, _)| { + let debug_fields = members.iter().map(|member| { + let field = &member.vkxml_field; let param_ident = field.param_ident(); let param_str = param_ident.to_string(); let debug_value = if is_static_array(field) && field.basetype == "char" { - quote! { - &unsafe { - ::std::ffi::CStr::from_ptr(self.#param_ident.as_ptr()) - } - } + let param_ident = format_ident!("{}_as_c_str", param_ident); + quote!(&self.#param_ident()) } else if param_str.contains("pfn") { - quote! { - &(self.#param_ident.map(|x| x as *const ())) - } + quote!(&(self.#param_ident.map(|x| x as *const ()))) } else if union_types.contains(field.basetype.as_str()) { quote!(&"union") } else { - quote! { - &self.#param_ident - } + quote!(&self.#param_ident) }; - quote! { - .field(#param_str, #debug_value) - } + quote!(.field(#param_str, #debug_value)) }); let name_str = name.to_string(); + let lifetime = has_lifetime.then(|| quote!(<'_>)); let q = quote! { #[cfg(feature = "debug")] - impl fmt::Debug for #name { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + impl fmt::Debug for #name #lifetime { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt.debug_struct(#name_str) #(#debug_fields)* .finish() @@ -1813,10 +1848,11 @@ pub fn derive_debug( Some(q) } -pub fn derive_setters( +fn derive_setters( struct_: &vkxml::Struct, - members: &[(&vkxml::Field, Option)], + members: &[PreprocessedMember<'_>], root_structs: &HashSet, + has_lifetimes: &HashSet, ) -> Option { if &struct_.name == "VkBaseInStructure" || &struct_.name == "VkBaseOutStructure" @@ -1827,15 +1863,14 @@ pub fn derive_setters( } let name = name_to_tokens(&struct_.name); - let name_builder = name_to_tokens(&(struct_.name.clone() + "Builder")); let next_field = members .iter() - .find(|(field, _)| field.param_ident() == "p_next"); + .find(|member| member.vkxml_field.param_ident() == "p_next"); let structure_type_field = members .iter() - .find(|(field, _)| field.param_ident() == "s_type"); + .find(|member| member.vkxml_field.param_ident() == "s_type"); // Must either have both, or none: assert_eq!(next_field.is_some(), structure_type_field.is_some()); @@ -1849,10 +1884,15 @@ pub fn derive_setters( ("VkDescriptorSetLayoutBinding", "descriptorCount"), // No ImageView attachments when VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT is set ("VkFramebufferCreateInfo", "attachmentCount"), + // descriptorCount also describes descriptor length in pNext extension structures + // https://github.com/ash-rs/ash/issues/806 + ("VkWriteDescriptorSet", "descriptorCount"), ]; - let filter_members = members + let skip_members = members .iter() - .filter_map(|(field, _)| { + .filter_map(|member| { + let field = &member.vkxml_field; + // Associated _count members if field.array.is_some() { if let Some(array_size) = &field.size { @@ -1862,14 +1902,37 @@ pub fn derive_setters( } } + if let Some(objecttype) = &member.vk_parse_type_member.objecttype { + let objecttype_field = members + .iter() + .find(|m| m.vkxml_field.name.as_ref().unwrap() == objecttype) + .unwrap(); + // Extensions using this type are deprecated exactly because of the existence of VkObjectType, hence + // there won't be an additional ash trait to support VkDebugReportObjectTypeEXT. + // See also https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_debug_utils.html#_description + if objecttype_field.vkxml_field.basetype != "VkDebugReportObjectTypeEXT" { + return Some(objecttype); + } + } + None }) .collect::>(); - let setters = members.iter().filter_map(|(field, deprecated)| { - let deprecated = deprecated.as_ref().map(|d| quote!(#d #[allow(deprecated)])); + let setters = members.iter().filter_map(|member| { + let field = &member.vkxml_field; + + let name = field.name.as_ref().unwrap(); + if skip_members.contains(&name) { + return None; + } + + let deprecated = member.deprecated.as_ref().map(|d| quote!(#d #[allow(deprecated)])); let param_ident = field.param_ident(); - let param_ty_tokens = field.safe_type_tokens(quote!('a), None); + let mut type_lifetime = has_lifetimes + .contains(&name_to_tokens(&field.basetype)) + .then(|| quote!(<'a>)); + let param_ty_tokens = field.safe_type_tokens(quote!('a), type_lifetime.clone(), None); let param_ident_string = param_ident.to_string(); if param_ident_string == "s_type" || param_ident_string == "p_next" { @@ -1882,150 +1945,193 @@ pub fn derive_setters( .unwrap_or(¶m_ident_string); let mut param_ident_short = format_ident!("{}", param_ident_short); - if let Some(name) = field.name.as_ref() { - if filter_members.contains(&name) { - return None; - } + // Unique cases + if struct_.name == "VkShaderModuleCreateInfo" && name == "codeSize" { + return None; + } - // Unique cases - if struct_.name == "VkShaderModuleCreateInfo" && name == "codeSize" { - return None; - } + if struct_.name == "VkShaderModuleCreateInfo" && name == "pCode" { + return Some(quote! { + #[inline] + pub fn code(mut self, code: &'a [u32]) -> Self { + self.code_size = code.len() * 4; + self.p_code = code.as_ptr(); + self + } + }); + } - if struct_.name == "VkShaderModuleCreateInfo" && name == "pCode" { - return Some(quote!{ + if name == "pSampleMask" { + return Some(quote! { + /// Sets `p_sample_mask` to `null` if the slice is empty. The mask will + /// be treated as if it has all bits set to `1`. + /// + /// See + /// for more details. + #[inline] + pub fn sample_mask(mut self, sample_mask: &'a [SampleMask]) -> Self { + self.p_sample_mask = if sample_mask.is_empty() { + std::ptr::null() + } else { + sample_mask.as_ptr() + }; + self + } + }); + } + + if field.basetype == "char" { + let param_ident_as_c_str = format_ident!("{}_as_c_str", param_ident_short); + if matches!(field.reference, Some(vkxml::ReferenceType::Pointer)) { + assert!(field.null_terminate); + assert_eq!(field.size, None); + return Some(quote! { + #deprecated #[inline] - pub fn code(mut self, code: &'a [u32]) -> Self { - self.inner.code_size = code.len() * 4; - self.inner.p_code = code.as_ptr(); + pub fn #param_ident_short(mut self, #param_ident_short: &'a core::ffi::CStr) -> Self { + self.#param_ident = #param_ident_short.as_ptr(); self } - }); - } - - if name == "pSampleMask" { - return Some(quote!{ - /// Sets `p_sample_mask` to `null` if the slice is empty. The mask will - /// be treated as if it has all bits set to `1`. - /// - /// See - /// for more details. + #deprecated #[inline] - pub fn sample_mask(mut self, sample_mask: &'a [SampleMask]) -> Self { - self.inner.p_sample_mask = if sample_mask.is_empty() { - std::ptr::null() - } else { - sample_mask.as_ptr() - }; - self + pub unsafe fn #param_ident_as_c_str(&self) -> &core::ffi::CStr { + core::ffi::CStr::from_ptr(self.#param_ident) } }); - } - } - - // TODO: Improve in future when https://github.com/rust-lang/rust/issues/53667 is merged id:6 - if field.reference.is_some() { - if field.basetype == "char" && matches!(field.reference, Some(vkxml::ReferenceType::Pointer)) { - assert!(field.null_terminate); + } else if is_static_array(field) { assert_eq!(field.size, None); - return Some(quote!{ + return Some(quote! { + #deprecated #[inline] + pub fn #param_ident_short(mut self, #param_ident_short: &core::ffi::CStr) -> core::result::Result { + write_c_str_slice_with_nul(&mut self.#param_ident, #param_ident_short).map(|()| self) + } #deprecated - pub fn #param_ident_short(mut self, #param_ident_short: &'a ::std::ffi::CStr) -> Self { - self.inner.#param_ident = #param_ident_short.as_ptr(); - self + #[inline] + pub fn #param_ident_as_c_str(&self) -> core::result::Result<&core::ffi::CStr, core::ffi::FromBytesUntilNulError> { + wrap_c_str_slice_until_nul(&self.#param_ident) } }); } + } - if matches!(field.array, Some(vkxml::ArrayType::Dynamic)) { - if let Some(ref array_size) = field.size { - let mut slice_param_ty_tokens = field.safe_type_tokens(quote!('a), None); + // TODO: Improve in future when https://github.com/rust-lang/rust/issues/53667 is merged id:6 + if field.reference.is_some() && matches!(field.array, Some(vkxml::ArrayType::Dynamic)) { + if let Some(ref array_size) = field.size { + let mut ptr = if field.is_const { + quote!(.as_ptr()) + } else if let Some(tl) = &mut type_lifetime { + // Work around invariance with mutable pointers: + // https://github.com/ash-rs/ash/issues/837 + // https://doc.rust-lang.org/nomicon/subtyping.html#variance + *tl = quote!(<'_>); + quote!(.as_mut_ptr().cast()) + } else { + quote!(.as_mut_ptr()) + }; - let mut ptr = if field.is_const { - quote!(.as_ptr()) - } else { - quote!(.as_mut_ptr()) - }; + let mut slice_param_ty_tokens = field.safe_type_tokens(quote!('a), type_lifetime.clone(), None); - // Interpret void array as byte array - if field.basetype == "void" && matches!(field.reference, Some(vkxml::ReferenceType::Pointer)) { - slice_param_ty_tokens = quote!([u8]); - ptr = quote!(#ptr.cast()); - }; + // Interpret void array as byte array + if field.basetype == "void" && matches!(field.reference, Some(vkxml::ReferenceType::Pointer)) { + slice_param_ty_tokens = quote!([u8]); + ptr = quote!(#ptr.cast()); + }; - let set_size_stmt = if field.is_pointer_to_static_sized_array() { - // this is a pointer to a piece of memory with statically known size. - let array_size = field.c_size.as_ref().unwrap(); - let c_size = convert_c_expression(array_size, &BTreeMap::new()); - let inner_type = field.inner_type_tokens(None, None); + let set_size_stmt = if field.is_pointer_to_static_sized_array() { + // this is a pointer to a piece of memory with statically known size. + let array_size = field.c_size.as_ref().unwrap(); + let c_size = convert_c_expression(array_size, &BTreeMap::new()); + let inner_type = field.inner_type_tokens(None, None); - slice_param_ty_tokens = quote!([#inner_type; #c_size]); - ptr = quote!(); + slice_param_ty_tokens = quote!([#inner_type; #c_size]); + ptr = quote!(); - quote!() + quote!() + } else { + // Deal with a "special" 2D dynamic array with an inner size of 1 (effectively an array containing pointers to single objects) + let array_size = if let Some(array_size) = array_size.strip_suffix(",1") { + param_ident_short = format_ident!("{}_ptrs", param_ident_short); + slice_param_ty_tokens = field.safe_type_tokens(quote!('a), type_lifetime.clone(), Some(1)); + ptr = quote!(#ptr.cast()); + array_size } else { - // Deal with a "special" 2D dynamic array with an inner size of 1 (effectively an array containing pointers to single objects) - let array_size = if let Some(array_size) = array_size.strip_suffix(",1") { - param_ident_short = format_ident!("{}_ptrs", param_ident_short); - slice_param_ty_tokens = field.safe_type_tokens(quote!('a), Some(1)); - ptr = quote!(#ptr.cast()); - array_size - } else { - array_size - }; + array_size + }; - let array_size_ident = format_ident!("{}", array_size.to_snake_case()); + let array_size_ident = format_ident!("{}", array_size.to_snake_case()); - let size_field = members.iter().map(|(m, _)| m).find(|m| m.name.as_deref() == Some(array_size)).unwrap(); + let size_field = members.iter().find(|member| member.vkxml_field.name.as_deref() == Some(array_size)).unwrap(); - let cast = if size_field.basetype == "size_t" { - quote!() - } else { - quote!(as _) - }; - - quote!(self.inner.#array_size_ident = #param_ident_short.len()#cast;) + let cast = if size_field.vkxml_field.basetype == "size_t" { + quote!() + } else { + quote!(as _) }; - let mutable = if field.is_const { quote!() } else { quote!(mut) }; + quote!(self.#array_size_ident = #param_ident_short.len()#cast;) + }; - return Some(quote! { - #[inline] - #deprecated - pub fn #param_ident_short(mut self, #param_ident_short: &'a #mutable #slice_param_ty_tokens) -> Self { - #set_size_stmt - self.inner.#param_ident = #param_ident_short #ptr; - self - } - }); - } + let mutable = if field.is_const { quote!() } else { quote!(mut) }; + + return Some(quote! { + #deprecated + #[inline] + pub fn #param_ident_short(mut self, #param_ident_short: &'a #mutable #slice_param_ty_tokens) -> Self { + #set_size_stmt + self.#param_ident = #param_ident_short #ptr; + self + } + }); } } if field.basetype == "VkBool32" { return Some(quote!{ - #[inline] #deprecated + #[inline] pub fn #param_ident_short(mut self, #param_ident_short: bool) -> Self { - self.inner.#param_ident = #param_ident_short.into(); + self.#param_ident = #param_ident_short.into(); self } }); } + if let Some(objecttype) = &member.vk_parse_type_member.objecttype { + let objecttype_field = members + .iter() + .find(|m| m.vkxml_field.name.as_ref().unwrap() == objecttype) + .unwrap(); + + // Extensions using this type are deprecated exactly because of the existence of VkObjectType, hence + // there won't be an additional ash trait to support VkDebugReportObjectTypeEXT. + if objecttype_field.vkxml_field.basetype != "VkDebugReportObjectTypeEXT" { + let objecttype_ident = format_ident!("{}", objecttype.to_snake_case()); + + return Some(quote!{ + #deprecated + #[inline] + pub fn #param_ident_short(mut self, #param_ident_short: T) -> Self { + self.#param_ident = #param_ident_short.as_raw(); + self.#objecttype_ident = T::TYPE; + self + } + }); + } + }; + let param_ty_tokens = if is_opaque_type(&field.basetype) { // Use raw pointers for void/opaque types - field.type_tokens(false) + field.type_tokens(false, type_lifetime) } else { param_ty_tokens }; Some(quote!{ - #[inline] #deprecated + #[inline] pub fn #param_ident_short(mut self, #param_ident_short: #param_ty_tokens) -> Self { - self.inner.#param_ident = #param_ident_short; + self.#param_ident = #param_ident_short; self } }) @@ -2036,8 +2142,9 @@ pub fn derive_setters( // The `p_next` field should only be considered if this struct is also a root struct let root_struct_next_field = next_field.filter(|_| root_structs.contains(&name)); - // We only implement a next methods for root structs with a `pnext` field. - let next_function = if let Some((next_field, _)) = root_struct_next_field { + // We only implement a next method for root structs with a `pnext` field. + let next_function = if let Some(next_member) = root_struct_next_field { + let next_field = &next_member.vkxml_field; assert_eq!(next_field.basetype, "void"); let mutability = if next_field.is_const { quote!(const) @@ -2048,7 +2155,7 @@ pub fn derive_setters( /// Prepends the given extension struct between the root and the first pointer. This /// method only exists on structs that can be passed to a function directly. Only /// valid extension structs can be pushed into the chain. - /// If the chain looks like `A -> B -> C`, and you call `builder.push_next(&mut D)`, then the + /// If the chain looks like `A -> B -> C`, and you call `x.push_next(&mut D)`, then the /// chain will look like `A -> D -> B -> C`. pub fn push_next(mut self, next: &'a mut T) -> Self { unsafe { @@ -2063,8 +2170,8 @@ pub fn derive_setters( // ^^^^^^ // next chain let last_next = ptr_chain_iter(next).last().unwrap(); - (*last_next).p_next = self.inner.p_next as _; - self.inner.p_next = next_ptr; + (*last_next).p_next = self.p_next as _; + self.p_next = next_ptr; } self } @@ -2081,6 +2188,8 @@ pub fn derive_setters( quote!() }; + let lifetime = has_lifetimes.contains(&name).then(|| quote!(<'a>)); + // If the struct extends something we need to implement the traits. let impl_extend_trait = struct_ .extends @@ -2088,14 +2197,13 @@ pub fn derive_setters( .flat_map(|extends| extends.split(',')) .map(|extends| format_ident!("Extends{}", name_to_tokens(extends))) .map(|extends| { - quote! { - unsafe impl #extends for #name_builder<'_> {} - unsafe impl #extends for #name {} - } + // Extension structs always have a pNext, and therefore always have a lifetime. + quote!(unsafe impl #extends for #name<'_> {}) }); - let impl_structure_type_trait = structure_type_field.map(|(s_type, _)| { - let value = s_type + let impl_structure_type_trait = structure_type_field.map(|member| { + let value = member + .vkxml_field .type_enums .as_deref() .expect("s_type field must have a value in `vk.xml`"); @@ -2104,7 +2212,7 @@ pub fn derive_setters( let value = variant_ident("VkStructureType", value); quote! { - unsafe impl TaggedStructure for #name { + unsafe impl #lifetime TaggedStructure for #name #lifetime { const STRUCTURE_TYPE: StructureType = StructureType::#value; } } @@ -2112,49 +2220,13 @@ pub fn derive_setters( let q = quote! { #impl_structure_type_trait - impl #name { - pub fn builder<'a>() -> #name_builder<'a> { - #name_builder { - inner: Self::default(), - marker: ::std::marker::PhantomData, - } - } - } - - #[repr(transparent)] - pub struct #name_builder<'a> { - inner: #name, - marker: ::std::marker::PhantomData<&'a ()>, - } #(#impl_extend_trait)* #next_trait - - impl<'a> ::std::ops::Deref for #name_builder<'a> { - type Target = #name; - - fn deref(&self) -> &Self::Target { - &self.inner - } - } - - impl<'a> ::std::ops::DerefMut for #name_builder<'a> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.inner - } - } - - impl<'a> #name_builder<'a> { + impl #lifetime #name #lifetime { #(#setters)* #next_function - - /// Calling build will **discard** all the lifetime information. Only call this if - /// necessary! Builders implement `Deref` targeting their corresponding Vulkan struct, - /// so references to builders can be passed directly to Vulkan functions. - pub fn build(self) -> #name { - self.inner - } } }; @@ -2172,11 +2244,19 @@ pub fn manual_derives(struct_: &vkxml::Struct) -> TokenStream { _ => quote! {}, } } + +struct PreprocessedMember<'a> { + vkxml_field: &'a vkxml::Field, + vk_parse_type_member: &'a vk_parse::TypeMemberDefinition, + deprecated: Option, +} + pub fn generate_struct( struct_: &vkxml::Struct, vk_parse_types: &HashMap, root_structs: &HashSet, union_types: &HashSet<&str>, + has_lifetimes: &HashSet, ) -> TokenStream { let name = name_to_tokens(&struct_.name); let vk_parse_struct = vk_parse_types[&struct_.name]; @@ -2263,7 +2343,7 @@ pub fn generate_struct( matches!(vk_parse_field.api.as_deref(), None | Some(DESIRED_API)) }) .map(|(field, vk_parse_field)| { - let deprecation = vk_parse_field + let deprecated = vk_parse_field .deprecated .as_ref() .map(|deprecated| match deprecated.as_str() { @@ -2273,11 +2353,17 @@ pub fn generate_struct( } x => panic!("Unknown deprecation reason {}", x), }); - (field, deprecation) + PreprocessedMember { + vkxml_field: field, + vk_parse_type_member: vk_parse_field, + deprecated, + } }) .collect::>(); - let params = members.iter().map(|(field, deprecation)| { + let params = members.iter().map(|member| { + let field = &member.vkxml_field; + let deprecated = &member.deprecated; let param_ident = field.param_ident(); let param_ty_tokens = if field.basetype == struct_.name { let pointer = field @@ -2286,15 +2372,24 @@ pub fn generate_struct( .map(|r| r.to_tokens(field.is_const)); quote!(#pointer Self) } else { - field.type_tokens(false) + let type_lifetime = has_lifetimes + .contains(&name_to_tokens(&field.basetype)) + .then(|| quote!(<'a>)); + field.type_tokens(false, type_lifetime) }; - quote!(#deprecation pub #param_ident: #param_ty_tokens) + quote!(#deprecated pub #param_ident: #param_ty_tokens) }); - let debug_tokens = derive_debug(struct_, &members, union_types); - let default_tokens = derive_default(struct_, &members); - let setter_tokens = derive_setters(struct_, &members, root_structs); + let has_lifetime = has_lifetimes.contains(&name); + let (lifetimes, marker) = match has_lifetime { + true => (quote!(<'a>), quote!(pub _marker: PhantomData<&'a ()>,)), + false => (quote!(), quote!()), + }; + + let debug_tokens = derive_debug(struct_, &members, union_types, has_lifetime); + let default_tokens = derive_default(struct_, &members, has_lifetime); + let setter_tokens = derive_setters(struct_, &members, root_structs, has_lifetimes); let manual_derive_tokens = manual_derives(struct_); let dbg_str = if debug_tokens.is_none() { quote!(#[cfg_attr(feature = "debug", derive(Debug))]) @@ -2312,8 +2407,10 @@ pub fn generate_struct( #dbg_str #[derive(Copy, Clone, #default_str #manual_derive_tokens)] #[doc = #khronos_link] - pub struct #name { + #[must_use] + pub struct #name #lifetimes { #(#params,)* + #marker } #debug_tokens #default_tokens @@ -2346,17 +2443,20 @@ pub fn generate_handle(handle: &vkxml::Handle) -> Option { }; Some(tokens) } -fn generate_funcptr(fnptr: &vkxml::FunctionPointer) -> TokenStream { +fn generate_funcptr(fnptr: &vkxml::FunctionPointer, has_lifetimes: &HashSet) -> TokenStream { let name = format_ident!("{}", fnptr.name); let ret_ty_tokens = if fnptr.return_type.is_void() { quote!() } else { - let ret_ty_tokens = fnptr.return_type.type_tokens(true); + let ret_ty_tokens = fnptr.return_type.type_tokens(true, None); quote!(-> #ret_ty_tokens) }; let params = fnptr.param.iter().map(|field| { let ident = field.param_ident(); - let type_tokens = field.type_tokens(true); + let type_lifetime = has_lifetimes + .contains(&name_to_tokens(&field.basetype)) + .then(|| quote!(<'_>)); + let type_tokens = field.type_tokens(true, type_lifetime); quote! { #ident: #type_tokens } @@ -2369,24 +2469,28 @@ fn generate_funcptr(fnptr: &vkxml::FunctionPointer) -> TokenStream { } } -fn generate_union(union: &vkxml::Union) -> TokenStream { +fn generate_union(union: &vkxml::Union, has_lifetimes: &HashSet) -> TokenStream { let name = name_to_tokens(&union.name); let fields = union.elements.iter().map(|field| { let name = field.param_ident(); - let ty = field.type_tokens(false); + let type_lifetime = has_lifetimes + .contains(&name_to_tokens(&field.basetype)) + .then(|| quote!(<'a>)); + let ty = field.type_tokens(false, type_lifetime); quote! { pub #name: #ty } }); let khronos_link = khronos_link(&union.name); + let lifetime = has_lifetimes.contains(&name).then(|| quote!(<'a>)); quote! { #[repr(C)] #[derive(Copy, Clone)] #[doc = #khronos_link] - pub union #name { + pub union #name #lifetime { #(#fields),* } - impl ::std::default::Default for #name { + impl #lifetime ::std::default::Default for #name #lifetime { #[inline] fn default() -> Self { unsafe { ::std::mem::zeroed() } @@ -2439,6 +2543,7 @@ pub fn generate_definition( allowed_types: &HashSet<&str>, union_types: &HashSet<&str>, root_structs: &HashSet, + has_lifetimes: &HashSet, vk_parse_types: &HashMap, bitflags_cache: &mut HashSet, const_values: &mut BTreeMap, @@ -2457,6 +2562,7 @@ pub fn generate_definition( vk_parse_types, root_structs, union_types, + has_lifetimes, )) } vkxml::DefinitionsElement::Bitmask(ref mask) @@ -2470,12 +2576,12 @@ pub fn generate_definition( generate_handle(handle) } vkxml::DefinitionsElement::FuncPtr(ref fp) if allowed_types.contains(fp.name.as_str()) => { - Some(generate_funcptr(fp)) + Some(generate_funcptr(fp, has_lifetimes)) } vkxml::DefinitionsElement::Union(ref union) if allowed_types.contains(union.name.as_str()) => { - Some(generate_union(union)) + Some(generate_union(union, has_lifetimes)) } _ => None, } @@ -2484,6 +2590,7 @@ pub fn generate_feature<'a>( feature: &vkxml::Feature, commands: &CommandMap<'a>, fn_cache: &mut HashSet<&'a str>, + has_lifetimes: &HashSet, ) -> TokenStream { if !contains_desired_api(&feature.api) { return quote!(); @@ -2516,6 +2623,7 @@ pub fn generate_feature<'a>( &static_commands, &HashMap::new(), fn_cache, + has_lifetimes, ) } else { quote! {} @@ -2525,18 +2633,21 @@ pub fn generate_feature<'a>( &entry_commands, &HashMap::new(), fn_cache, + has_lifetimes, ); let instance = generate_function_pointers( format_ident!("InstanceFnV{}", version), &instance_commands, &HashMap::new(), fn_cache, + has_lifetimes, ); let device = generate_function_pointers( format_ident!("DeviceFnV{}", version), &device_commands, &HashMap::new(), fn_cache, + has_lifetimes, ); quote! { #static_fn @@ -2635,7 +2746,7 @@ pub fn generate_const_debugs(const_values: &BTreeMap) - quote! { impl fmt::Debug for #ty { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { const KNOWN: &[(#type_, &str)] = &[#(#cases),*]; debug_flags(f, KNOWN, self.0) } @@ -2653,7 +2764,7 @@ pub fn generate_const_debugs(const_values: &BTreeMap) - }); quote! { impl fmt::Debug for #ty { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let name = match *self { #(#cases)* _ => None, @@ -2734,6 +2845,7 @@ pub fn extract_native_types(registry: &vk_parse::Registry) -> (Vec<(String, Stri pub fn generate_aliases_of_types( types: &vk_parse::Types, allowed_types: &HashSet<&str>, + has_lifetimes: &HashSet, ty_cache: &mut HashSet, ) -> TokenStream { let aliases = types @@ -2751,8 +2863,10 @@ pub fn generate_aliases_of_types( return None; }; let alias_ident = name_to_tokens(alias); - let tokens = quote! { - pub type #name_ident = #alias_ident; + let tokens = if has_lifetimes.contains(&alias_ident) { + quote!(pub type #name_ident<'a> = #alias_ident<'a>;) + } else { + quote!(pub type #name_ident = #alias_ident;) }; Some(tokens) }); @@ -2775,9 +2889,6 @@ pub fn write_source_code>(vk_headers_dir: &Path, src_dir: P) { .filter(|e| { if let Some(supported) = &e.supported { contains_desired_api(supported) || - // Backwards-compatibility with ash 0.37: - // Keep extension 196 disabled as it aliases an undefined constant - (supported == "disabled" && e.number != Some(196)) || // VK_ANDROID_native_buffer is for internal use only, but types defined elsewhere // reference enum extension constants. Exempt the extension from this check until // types are properly folded in with their extension (where applicable). @@ -2890,6 +3001,58 @@ pub fn write_source_code>(vk_headers_dir: &Path, src_dir: P) { constants_code.push(quote! { pub const SHADER_UNUSED_NV : u32 = SHADER_UNUSED_KHR;}); + let union_types = definitions + .iter() + .filter_map(get_variant!(vkxml::DefinitionsElement::Union)) + .map(|union_| union_.name.as_str()) + .collect::>(); + + let mut identifier_renames = BTreeMap::new(); + + // Identify structs that need a lifetime annotation + // Note that this relies on `vk.xml` defining types before they are used, + // as is required in C(++) too. + let mut has_lifetimes = definitions + .iter() + .filter_map(get_variant!(vkxml::DefinitionsElement::Struct)) + .filter(|s| { + s.elements + .iter() + .filter_map(get_variant!(vkxml::StructElement::Member)) + .any(|x| x.reference.is_some()) + }) + .map(|s| name_to_tokens(&s.name)) + .collect::>(); + for def in &definitions { + match def { + vkxml::DefinitionsElement::Struct(s) => s + .elements + .iter() + .filter_map(get_variant!(vkxml::StructElement::Member)) + .any(|field| has_lifetimes.contains(&name_to_tokens(&field.basetype))) + .then(|| has_lifetimes.insert(name_to_tokens(&s.name))), + vkxml::DefinitionsElement::Union(u) => u + .elements + .iter() + .any(|field| has_lifetimes.contains(&name_to_tokens(&field.basetype))) + .then(|| has_lifetimes.insert(name_to_tokens(&u.name))), + _ => continue, + }; + } + for type_ in spec2 + .0 + .iter() + .filter_map(get_variant!(vk_parse::RegistryChild::Types)) + .flat_map(|types| &types.children) + .filter_map(get_variant!(vk_parse::TypesChild::Type)) + { + if let (Some(name), Some(alias)) = (&type_.name, &type_.alias) { + if has_lifetimes.contains(&name_to_tokens(alias)) { + has_lifetimes.insert(name_to_tokens(name)); + } + } + } + let extension_code = extensions .iter() .filter_map(|ext| { @@ -2900,18 +3063,11 @@ pub fn write_source_code>(vk_headers_dir: &Path, src_dir: P) { &mut const_values, &cmd_aliases, &mut fn_cache, + &has_lifetimes, ) }) .collect_vec(); - let union_types = definitions - .iter() - .filter_map(get_variant!(vkxml::DefinitionsElement::Union)) - .map(|union_| union_.name.as_str()) - .collect::>(); - - let mut identifier_renames = BTreeMap::new(); - let vk_parse_types = spec2 .0 .iter() @@ -2943,6 +3099,7 @@ pub fn write_source_code>(vk_headers_dir: &Path, src_dir: P) { &required_types, &union_types, &root_structs, + &has_lifetimes, &vk_parse_types, &mut bitflags_cache, &mut const_values, @@ -2955,12 +3112,12 @@ pub fn write_source_code>(vk_headers_dir: &Path, src_dir: P) { .0 .iter() .filter_map(get_variant!(vk_parse::RegistryChild::Types)) - .map(|ty| generate_aliases_of_types(ty, &required_types, &mut ty_cache)) + .map(|ty| generate_aliases_of_types(ty, &required_types, &has_lifetimes, &mut ty_cache)) .collect(); let feature_code: Vec<_> = features .iter() - .map(|feature| generate_feature(feature, &commands, &mut fn_cache)) + .map(|feature| generate_feature(feature, &commands, &mut fn_cache, &has_lifetimes)) .collect(); let feature_extensions_code = generate_feature_extension(&spec2, &mut const_cache, &mut const_values); @@ -2997,6 +3154,7 @@ pub fn write_source_code>(vk_headers_dir: &Path, src_dir: P) { }; let definition_code = quote! { + use std::marker::PhantomData; use std::fmt; use std::os::raw::*; use crate::vk::{Handle, ptr_chain_iter}; @@ -3027,6 +3185,8 @@ pub fn write_source_code>(vk_headers_dir: &Path, src_dir: P) { }; let extension_code = quote! { + #![allow(unused_qualifications)] // Because we do not know in what file the PFNs are defined + use std::os::raw::*; use crate::vk::platform_types::*; use crate::vk::aliases::*;