diff --git a/src/back/glsl/mod.rs b/src/back/glsl/mod.rs index d6aff795c9..653826ae41 100644 --- a/src/back/glsl/mod.rs +++ b/src/back/glsl/mod.rs @@ -484,7 +484,13 @@ impl<'a, W: Write> Writer<'a, W> { // Generate a map with names required to write the module let mut names = crate::FastHashMap::default(); let mut namer = proc::Namer::default(); - namer.reset(module, keywords::RESERVED_KEYWORDS, &["gl_"], &mut names); + namer.reset( + module, + keywords::RESERVED_KEYWORDS, + &[], + &["gl_"], + &mut names, + ); // Build the instance let mut this = Self { diff --git a/src/back/hlsl/keywords.rs b/src/back/hlsl/keywords.rs index 7519b767a1..459db245ee 100644 --- a/src/back/hlsl/keywords.rs +++ b/src/back/hlsl/keywords.rs @@ -4,9 +4,23 @@ HLSL Reserved Words - */ +// When compiling with FXC without strict mode, these keywords are actually case insensitive. +// If you compile with strict mode and specify a different casing like "Pass" instead in an identifier, FXC will give this error: +// "error X3086: alternate cases for 'pass' are deprecated in strict mode" +// This behavior is not documented anywhere, but as far as I can tell this is the full list. +pub const RESERVED_CASE_INSENSITIVE: &[&str] = &[ + "asm", + "decl", + "pass", + "technique", + "Texture1D", + "Texture2D", + "Texture3D", + "TextureCube", +]; + pub const RESERVED: &[&str] = &[ "AppendStructuredBuffer", - "asm", "asm_fragment", "BlendState", "bool", @@ -68,7 +82,6 @@ pub const RESERVED: &[&str] = &[ "out", "OutputPatch", "packoffset", - "pass", "pixelfragment", "PixelShader", "point", @@ -101,18 +114,13 @@ pub const RESERVED: &[&str] = &[ "switch", "StructuredBuffer", "tbuffer", - "technique", "technique10", "technique11", "texture", - "Texture1D", "Texture1DArray", - "Texture2D", "Texture2DArray", "Texture2DMS", "Texture2DMSArray", - "Texture3D", - "TextureCube", "TextureCubeArray", "true", "typedef", diff --git a/src/back/hlsl/writer.rs b/src/back/hlsl/writer.rs index 0b08dbe0a1..940f0ae373 100644 --- a/src/back/hlsl/writer.rs +++ b/src/back/hlsl/writer.rs @@ -89,8 +89,13 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { fn reset(&mut self, module: &Module) { self.names.clear(); - self.namer - .reset(module, super::keywords::RESERVED, &[], &mut self.names); + self.namer.reset( + module, + super::keywords::RESERVED, + super::keywords::RESERVED_CASE_INSENSITIVE, + &[], + &mut self.names, + ); self.entry_point_io.clear(); self.named_expressions.clear(); self.wrapped.clear(); diff --git a/src/back/msl/writer.rs b/src/back/msl/writer.rs index 07a79f7497..cfef6645b6 100644 --- a/src/back/msl/writer.rs +++ b/src/back/msl/writer.rs @@ -3033,7 +3033,7 @@ impl Writer { ) -> Result { self.names.clear(); self.namer - .reset(module, super::keywords::RESERVED, &[], &mut self.names); + .reset(module, super::keywords::RESERVED, &[], &[], &mut self.names); self.struct_member_pads.clear(); writeln!( diff --git a/src/back/wgsl/writer.rs b/src/back/wgsl/writer.rs index da9f605fe3..d26ceda487 100644 --- a/src/back/wgsl/writer.rs +++ b/src/back/wgsl/writer.rs @@ -86,6 +86,7 @@ impl Writer { module, crate::keywords::wgsl::RESERVED, // an identifier must not start with two underscore + &[], &["__"], &mut self.names, ); diff --git a/src/proc/namer.rs b/src/proc/namer.rs index 053126b8ac..8227b50d89 100644 --- a/src/proc/namer.rs +++ b/src/proc/namer.rs @@ -1,5 +1,6 @@ use crate::{arena::Handle, FastHashMap, FastHashSet}; use std::borrow::Cow; +use std::hash::{Hash, Hasher}; pub type EntryPointIndex = u16; const SEPARATOR: char = '_'; @@ -25,6 +26,7 @@ pub struct Namer { /// The last numeric suffix used for each base name. Zero means "no suffix". unique: FastHashMap, keywords: FastHashSet, + keywords_case_insensitive: FastHashSet>, reserved_prefixes: Vec, } @@ -102,7 +104,12 @@ impl Namer { } None => { let mut suffixed = base.to_string(); - if base.ends_with(char::is_numeric) || self.keywords.contains(base.as_ref()) { + if base.ends_with(char::is_numeric) + || self.keywords.contains(base.as_ref()) + || self + .keywords_case_insensitive + .contains(&AsciiUniCase(base.as_ref())) + { suffixed.push(SEPARATOR); } debug_assert!(!self.keywords.contains(&suffixed)); @@ -137,6 +144,7 @@ impl Namer { &mut self, module: &crate::Module, reserved_keywords: &[&str], + reserved_keywords_case_insensitive: &[&'static str], reserved_prefixes: &[&str], output: &mut FastHashMap, ) { @@ -148,6 +156,17 @@ impl Namer { self.keywords.clear(); self.keywords .extend(reserved_keywords.iter().map(|string| (string.to_string()))); + + debug_assert!(reserved_keywords_case_insensitive + .iter() + .all(|s| s.is_ascii())); + self.keywords_case_insensitive.clear(); + self.keywords_case_insensitive.extend( + reserved_keywords_case_insensitive + .iter() + .map(|string| (AsciiUniCase(*string))), + ); + let mut temp = String::new(); for (ty_handle, ty) in module.types.iter() { @@ -252,6 +271,33 @@ impl Namer { } } +/// A string wrapper type with an ascii case insensitive Eq and Hash impl +struct AsciiUniCase + ?Sized>(S); + +impl> PartialEq for AsciiUniCase { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.0.as_ref().eq_ignore_ascii_case(other.0.as_ref()) + } +} + +impl> Eq for AsciiUniCase {} + +impl> Hash for AsciiUniCase { + #[inline] + fn hash(&self, hasher: &mut H) { + for byte in self + .0 + .as_ref() + .as_bytes() + .iter() + .map(|b| b.to_ascii_lowercase()) + { + hasher.write_u8(byte); + } + } +} + #[test] fn test() { let mut namer = Namer::default(); diff --git a/tests/in/hlsl-keyword.wgsl b/tests/in/hlsl-keyword.wgsl new file mode 100644 index 0000000000..e2e4e722d2 --- /dev/null +++ b/tests/in/hlsl-keyword.wgsl @@ -0,0 +1,6 @@ +@fragment +fn fs_main() -> @location(0) vec4f { + // Make sure case-insensitive keywords are escaped in HLSL. + var Pass = vec4(1.0,1.0,1.0,1.0); + return Pass; +} \ No newline at end of file diff --git a/tests/out/hlsl/hlsl-keyword.hlsl b/tests/out/hlsl/hlsl-keyword.hlsl new file mode 100644 index 0000000000..efdeb9960f --- /dev/null +++ b/tests/out/hlsl/hlsl-keyword.hlsl @@ -0,0 +1,9 @@ + +float4 fs_main() : SV_Target0 +{ + float4 Pass_ = (float4)0; + + Pass_ = float4(1.0, 1.0, 1.0, 1.0); + float4 _expr6 = Pass_; + return _expr6; +} diff --git a/tests/out/hlsl/hlsl-keyword.hlsl.config b/tests/out/hlsl/hlsl-keyword.hlsl.config new file mode 100644 index 0000000000..b9f047558a --- /dev/null +++ b/tests/out/hlsl/hlsl-keyword.hlsl.config @@ -0,0 +1,3 @@ +vertex=() +fragment=(fs_main:ps_5_1 ) +compute=() diff --git a/tests/snapshots.rs b/tests/snapshots.rs index 7a0d4333db..8f6fa3a8f7 100644 --- a/tests/snapshots.rs +++ b/tests/snapshots.rs @@ -584,6 +584,7 @@ fn convert_wgsl() { ("force_point_size_vertex_shader_webgl", Targets::GLSL), ("invariant", Targets::GLSL), ("ray-query", Targets::SPIRV | Targets::METAL), + ("hlsl-keyword", Targets::HLSL), ]; for &(name, targets) in inputs.iter() {