spirv-val: Add Vulkan ForwardPointer VUID (#4089)

diff --git a/source/val/validate_type.cpp b/source/val/validate_type.cpp
index 5924c69..8352301 100644
--- a/source/val/validate_type.cpp
+++ b/source/val/validate_type.cpp
@@ -555,8 +555,8 @@
            << "Pointer type in OpTypeForwardPointer is not a pointer type.";
   }
 
-  if (inst->GetOperandAs<uint32_t>(1) !=
-      pointer_type_inst->GetOperandAs<uint32_t>(1)) {
+  const auto storage_class = inst->GetOperandAs<SpvStorageClass>(1);
+  if (storage_class != pointer_type_inst->GetOperandAs<uint32_t>(1)) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "Storage class in OpTypeForwardPointer does not match the "
            << "pointer definition.";
@@ -569,6 +569,15 @@
            << "Forward pointers must point to a structure";
   }
 
+  if (spvIsVulkanEnv(_.context()->target_env)) {
+    if (storage_class != SpvStorageClassPhysicalStorageBuffer) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << _.VkErrorID(4711)
+             << "In Vulkan, OpTypeForwardPointer must have "
+             << "a storage class of PhysicalStorageBuffer.";
+    }
+  }
+
   return SPV_SUCCESS;
 }
 
diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp
index e168c01..0fe9082 100644
--- a/source/val/validation_state.cpp
+++ b/source/val/validation_state.cpp
@@ -1671,6 +1671,8 @@
       return VUID_WRAP(VUID-StandaloneSpirv-OpImageTexelPointer-04658);
     case 4685:
       return VUID_WRAP(VUID-StandaloneSpirv-OpGroupNonUniformBallotBitCount-04685);
+    case 4711:
+      return VUID_WRAP(VUID-StandaloneSpirv-OpTypeForwardPointer-04711);
     default:
       return "";  // unknown id
   };
diff --git a/test/val/val_data_test.cpp b/test/val/val_data_test.cpp
index 1080a59..99a9aa0 100644
--- a/test/val/val_data_test.cpp
+++ b/test/val/val_data_test.cpp
@@ -960,6 +960,26 @@
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5));
 }
 
+TEST_F(ValidateData, VulkanTypeForwardStorageClass) {
+  std::string test = R"(
+OpCapability Shader
+OpCapability PhysicalStorageBufferAddresses
+OpMemoryModel Logical GLSL450
+OpTypeForwardPointer %1 Uniform
+%2 = OpTypeStruct
+%3 = OpTypeRuntimeArray %1
+%1 = OpTypePointer Uniform %2
+)";
+
+  CompileSuccessfully(test, SPV_ENV_VULKAN_1_2);
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpTypeForwardPointer-04711"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("In Vulkan, OpTypeForwardPointer must have "
+                        "a storage class of PhysicalStorageBuffer."));
+}
+
 TEST_F(ValidateData, TypeForwardReferenceMustBeForwardPointer) {
   std::string test = R"(
 OpCapability Shader