generator: Generate enums from vk_parse representation (#410)

* generator: Generate enums from vk_parse representation

This change prepares for future additions in vk_parse fields ([1]) by
converting over the enum generation path from vkxml.  Most of the
conversion is easy by repurposing the existing `EnumSpec` parsing logic
from extension constants for all enumeration variants, with slight
modifications to not bail when `extends` is not set which is specific to
extension constants.

As an (unintended) added bonus this unification of the `EnumSpec`
codepath allows aliases (for backwards-compatible names) to be generated
as discussed earlier in [2].

[1]: https://github.com/krolli/vk-parse/pull/17
[2]: https://github.com/MaikKlein/ash/pull/384#discussion_r588693967

* generator: Turn "backwards"-mentioning docs into deprecation notices

All constant aliases for misspelled items (missing `_BIT` andsoforth)
contain the text "backwards compatibility" or "Backwards-compatible
alias".

* generator: Drop aliases whose name becomes identical after de-mangling

* generator: Remove aliases from const_debugs

These already have a match against the name they're aliasing, and some
of the aliases are "deprecated" (since they're just typo fixups for
backwards compatibility).
This commit is contained in:
Marijn Suijten 2021-04-18 21:58:40 +02:00 committed by GitHub
parent 90b0531474
commit 46ed5158ab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 133 additions and 86 deletions

View file

@ -496,6 +496,8 @@ impl StencilFaceFlags {
pub const BACK: Self = Self(0b10); pub const BACK: Self = Self(0b10);
#[doc = "Front and back faces"] #[doc = "Front and back faces"]
pub const FRONT_AND_BACK: Self = Self(0x0000_0003); pub const FRONT_AND_BACK: Self = Self(0x0000_0003);
#[deprecated = "Alias for backwards compatibility"]
pub const STENCIL_FRONT_AND_BACK: Self = Self::FRONT_AND_BACK;
} }
#[repr(transparent)] #[repr(transparent)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -689,6 +691,7 @@ impl ExternalSemaphoreHandleTypeFlags {
pub const OPAQUE_WIN32: Self = Self(0b10); pub const OPAQUE_WIN32: Self = Self(0b10);
pub const OPAQUE_WIN32_KMT: Self = Self(0b100); pub const OPAQUE_WIN32_KMT: Self = Self(0b100);
pub const D3D12_FENCE: Self = Self(0b1000); pub const D3D12_FENCE: Self = Self(0b1000);
pub const D3D11_FENCE: Self = Self::D3D12_FENCE;
pub const SYNC_FD: Self = Self(0b1_0000); pub const SYNC_FD: Self = Self(0b1_0000);
} }
#[repr(transparent)] #[repr(transparent)]

View file

@ -1128,6 +1128,8 @@ impl ColorSpaceKHR {
} }
impl ColorSpaceKHR { impl ColorSpaceKHR {
pub const SRGB_NONLINEAR: Self = Self(0); pub const SRGB_NONLINEAR: Self = Self(0);
#[deprecated = "Backwards-compatible alias containing a typo"]
pub const COLORSPACE_SRGB_NONLINEAR: Self = Self::SRGB_NONLINEAR;
} }
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[repr(transparent)] #[repr(transparent)]
@ -1189,9 +1191,13 @@ impl DebugReportObjectTypeEXT {
pub const SURFACE_KHR: Self = Self(26); pub const SURFACE_KHR: Self = Self(26);
pub const SWAPCHAIN_KHR: Self = Self(27); pub const SWAPCHAIN_KHR: Self = Self(27);
pub const DEBUG_REPORT_CALLBACK_EXT: Self = Self(28); pub const DEBUG_REPORT_CALLBACK_EXT: Self = Self(28);
#[deprecated = "Backwards-compatible alias containing a typo"]
pub const DEBUG_REPORT: Self = Self::DEBUG_REPORT_CALLBACK_EXT;
pub const DISPLAY_KHR: Self = Self(29); pub const DISPLAY_KHR: Self = Self(29);
pub const DISPLAY_MODE_KHR: Self = Self(30); pub const DISPLAY_MODE_KHR: Self = Self(30);
pub const VALIDATION_CACHE_EXT: Self = Self(33); pub const VALIDATION_CACHE_EXT: Self = Self(33);
#[deprecated = "Backwards-compatible alias containing a typo"]
pub const VALIDATION_CACHE: Self = Self::VALIDATION_CACHE_EXT;
} }
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[repr(transparent)] #[repr(transparent)]
@ -1974,6 +1980,9 @@ impl PerformanceCounterScopeKHR {
pub const COMMAND_BUFFER: Self = Self(0); pub const COMMAND_BUFFER: Self = Self(0);
pub const RENDER_PASS: Self = Self(1); pub const RENDER_PASS: Self = Self(1);
pub const COMMAND: Self = Self(2); pub const COMMAND: Self = Self(2);
pub const QUERY_SCOPE_COMMAND_BUFFER: Self = Self::COMMAND_BUFFER;
pub const QUERY_SCOPE_RENDER_PASS: Self = Self::RENDER_PASS;
pub const QUERY_SCOPE_COMMAND: Self = Self::COMMAND;
} }
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[repr(transparent)] #[repr(transparent)]

View file

@ -401,13 +401,16 @@ impl ConstVal {
} }
} }
pub trait ConstantExt { pub trait ConstantExt {
fn constant(&self) -> Constant; fn constant(&self, enum_name: &str) -> Constant;
fn variant_ident(&self, enum_name: &str) -> Ident; fn variant_ident(&self, enum_name: &str) -> Ident;
fn notation(&self) -> Option<&str>; fn notation(&self) -> Option<&str>;
fn is_alias(&self) -> bool {
false
}
} }
impl ConstantExt for vkxml::ExtensionEnum { impl ConstantExt for vkxml::ExtensionEnum {
fn constant(&self) -> Constant { fn constant(&self, _enum_name: &str) -> Constant {
Constant::from_extension_enum(self).unwrap() Constant::from_extension_enum(self).unwrap()
} }
fn variant_ident(&self, enum_name: &str) -> Ident { fn variant_ident(&self, enum_name: &str) -> Ident {
@ -418,8 +421,25 @@ impl ConstantExt for vkxml::ExtensionEnum {
} }
} }
impl ConstantExt for vk_parse::Enum {
fn constant(&self, enum_name: &str) -> Constant {
Constant::from_vk_parse_enum_spec(&self.spec, Some(enum_name), None)
.unwrap()
.0
}
fn variant_ident(&self, enum_name: &str) -> Ident {
variant_ident(enum_name, &self.name)
}
fn notation(&self) -> Option<&str> {
self.comment.as_deref()
}
fn is_alias(&self) -> bool {
matches!(self.spec, vk_parse::EnumSpec::Alias { .. })
}
}
impl ConstantExt for vkxml::Constant { impl ConstantExt for vkxml::Constant {
fn constant(&self) -> Constant { fn constant(&self, _enum_name: &str) -> Constant {
Constant::from_constant(self) Constant::from_constant(self)
} }
fn variant_ident(&self, enum_name: &str) -> Ident { fn variant_ident(&self, enum_name: &str) -> Ident {
@ -534,6 +554,53 @@ impl Constant {
.map(|e| Constant::CExpr(e.clone())); .map(|e| Constant::CExpr(e.clone()));
number.or(hex).or(bitpos).or(expr).expect("") number.or(hex).or(bitpos).or(expr).expect("")
} }
/// Returns (Constant, optional base type, is_alias)
pub fn from_vk_parse_enum_spec(
spec: &vk_parse::EnumSpec,
enum_name: Option<&str>,
extension_number: Option<i64>,
) -> Option<(Self, Option<String>, bool)> {
use vk_parse::EnumSpec;
match spec {
EnumSpec::Bitpos { bitpos, extends } => {
Some((Self::BitPos(*bitpos as u32), extends.clone(), false))
}
EnumSpec::Offset {
offset,
extends,
extnumber,
dir: positive,
} => {
let ext_base = 1_000_000_000;
let ext_block_size = 1000;
let extnumber = extnumber
.or(extension_number)
.expect("Need an extension number");
let value = ext_base + (extnumber - 1) * ext_block_size + offset;
let value = if *positive { value } else { -value };
Some((Self::Number(value as i32), Some(extends.clone()), false))
}
EnumSpec::Value { value, extends } => {
let value = value
.strip_prefix("0x")
.map(|hex| Self::Hex(hex.to_owned()))
.or_else(|| value.parse::<i32>().ok().map(Self::Number))?;
Some((value, extends.clone(), false))
}
EnumSpec::Alias { alias, extends } => {
let base_type = extends.as_deref().or(enum_name)?;
let key = variant_ident(base_type, &alias);
if key == "DISPATCH_BASE" {
None
} else {
Some((Self::Alias(key), Some(base_type.to_owned()), true))
}
}
_ => None,
}
}
} }
pub trait FeatureExt { pub trait FeatureExt {
@ -1007,7 +1074,7 @@ pub struct ExtensionConstant<'a> {
pub constant: Constant, pub constant: Constant,
} }
impl<'a> ConstantExt for ExtensionConstant<'a> { impl<'a> ConstantExt for ExtensionConstant<'a> {
fn constant(&self) -> Constant { fn constant(&self, _enum_name: &str) -> Constant {
self.constant.clone() self.constant.clone()
} }
fn variant_ident(&self, enum_name: &str) -> Ident { fn variant_ident(&self, enum_name: &str) -> Ident {
@ -1025,7 +1092,6 @@ pub fn generate_extension_constants<'a>(
const_cache: &mut HashSet<&'a str, impl BuildHasher>, const_cache: &mut HashSet<&'a str, impl BuildHasher>,
const_values: &mut BTreeMap<Ident, Vec<ConstantMatchInfo>>, const_values: &mut BTreeMap<Ident, Vec<ConstantMatchInfo>>,
) -> TokenStream { ) -> TokenStream {
use vk_parse::EnumSpec;
let items = extension_items let items = extension_items
.iter() .iter()
.filter_map(|item| match item { .filter_map(|item| match item {
@ -1034,52 +1100,16 @@ pub fn generate_extension_constants<'a>(
}) })
.flat_map(|iter| iter); .flat_map(|iter| iter);
let enum_tokens = items.filter_map(|item| match item { let enum_tokens = items.filter_map(|item| match item {
vk_parse::InterfaceItem::Enum(_enum) => { vk_parse::InterfaceItem::Enum(enum_) => {
if const_cache.contains(_enum.name.as_str()) { if const_cache.contains(enum_.name.as_str()) {
return None; return None;
} }
let (constant, extends, is_alias) = match &_enum.spec { let (constant, extends, is_alias) =
EnumSpec::Bitpos { bitpos, extends } => { Constant::from_vk_parse_enum_spec(&enum_.spec, None, Some(extension_number))?;
Some((Constant::BitPos(*bitpos as u32), extends.clone(), false))
}
EnumSpec::Offset {
offset,
extends,
extnumber,
dir: positive,
} => {
let ext_base = 1_000_000_000;
let ext_block_size = 1000;
let extnumber = extnumber.unwrap_or_else(|| extension_number);
let value = ext_base + (extnumber - 1) * ext_block_size + offset;
let value = if *positive { value } else { -value };
Some((Constant::Number(value as i32), Some(extends.clone()), false))
}
EnumSpec::Value { value, extends } => {
if let (Some(extends), Ok(value)) = (extends, value.parse::<i32>()) {
Some((Constant::Number(value), Some(extends.clone()), false))
} else {
None
}
}
EnumSpec::Alias { alias, extends } => {
if let Some(extends) = extends {
let key = variant_ident(&extends, &alias);
if key == "DISPATCH_BASE" {
None
} else {
Some((Constant::Alias(key), Some(extends.clone()), true))
}
} else {
None
}
}
_ => None,
}?;
let extends = extends?; let extends = extends?;
let ext_constant = ExtensionConstant { let ext_constant = ExtensionConstant {
name: &_enum.name, name: &enum_.name,
constant, constant,
}; };
let ident = name_to_tokens(&extends); let ident = name_to_tokens(&extends);
@ -1097,7 +1127,7 @@ pub fn generate_extension_constants<'a>(
#impl_block #impl_block
}; };
const_cache.insert(_enum.name.as_str()); const_cache.insert(enum_.name.as_str());
Some(q) Some(q)
} }
_ => None, _ => None,
@ -1334,7 +1364,7 @@ pub fn bitflags_impl_block(
.iter() .iter()
.map(|constant| { .map(|constant| {
let variant_ident = constant.variant_ident(enum_name); let variant_ident = constant.variant_ident(enum_name);
let constant = constant.constant(); let constant = constant.constant(enum_name);
let tokens = if let Constant::Alias(_) = &constant { let tokens = if let Constant::Alias(_) = &constant {
quote!(#constant) quote!(#constant)
} else { } else {
@ -1346,8 +1376,10 @@ pub fn bitflags_impl_block(
let notations = constants.iter().map(|constant| { let notations = constants.iter().map(|constant| {
constant.notation().map(|n| { constant.notation().map(|n| {
quote! { if n.to_lowercase().contains("backwards") {
#[doc = #n] quote!(#[deprecated = #n])
} else {
quote!(#[doc = #n])
} }
}) })
}); });
@ -1370,39 +1402,52 @@ pub fn bitflags_impl_block(
} }
pub fn generate_enum<'a>( pub fn generate_enum<'a>(
_enum: &'a vkxml::Enumeration, enum_: &'a vk_parse::Enums,
const_cache: &mut HashSet<&'a str, impl BuildHasher>, const_cache: &mut HashSet<&'a str, impl BuildHasher>,
const_values: &mut BTreeMap<Ident, Vec<ConstantMatchInfo>>, const_values: &mut BTreeMap<Ident, Vec<ConstantMatchInfo>>,
bitflags_cache: &mut HashSet<Ident, impl BuildHasher>, bitflags_cache: &mut HashSet<Ident, impl BuildHasher>,
) -> EnumType { ) -> EnumType {
let name = &_enum.name[2..]; let name = enum_.name.as_ref().unwrap();
let _name = name.replace("FlagBits", "Flags"); let clean_name = name.strip_prefix("Vk").unwrap();
let _name = clean_name.replace("FlagBits", "Flags");
let ident = format_ident!("{}", _name.as_str()); let ident = format_ident!("{}", _name.as_str());
let constants: Vec<_> = _enum let constants = enum_
.elements .children
.iter() .iter()
.filter_map(|elem| match *elem { .filter_map(|elem| match *elem {
vkxml::EnumerationElement::Enum(ref constant) => Some(constant), vk_parse::EnumsChild::Enum(ref constant) => Some(constant),
_ => None, _ => None,
}) })
.filter(|constant| match &constant.spec {
vk_parse::EnumSpec::Alias { alias, .. } => {
// Remove any alias whose name is identical after name de-mangling. For example
// the XML contains compatibility aliases for variants without _BIT postfix
// which are removed by the generator anyway, after which they become identical.
let alias_name = constant.variant_ident(name);
let aliases_to = variant_ident(name, alias);
alias_name != aliases_to
}
_ => true,
})
.collect_vec(); .collect_vec();
let mut values = Vec::with_capacity(constants.len()); let mut values = Vec::with_capacity(constants.len());
for constant in &constants { for constant in &constants {
const_cache.insert(constant.name.as_str()); const_cache.insert(constant.name.as_str());
values.push(ConstantMatchInfo { values.push(ConstantMatchInfo {
ident: constant.variant_ident(&_enum.name), ident: constant.variant_ident(name),
is_alias: false, is_alias: constant.is_alias(),
}); });
} }
const_values.insert(ident.clone(), values); const_values.insert(ident.clone(), values);
let khronos_link = khronos_link(&_enum.name); let khronos_link = khronos_link(name);
if name.contains("Bit") { if clean_name.contains("Bit") {
let ident = format_ident!("{}", _name.as_str()); let ident = format_ident!("{}", _name.as_str());
let all_bits = constants let all_bits = constants
.iter() .iter()
.filter_map(|constant| Constant::from_constant(constant).value()) .filter_map(|constant| constant.constant(name).value())
.fold(0, |acc, next| acc | next.bits()); .fold(0, |acc, next| acc | next.bits());
let bit_string = format!("{:b}", all_bits); let bit_string = format!("{:b}", all_bits);
let bit_string = interleave_number('_', 4, &bit_string); let bit_string = interleave_number('_', 4, &bit_string);
@ -1411,7 +1456,7 @@ pub fn generate_enum<'a>(
if bitflags_cache.contains(&ident) { if bitflags_cache.contains(&ident) {
EnumType::Bitflags(quote! {}) EnumType::Bitflags(quote! {})
} else { } else {
let impl_bitflags = bitflags_impl_block(ident.clone(), &_enum.name, &constants); let impl_bitflags = bitflags_impl_block(ident.clone(), name, &constants);
bitflags_cache.insert(ident.clone()); bitflags_cache.insert(ident.clone());
let q = quote! { let q = quote! {
#[repr(transparent)] #[repr(transparent)]
@ -1426,11 +1471,11 @@ pub fn generate_enum<'a>(
} else { } else {
let (struct_attribute, special_quote) = match _name.as_str() { let (struct_attribute, special_quote) = match _name.as_str() {
//"StructureType" => generate_structure_type(&_name, _enum, create_info_constants), //"StructureType" => generate_structure_type(&_name, _enum, create_info_constants),
"Result" => (quote!(#[must_use]), generate_result(ident.clone(), _enum)), "Result" => (quote!(#[must_use]), generate_result(ident.clone(), enum_)),
_ => (quote!(), quote!()), _ => (quote!(), quote!()),
}; };
let impl_block = bitflags_impl_block(ident.clone(), &_enum.name, &constants); let impl_block = bitflags_impl_block(ident.clone(), name, &constants);
let enum_quote = quote! { let enum_quote = quote! {
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[repr(transparent)] #[repr(transparent)]
@ -1452,19 +1497,19 @@ pub fn generate_enum<'a>(
} }
} }
pub fn generate_result(ident: Ident, _enum: &vkxml::Enumeration) -> TokenStream { pub fn generate_result(ident: Ident, enum_: &vk_parse::Enums) -> TokenStream {
let notation = _enum.elements.iter().filter_map(|elem| { let notation = enum_.children.iter().filter_map(|elem| {
let (variant_name, notation) = match *elem { let (variant_name, notation) = match *elem {
vkxml::EnumerationElement::Enum(ref constant) => ( vk_parse::EnumsChild::Enum(ref constant) => (
constant.name.as_str(), constant.name.as_str(),
constant.notation.as_deref().unwrap_or(""), constant.comment.as_deref().unwrap_or(""),
), ),
_ => { _ => {
return None; return None;
} }
}; };
let variant_ident = variant_ident(&_enum.name, variant_name); let variant_ident = variant_ident(&enum_.name.as_ref().unwrap(), variant_name);
Some(quote! { Some(quote! {
#ident::#variant_ident => Some(#notation) #ident::#variant_ident => Some(#notation)
}) })
@ -2461,21 +2506,6 @@ pub fn write_source_code<P: AsRef<Path>>(vk_xml: &Path, src_dir: P) {
.flat_map(|definitions| definitions.elements.iter()) .flat_map(|definitions| definitions.elements.iter())
.collect(); .collect();
let enums: Vec<&vkxml::Enumeration> = spec
.elements
.iter()
.filter_map(|elem| match elem {
vkxml::RegistryElement::Enums(ref enums) => Some(enums),
_ => None,
})
.flat_map(|enums| {
enums.elements.iter().filter_map(|_enum| match *_enum {
vkxml::EnumsElement::Enumeration(ref e) => Some(e),
_ => None,
})
})
.collect();
let constants: Vec<&vkxml::Constant> = spec let constants: Vec<&vkxml::Constant> = spec
.elements .elements
.iter() .iter()
@ -2492,8 +2522,13 @@ pub fn write_source_code<P: AsRef<Path>>(vk_xml: &Path, src_dir: P) {
let mut const_values: BTreeMap<Ident, Vec<ConstantMatchInfo>> = BTreeMap::new(); let mut const_values: BTreeMap<Ident, Vec<ConstantMatchInfo>> = BTreeMap::new();
let (enum_code, bitflags_code) = enums let (enum_code, bitflags_code) = spec2
.into_iter() .0
.iter()
.filter_map(|item| match item {
vk_parse::RegistryChild::Enums(ref enums) if enums.kind.is_some() => Some(enums),
_ => None,
})
.map(|e| generate_enum(e, &mut const_cache, &mut const_values, &mut bitflags_cache)) .map(|e| generate_enum(e, &mut const_cache, &mut const_values, &mut bitflags_cache))
.fold((Vec::new(), Vec::new()), |mut acc, elem| { .fold((Vec::new(), Vec::new()), |mut acc, elem| {
match elem { match elem {