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);
#[doc = "Front and back faces"]
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)]
#[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_KMT: Self = Self(0b100);
pub const D3D12_FENCE: Self = Self(0b1000);
pub const D3D11_FENCE: Self = Self::D3D12_FENCE;
pub const SYNC_FD: Self = Self(0b1_0000);
}
#[repr(transparent)]

View file

@ -1128,6 +1128,8 @@ impl ColorSpaceKHR {
}
impl ColorSpaceKHR {
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)]
#[repr(transparent)]
@ -1189,9 +1191,13 @@ impl DebugReportObjectTypeEXT {
pub const SURFACE_KHR: Self = Self(26);
pub const SWAPCHAIN_KHR: Self = Self(27);
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_MODE_KHR: Self = Self(30);
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)]
#[repr(transparent)]
@ -1974,6 +1980,9 @@ impl PerformanceCounterScopeKHR {
pub const COMMAND_BUFFER: Self = Self(0);
pub const RENDER_PASS: Self = Self(1);
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)]
#[repr(transparent)]

View file

@ -401,13 +401,16 @@ impl ConstVal {
}
}
pub trait ConstantExt {
fn constant(&self) -> Constant;
fn constant(&self, enum_name: &str) -> Constant;
fn variant_ident(&self, enum_name: &str) -> Ident;
fn notation(&self) -> Option<&str>;
fn is_alias(&self) -> bool {
false
}
}
impl ConstantExt for vkxml::ExtensionEnum {
fn constant(&self) -> Constant {
fn constant(&self, _enum_name: &str) -> Constant {
Constant::from_extension_enum(self).unwrap()
}
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 {
fn constant(&self) -> Constant {
fn constant(&self, _enum_name: &str) -> Constant {
Constant::from_constant(self)
}
fn variant_ident(&self, enum_name: &str) -> Ident {
@ -534,6 +554,53 @@ impl Constant {
.map(|e| Constant::CExpr(e.clone()));
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 {
@ -1007,7 +1074,7 @@ pub struct ExtensionConstant<'a> {
pub constant: Constant,
}
impl<'a> ConstantExt for ExtensionConstant<'a> {
fn constant(&self) -> Constant {
fn constant(&self, _enum_name: &str) -> Constant {
self.constant.clone()
}
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_values: &mut BTreeMap<Ident, Vec<ConstantMatchInfo>>,
) -> TokenStream {
use vk_parse::EnumSpec;
let items = extension_items
.iter()
.filter_map(|item| match item {
@ -1034,52 +1100,16 @@ pub fn generate_extension_constants<'a>(
})
.flat_map(|iter| iter);
let enum_tokens = items.filter_map(|item| match item {
vk_parse::InterfaceItem::Enum(_enum) => {
if const_cache.contains(_enum.name.as_str()) {
vk_parse::InterfaceItem::Enum(enum_) => {
if const_cache.contains(enum_.name.as_str()) {
return None;
}
let (constant, extends, is_alias) = match &_enum.spec {
EnumSpec::Bitpos { bitpos, extends } => {
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 (constant, extends, is_alias) =
Constant::from_vk_parse_enum_spec(&enum_.spec, None, Some(extension_number))?;
let extends = extends?;
let ext_constant = ExtensionConstant {
name: &_enum.name,
name: &enum_.name,
constant,
};
let ident = name_to_tokens(&extends);
@ -1097,7 +1127,7 @@ pub fn generate_extension_constants<'a>(
#impl_block
};
const_cache.insert(_enum.name.as_str());
const_cache.insert(enum_.name.as_str());
Some(q)
}
_ => None,
@ -1334,7 +1364,7 @@ pub fn bitflags_impl_block(
.iter()
.map(|constant| {
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 {
quote!(#constant)
} else {
@ -1346,8 +1376,10 @@ pub fn bitflags_impl_block(
let notations = constants.iter().map(|constant| {
constant.notation().map(|n| {
quote! {
#[doc = #n]
if n.to_lowercase().contains("backwards") {
quote!(#[deprecated = #n])
} else {
quote!(#[doc = #n])
}
})
});
@ -1370,39 +1402,52 @@ pub fn bitflags_impl_block(
}
pub fn generate_enum<'a>(
_enum: &'a vkxml::Enumeration,
enum_: &'a vk_parse::Enums,
const_cache: &mut HashSet<&'a str, impl BuildHasher>,
const_values: &mut BTreeMap<Ident, Vec<ConstantMatchInfo>>,
bitflags_cache: &mut HashSet<Ident, impl BuildHasher>,
) -> EnumType {
let name = &_enum.name[2..];
let _name = name.replace("FlagBits", "Flags");
let name = enum_.name.as_ref().unwrap();
let clean_name = name.strip_prefix("Vk").unwrap();
let _name = clean_name.replace("FlagBits", "Flags");
let ident = format_ident!("{}", _name.as_str());
let constants: Vec<_> = _enum
.elements
let constants = enum_
.children
.iter()
.filter_map(|elem| match *elem {
vkxml::EnumerationElement::Enum(ref constant) => Some(constant),
vk_parse::EnumsChild::Enum(ref constant) => Some(constant),
_ => 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();
let mut values = Vec::with_capacity(constants.len());
for constant in &constants {
const_cache.insert(constant.name.as_str());
values.push(ConstantMatchInfo {
ident: constant.variant_ident(&_enum.name),
is_alias: false,
ident: constant.variant_ident(name),
is_alias: constant.is_alias(),
});
}
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 all_bits = constants
.iter()
.filter_map(|constant| Constant::from_constant(constant).value())
.filter_map(|constant| constant.constant(name).value())
.fold(0, |acc, next| acc | next.bits());
let bit_string = format!("{:b}", all_bits);
let bit_string = interleave_number('_', 4, &bit_string);
@ -1411,7 +1456,7 @@ pub fn generate_enum<'a>(
if bitflags_cache.contains(&ident) {
EnumType::Bitflags(quote! {})
} 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());
let q = quote! {
#[repr(transparent)]
@ -1426,11 +1471,11 @@ pub fn generate_enum<'a>(
} else {
let (struct_attribute, special_quote) = match _name.as_str() {
//"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!()),
};
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! {
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[repr(transparent)]
@ -1452,19 +1497,19 @@ pub fn generate_enum<'a>(
}
}
pub fn generate_result(ident: Ident, _enum: &vkxml::Enumeration) -> TokenStream {
let notation = _enum.elements.iter().filter_map(|elem| {
pub fn generate_result(ident: Ident, enum_: &vk_parse::Enums) -> TokenStream {
let notation = enum_.children.iter().filter_map(|elem| {
let (variant_name, notation) = match *elem {
vkxml::EnumerationElement::Enum(ref constant) => (
vk_parse::EnumsChild::Enum(ref constant) => (
constant.name.as_str(),
constant.notation.as_deref().unwrap_or(""),
constant.comment.as_deref().unwrap_or(""),
),
_ => {
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! {
#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())
.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
.elements
.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 (enum_code, bitflags_code) = enums
.into_iter()
let (enum_code, bitflags_code) = spec2
.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))
.fold((Vec::new(), Vec::new()), |mut acc, elem| {
match elem {