Fail validation when RelaxedPrecision is applied to a type. (#4823)

* Fail validation when RelaxedPrecision is applied to a type.

Fixes #4723
Fixes #4725

* Fixup invalid test
diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp
index b9448e3..907c184 100644
--- a/source/val/validate_decorations.cpp
+++ b/source/val/validate_decorations.cpp
@@ -1661,6 +1661,24 @@
             "of a structure type";
 }
 
+spv_result_t CheckRelaxPrecisionDecoration(ValidationState_t& vstate,
+                                           const Instruction& inst,
+                                           const Decoration& decoration) {
+  // This is not the most precise check, but the rules for RelaxPrecision are
+  // very general, and it will be difficult to implement precisely.  For now,
+  // I will only check for the cases that cause problems for the optimizer.
+  if (!spvOpcodeGeneratesType(inst.opcode())) {
+    return SPV_SUCCESS;
+  }
+
+  if (decoration.struct_member_index() != Decoration::kInvalidMember &&
+      inst.opcode() == SpvOpTypeStruct) {
+    return SPV_SUCCESS;
+  }
+  return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+         << "RelaxPrecision decoration cannot be applied to a type";
+}
+
 #define PASS_OR_BAIL_AT_LINE(X, LINE)           \
   {                                             \
     spv_result_t e##LINE = (X);                 \
@@ -1715,6 +1733,10 @@
         case SpvDecorationLocation:
           PASS_OR_BAIL(CheckLocationDecoration(vstate, *inst, decoration));
           break;
+        case SpvDecorationRelaxedPrecision:
+          PASS_OR_BAIL(
+              CheckRelaxPrecisionDecoration(vstate, *inst, decoration));
+          break;
         default:
           break;
       }
diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp
index 3c68263..1af45dd 100644
--- a/test/val/val_decoration_test.cpp
+++ b/test/val/val_decoration_test.cpp
@@ -50,20 +50,20 @@
     OpCapability Shader
     OpCapability Linkage
     OpMemoryModel Logical GLSL450
-    OpDecorate %1 ArrayStride 4
-    OpDecorate %1 RelaxedPrecision
+    OpDecorate %1 Location 4
+    OpDecorate %1 Centroid
     %2 = OpTypeFloat 32
-    %1 = OpTypeRuntimeArray %2
+    %3 = OpTypePointer Output %2
+    %1 = OpVariable %3 Output
     ; Since %1 is used first in Decoration, it gets id 1.
 )";
   const uint32_t id = 1;
   CompileSuccessfully(spirv);
   EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
   // Must have 2 decorations.
-  EXPECT_THAT(
-      vstate_->id_decorations(id),
-      Eq(std::set<Decoration>{Decoration(SpvDecorationArrayStride, {4}),
-                              Decoration(SpvDecorationRelaxedPrecision)}));
+  EXPECT_THAT(vstate_->id_decorations(id),
+              Eq(std::set<Decoration>{Decoration(SpvDecorationLocation, {4}),
+                                      Decoration(SpvDecorationCentroid)}));
 }
 
 TEST_F(ValidateDecorations, ValidateOpMemberDecorateRegistration) {
@@ -8363,6 +8363,52 @@
               HasSubstr("PerVertexKHR must be declared as arrays"));
 }
 
+TEST_F(ValidateDecorations, RelaxedPrecisionDecorationOnNumericTypeBad) {
+  const spv_target_env env = SPV_ENV_VULKAN_1_0;
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpDecorate %float RelaxedPrecision
+       %void = OpTypeVoid
+      %voidfn = OpTypeFunction %void
+      %float = OpTypeFloat 32
+       %main = OpFunction %void None %voidfn
+      %label = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, env);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("RelaxPrecision decoration cannot be applied to a type"));
+}
+
+TEST_F(ValidateDecorations, RelaxedPrecisionDecorationOnStructMember) {
+  const spv_target_env env = SPV_ENV_VULKAN_1_0;
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main"
+               OpExecutionMode %main OriginUpperLeft
+               OpMemberDecorate %struct 0 RelaxedPrecision
+       %void = OpTypeVoid
+     %voidfn = OpTypeFunction %void
+      %float = OpTypeFloat 32
+     %struct = OpTypeStruct %float
+       %main = OpFunction %void None %voidfn
+      %label = OpLabel
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, env);
+  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
+}
+
 }  // namespace
 }  // namespace val
 }  // namespace spvtools