@@ -31914,3 +31914,231 @@ fn pointer_member_overlap_is_rejected() {
3191431914 .expect_err("int at offset 4 overlaps 8-byte pointer at offset 0");
3191531915 assert!(matches!(err, ValidationError::InvalidBlockLayout { .. }));
3191631916}
31917+
31918+ // ============================================================================
31919+ // Vulkan Block/BufferBlock decoration tests (#11)
31920+ // ============================================================================
31921+
31922+ /// PushConstant variable must have Block decoration in Vulkan (VUID-06675).
31923+ #[test]
31924+ fn vulkan_push_constant_must_have_block_decoration() {
31925+ // PushConstant WITHOUT Block decoration
31926+ let text = [
31927+ "OpCapability Shader",
31928+ "OpMemoryModel Logical GLSL450",
31929+ "OpMemberDecorate %struct 0 Offset 0",
31930+ "%void = OpTypeVoid",
31931+ "%fn = OpTypeFunction %void",
31932+ "%int = OpTypeInt 32 0",
31933+ "%struct = OpTypeStruct %int",
31934+ "%ptr = OpTypePointer PushConstant %struct",
31935+ "%var = OpVariable %ptr PushConstant",
31936+ "OpEntryPoint Vertex %main \"main\" %var",
31937+ "%main = OpFunction %void None %fn",
31938+ "%entry = OpLabel",
31939+ "OpReturn",
31940+ "OpFunctionEnd",
31941+ ]
31942+ .join("\n");
31943+ let err = text
31944+ .as_str()
31945+ .validate(TargetEnv::Vulkan1_0)
31946+ .expect_err("PushConstant without Block should fail in Vulkan");
31947+ assert!(matches!(
31948+ err,
31949+ ValidationError::VulkanBufferMissingBlockDecoration { .. }
31950+ ));
31951+ }
31952+
31953+ /// PushConstant with Block decoration should pass.
31954+ #[test]
31955+ fn vulkan_push_constant_with_block_is_valid() {
31956+ let text = [
31957+ "OpCapability Shader",
31958+ "OpMemoryModel Logical GLSL450",
31959+ "OpDecorate %struct Block",
31960+ "OpMemberDecorate %struct 0 Offset 0",
31961+ "%void = OpTypeVoid",
31962+ "%fn = OpTypeFunction %void",
31963+ "%int = OpTypeInt 32 0",
31964+ "%struct = OpTypeStruct %int",
31965+ "%ptr = OpTypePointer PushConstant %struct",
31966+ "%var = OpVariable %ptr PushConstant",
31967+ "OpEntryPoint Vertex %main \"main\" %var",
31968+ "%main = OpFunction %void None %fn",
31969+ "%entry = OpLabel",
31970+ "OpReturn",
31971+ "OpFunctionEnd",
31972+ ]
31973+ .join("\n");
31974+ text.as_str()
31975+ .validate(TargetEnv::Vulkan1_0)
31976+ .expect("PushConstant with Block decoration should pass");
31977+ }
31978+
31979+ /// StorageBuffer with BufferBlock is disallowed in Vulkan (VUID-06675).
31980+ #[test]
31981+ fn vulkan_storage_buffer_with_buffer_block_is_rejected() {
31982+ let text = [
31983+ "OpCapability Shader",
31984+ "OpMemoryModel Logical GLSL450",
31985+ "OpDecorate %struct BufferBlock",
31986+ "OpDecorate %var DescriptorSet 0",
31987+ "OpDecorate %var Binding 0",
31988+ "OpMemberDecorate %struct 0 Offset 0",
31989+ "%int = OpTypeInt 32 0",
31990+ "%struct = OpTypeStruct %int",
31991+ "%ptr = OpTypePointer StorageBuffer %struct",
31992+ "%var = OpVariable %ptr StorageBuffer",
31993+ ]
31994+ .join("\n");
31995+ let err = text
31996+ .as_str()
31997+ .validate(TargetEnv::Vulkan1_1)
31998+ .expect_err("StorageBuffer with BufferBlock should fail in Vulkan");
31999+ assert!(matches!(
32000+ err,
32001+ ValidationError::VulkanStorageBufferHasBufferBlock { .. }
32002+ ));
32003+ }
32004+
32005+ /// StorageBuffer without Block is rejected in Vulkan (VUID-06675).
32006+ #[test]
32007+ fn vulkan_storage_buffer_must_have_block_decoration() {
32008+ let text = [
32009+ "OpCapability Shader",
32010+ "OpMemoryModel Logical GLSL450",
32011+ "OpDecorate %var DescriptorSet 0",
32012+ "OpDecorate %var Binding 0",
32013+ "OpMemberDecorate %struct 0 Offset 0",
32014+ "%int = OpTypeInt 32 0",
32015+ "%struct = OpTypeStruct %int",
32016+ "%ptr = OpTypePointer StorageBuffer %struct",
32017+ "%var = OpVariable %ptr StorageBuffer",
32018+ ]
32019+ .join("\n");
32020+ let err = text
32021+ .as_str()
32022+ .validate(TargetEnv::Vulkan1_1)
32023+ .expect_err("StorageBuffer without Block should fail in Vulkan");
32024+ assert!(matches!(
32025+ err,
32026+ ValidationError::VulkanBufferMissingBlockDecoration { .. }
32027+ ));
32028+ }
32029+
32030+ /// StorageBuffer with Block decoration should pass.
32031+ #[test]
32032+ fn vulkan_storage_buffer_with_block_is_valid() {
32033+ let text = [
32034+ "OpCapability Shader",
32035+ "OpMemoryModel Logical GLSL450",
32036+ "OpDecorate %struct Block",
32037+ "OpDecorate %var DescriptorSet 0",
32038+ "OpDecorate %var Binding 0",
32039+ "OpMemberDecorate %struct 0 Offset 0",
32040+ "%int = OpTypeInt 32 0",
32041+ "%struct = OpTypeStruct %int",
32042+ "%ptr = OpTypePointer StorageBuffer %struct",
32043+ "%var = OpVariable %ptr StorageBuffer",
32044+ ]
32045+ .join("\n");
32046+ text.as_str()
32047+ .validate(TargetEnv::Vulkan1_1)
32048+ .expect("StorageBuffer with Block decoration should pass");
32049+ }
32050+
32051+ /// Uniform without Block or BufferBlock is rejected in Vulkan (VUID-06676).
32052+ #[test]
32053+ fn vulkan_uniform_must_have_block_or_buffer_block() {
32054+ let text = [
32055+ "OpCapability Shader",
32056+ "OpMemoryModel Logical GLSL450",
32057+ "OpDecorate %var DescriptorSet 0",
32058+ "OpDecorate %var Binding 0",
32059+ "OpMemberDecorate %struct 0 Offset 0",
32060+ "%int = OpTypeInt 32 0",
32061+ "%struct = OpTypeStruct %int",
32062+ "%ptr = OpTypePointer Uniform %struct",
32063+ "%var = OpVariable %ptr Uniform",
32064+ ]
32065+ .join("\n");
32066+ let err = text
32067+ .as_str()
32068+ .validate(TargetEnv::Vulkan1_0)
32069+ .expect_err("Uniform without Block or BufferBlock should fail in Vulkan");
32070+ assert!(matches!(
32071+ err,
32072+ ValidationError::VulkanUniformMissingBlockDecoration { .. }
32073+ ));
32074+ }
32075+
32076+ /// Uniform with Block decoration should pass.
32077+ #[test]
32078+ fn vulkan_uniform_with_block_is_valid() {
32079+ let text = [
32080+ "OpCapability Shader",
32081+ "OpMemoryModel Logical GLSL450",
32082+ "OpDecorate %struct Block",
32083+ "OpDecorate %var DescriptorSet 0",
32084+ "OpDecorate %var Binding 0",
32085+ "OpMemberDecorate %struct 0 Offset 0",
32086+ "%int = OpTypeInt 32 0",
32087+ "%struct = OpTypeStruct %int",
32088+ "%ptr = OpTypePointer Uniform %struct",
32089+ "%var = OpVariable %ptr Uniform",
32090+ ]
32091+ .join("\n");
32092+ text.as_str()
32093+ .validate(TargetEnv::Vulkan1_0)
32094+ .expect("Uniform with Block decoration should pass");
32095+ }
32096+
32097+ /// Uniform with BufferBlock decoration should also pass.
32098+ #[test]
32099+ fn vulkan_uniform_with_buffer_block_is_valid() {
32100+ let text = [
32101+ "OpCapability Shader",
32102+ "OpMemoryModel Logical GLSL450",
32103+ "OpDecorate %struct BufferBlock",
32104+ "OpDecorate %var DescriptorSet 0",
32105+ "OpDecorate %var Binding 0",
32106+ "OpMemberDecorate %struct 0 Offset 0",
32107+ "%int = OpTypeInt 32 0",
32108+ "%struct = OpTypeStruct %int",
32109+ "%ptr = OpTypePointer Uniform %struct",
32110+ "%var = OpVariable %ptr Uniform",
32111+ ]
32112+ .join("\n");
32113+ text.as_str()
32114+ .validate(TargetEnv::Vulkan1_0)
32115+ .expect("Uniform with BufferBlock decoration should pass");
32116+ }
32117+
32118+ /// Vulkan-specific checks use VUID error types. Under Universal, the base
32119+ /// SPIR-V spec also requires Block on StorageBuffer, but with a different
32120+ /// error type (MissingBlockDecoration vs VulkanBufferMissingBlockDecoration).
32121+ #[test]
32122+ fn universal_env_uses_base_spec_error_for_missing_block() {
32123+ let text = [
32124+ "OpCapability Shader",
32125+ "OpMemoryModel Logical GLSL450",
32126+ "OpDecorate %var DescriptorSet 0",
32127+ "OpDecorate %var Binding 0",
32128+ "OpMemberDecorate %struct 0 Offset 0",
32129+ "%int = OpTypeInt 32 0",
32130+ "%struct = OpTypeStruct %int",
32131+ "%ptr = OpTypePointer StorageBuffer %struct",
32132+ "%var = OpVariable %ptr StorageBuffer",
32133+ ]
32134+ .join("\n");
32135+ // Under Universal, the base spec catches the missing Block decoration
32136+ let err = text
32137+ .as_str()
32138+ .validate(TargetEnv::Universal1_6)
32139+ .expect_err("Missing Block should fail in any env");
32140+ assert!(matches!(
32141+ err,
32142+ ValidationError::MissingBlockDecoration { .. }
32143+ ));
32144+ }
0 commit comments