Extra small storage validation (#2732)

Fixes #2729

* Check acceptable uses of small type generators
diff --git a/Android.mk b/Android.mk
index 8e44378..075dc61 100644
--- a/Android.mk
+++ b/Android.mk
@@ -70,6 +70,7 @@
 		source/val/validate_non_uniform.cpp \
 		source/val/validate_primitives.cpp \
 		source/val/validate_scopes.cpp \
+		source/val/validate_small_type_uses.cpp \
 		source/val/validate_type.cpp
 
 SPVTOOLS_OPT_SRC_FILES := \
diff --git a/BUILD.gn b/BUILD.gn
index ad71c1d..3c2a9f6 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -428,6 +428,7 @@
     "source/val/validate_non_uniform.cpp",
     "source/val/validate_primitives.cpp",
     "source/val/validate_scopes.cpp",
+    "source/val/validate_small_type_uses.cpp",
     "source/val/validate_type.cpp",
     "source/val/validation_state.cpp",
   ]
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index bd4c465..cb63ff0 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -304,6 +304,7 @@
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_non_uniform.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_primitives.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_scopes.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_small_type_uses.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_type.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/decoration.h
   ${CMAKE_CURRENT_SOURCE_DIR}/val/basic_block.cpp
diff --git a/source/val/validate.cpp b/source/val/validate.cpp
index 0840662..6f1a26c 100644
--- a/source/val/validate.cpp
+++ b/source/val/validate.cpp
@@ -412,6 +412,7 @@
   // those checks register the limitation checked here.
   for (const auto inst : vstate->ordered_instructions()) {
     if (auto error = ValidateExecutionLimitations(*vstate, &inst)) return error;
+    if (auto error = ValidateSmallTypeUses(*vstate, &inst)) return error;
   }
 
   return SPV_SUCCESS;
diff --git a/source/val/validate.h b/source/val/validate.h
index aaae570..b6c4072 100644
--- a/source/val/validate.h
+++ b/source/val/validate.h
@@ -208,6 +208,13 @@
 spv_result_t ValidateExecutionLimitations(ValidationState_t& _,
                                           const Instruction* inst);
 
+/// Validates restricted  uses of 8- and 16-bit types.
+///
+/// Validates shaders that uses 8- or 16-bit storage capabilities, but not full
+/// capabilities only have appropriate uses of those types.
+spv_result_t ValidateSmallTypeUses(ValidationState_t& _,
+                                   const Instruction* inst);
+
 /// @brief Validate the ID's within a SPIR-V binary
 ///
 /// @param[in] pInstructions array of instructions
diff --git a/source/val/validate_misc.cpp b/source/val/validate_misc.cpp
index 8e007ed..28e3fc6 100644
--- a/source/val/validate_misc.cpp
+++ b/source/val/validate_misc.cpp
@@ -22,9 +22,30 @@
 
 namespace spvtools {
 namespace val {
+namespace {
+
+spv_result_t ValidateUndef(ValidationState_t& _, const Instruction* inst) {
+  if (_.HasCapability(SpvCapabilityShader) &&
+      _.ContainsLimitedUseIntOrFloatType(inst->type_id()) &&
+      !_.IsPointerType(inst->type_id())) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Cannot create undefined values with 8- or 16-bit types";
+  }
+
+  return SPV_SUCCESS;
+}
+
+}  // namespace
 
 spv_result_t MiscPass(ValidationState_t& _, const Instruction* inst) {
   switch (inst->opcode()) {
+    case SpvOpUndef:
+      if (auto error = ValidateUndef(_, inst)) return error;
+      break;
+    default:
+      break;
+  }
+  switch (inst->opcode()) {
     case SpvOpBeginInvocationInterlockEXT:
     case SpvOpEndInvocationInterlockEXT:
       _.function(inst->function()->id())
diff --git a/source/val/validate_small_type_uses.cpp b/source/val/validate_small_type_uses.cpp
new file mode 100644
index 0000000..9db82e7
--- /dev/null
+++ b/source/val/validate_small_type_uses.cpp
@@ -0,0 +1,57 @@
+// Copyright (c) 2019 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/val/validate.h"
+
+#include "source/val/instruction.h"
+#include "source/val/validation_state.h"
+
+namespace spvtools {
+namespace val {
+
+spv_result_t ValidateSmallTypeUses(ValidationState_t& _,
+                                   const Instruction* inst) {
+  if (!_.HasCapability(SpvCapabilityShader) || inst->type_id() == 0 ||
+      !_.ContainsLimitedUseIntOrFloatType(inst->type_id())) {
+    return SPV_SUCCESS;
+  }
+
+  if (_.IsPointerType(inst->type_id())) return SPV_SUCCESS;
+
+  // The validator should previously have checked ways to generate 8- or 16-bit
+  // types. So we only need to considervalid paths from source to sink.
+  // When restricted, uses of 8- or 16-bit types can only be stores,
+  // width-only conversions, decorations and copy object.
+  for (auto use : inst->uses()) {
+    const auto* user = use.first;
+    switch (user->opcode()) {
+      case SpvOpDecorate:
+      case SpvOpDecorateId:
+      case SpvOpCopyObject:
+      case SpvOpStore:
+      case SpvOpFConvert:
+      case SpvOpUConvert:
+      case SpvOpSConvert:
+        break;
+      default:
+        return _.diag(SPV_ERROR_INVALID_ID, user)
+               << "Invalid use of 8- or 16-bit result";
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+}  // namespace val
+}  // namespace spvtools
diff --git a/test/val/CMakeLists.txt b/test/val/CMakeLists.txt
index 8f4bc33..b52c764 100644
--- a/test/val/CMakeLists.txt
+++ b/test/val/CMakeLists.txt
@@ -61,6 +61,7 @@
        val_literals_test.cpp
        val_logicals_test.cpp
        val_memory_test.cpp
+       val_misc_test.cpp
        val_modes_test.cpp
        val_non_uniform_test.cpp
        val_opencl_test.cpp
@@ -72,6 +73,7 @@
 
 add_spvtools_unittest(TARGET val_stuvw
   SRCS
+       val_small_type_uses_test.cpp
        val_ssa_test.cpp
        val_state_test.cpp
        val_storage_test.cpp
diff --git a/test/val/val_misc_test.cpp b/test/val/val_misc_test.cpp
new file mode 100644
index 0000000..350f561
--- /dev/null
+++ b/test/val/val_misc_test.cpp
@@ -0,0 +1,88 @@
+// Copyright (c) 2019 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Validation tests for misc instructions
+
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "test/unit_spirv.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::Eq;
+using ::testing::HasSubstr;
+
+using ValidateMisc = spvtest::ValidateBase<bool>;
+
+TEST_F(ValidateMisc, UndefRestrictedShort) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability StorageBuffer16BitAccess
+OpExtension "SPV_KHR_16bit_storage"
+OpMemoryModel Logical GLSL450
+%short = OpTypeInt 16 0
+%undef = OpUndef %short
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Cannot create undefined values with 8- or 16-bit types"));
+}
+
+TEST_F(ValidateMisc, UndefRestrictedChar) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability StorageBuffer8BitAccess
+OpExtension "SPV_KHR_8bit_storage"
+OpMemoryModel Logical GLSL450
+%char = OpTypeInt 8 0
+%undef = OpUndef %char
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Cannot create undefined values with 8- or 16-bit types"));
+}
+
+TEST_F(ValidateMisc, UndefRestrictedHalf) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Linkage
+OpCapability StorageBuffer16BitAccess
+OpExtension "SPV_KHR_16bit_storage"
+OpMemoryModel Logical GLSL450
+%half = OpTypeFloat 16
+%undef = OpUndef %half
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Cannot create undefined values with 8- or 16-bit types"));
+}
+}  // namespace
+}  // namespace val
+}  // namespace spvtools
diff --git a/test/val/val_small_type_uses_test.cpp b/test/val/val_small_type_uses_test.cpp
new file mode 100644
index 0000000..b950af5
--- /dev/null
+++ b/test/val/val_small_type_uses_test.cpp
@@ -0,0 +1,338 @@
+// Copyright (c) 2019 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Validation tests for 8- and 16-bit type uses.
+
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "test/unit_spirv.h"
+#include "test/val/val_code_generator.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::Eq;
+using ::testing::HasSubstr;
+using ::testing::Values;
+
+using ValidateSmallTypeUses = spvtest::ValidateBase<bool>;
+
+CodeGenerator GetSmallTypesGenerator() {
+  CodeGenerator generator;
+  generator.capabilities_ = R"(
+OpCapability Shader
+OpCapability StorageBuffer16BitAccess
+OpCapability StorageBuffer8BitAccess
+)";
+  generator.extensions_ = R"(
+OpExtension "SPV_KHR_16bit_storage"
+OpExtension "SPV_KHR_8bit_storage"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+%ext = OpExtInstImport "GLSL.std.450"
+)";
+  generator.memory_model_ = "OpMemoryModel Logical GLSL450\n";
+  std::string body = R"(
+%short_gep = OpAccessChain %ptr_ssbo_short %var %int_0 %int_0
+%ld_short = OpLoad %short %short_gep
+%short_to_int = OpSConvert %int %ld_short
+%short_to_uint = OpUConvert %int %ld_short
+%short_to_char = OpSConvert %char %ld_short
+%short_to_uchar = OpSConvert %char %ld_short
+%short2_gep = OpAccessChain %ptr_ssbo_short2 %var %int_0
+%ld_short2 = OpLoad %short2 %short2_gep
+%short2_to_int2 = OpSConvert %int2 %ld_short2
+%short2_to_uint2 = OpUConvert %int2 %ld_short2
+%short2_to_char2 = OpSConvert %char2 %ld_short2
+%short2_to_uchar2 = OpSConvert %char2 %ld_short2
+
+%char_gep = OpAccessChain %ptr_ssbo_char %var %int_2 %int_0
+%ld_char = OpLoad %char %char_gep
+%char_to_int = OpSConvert %int %ld_char
+%char_to_uint = OpUConvert %int %ld_char
+%char_to_short = OpSConvert %short %ld_char
+%char_to_ushort = OpSConvert %short %ld_char
+%char2_gep = OpAccessChain %ptr_ssbo_char2 %var %int_2
+%ld_char2 = OpLoad %char2 %char2_gep
+%char2_to_int2 = OpSConvert %int2 %ld_char2
+%char2_to_uint2 = OpUConvert %int2 %ld_char2
+%char2_to_short2 = OpSConvert %short2 %ld_char2
+%char2_to_ushort2 = OpSConvert %short2 %ld_char2
+
+%half_gep = OpAccessChain %ptr_ssbo_half %var %int_1 %int_0
+%ld_half = OpLoad %half %half_gep
+%half_to_float = OpFConvert %float %ld_half
+%half2_gep = OpAccessChain %ptr_ssbo_half2 %var %int_1
+%ld_half2 = OpLoad %half2 %half2_gep
+%half2_to_float2 = OpFConvert %float2 %ld_half2
+
+%int_to_short = OpSConvert %short %int_0
+%int_to_ushort = OpUConvert %short %int_0
+%int_to_char = OpSConvert %char %int_0
+%int_to_uchar = OpUConvert %char %int_0
+%int2_to_short2 = OpSConvert %short2 %int2_0
+%int2_to_ushort2 = OpUConvert %short2 %int2_0
+%int2_to_char2 = OpSConvert %char2 %int2_0
+%int2_to_uchar2 = OpUConvert %char2 %int2_0
+%int_gep = OpAccessChain %ptr_ssbo_int %var %int_3 %int_0
+%int2_gep = OpAccessChain %ptr_ssbo_int2 %var %int_3
+
+%float_to_half = OpFConvert %half %float_0
+%float2_to_half2 = OpFConvert %half2 %float2_0
+%float_gep = OpAccessChain %ptr_ssbo_float %var %int_4 %int_0
+%float2_gep = OpAccessChain %ptr_ssbo_float2 %var %int_4
+)";
+  generator.entry_points_.push_back(
+      {"foo", "GLCompute", "OpExecutionMode %foo LocalSize 1 1 1", body, ""});
+  generator.before_types_ = R"(
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 1 Offset 8
+OpMemberDecorate %block 2 Offset 16
+OpMemberDecorate %block 3 Offset 32
+OpMemberDecorate %block 4 Offset 64
+)";
+  generator.types_ = R"(
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int2 = OpTypeVector %int 2
+%float = OpTypeFloat 32
+%float2 = OpTypeVector %float 2
+%bool = OpTypeBool
+%bool2 = OpTypeVector %bool 2
+%char = OpTypeInt 8 0
+%char2 = OpTypeVector %char 2
+%ptr_ssbo_char = OpTypePointer StorageBuffer %char
+%ptr_ssbo_char2 = OpTypePointer StorageBuffer %char2
+%short = OpTypeInt 16 0
+%short2 = OpTypeVector %short 2
+%ptr_ssbo_short = OpTypePointer StorageBuffer %short
+%ptr_ssbo_short2 = OpTypePointer StorageBuffer %short2
+%half = OpTypeFloat 16
+%half2 = OpTypeVector %half 2
+%ptr_ssbo_half = OpTypePointer StorageBuffer %half
+%ptr_ssbo_half2 = OpTypePointer StorageBuffer %half2
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%ptr_ssbo_int2 = OpTypePointer StorageBuffer %int2
+%ptr_ssbo_float = OpTypePointer StorageBuffer %float
+%ptr_ssbo_float2 = OpTypePointer StorageBuffer %float2
+%block = OpTypeStruct %short2 %half2 %char2 %int2 %float2
+%ptr_ssbo_block = OpTypePointer StorageBuffer %block
+%func = OpTypeFunction %void
+)";
+  generator.after_types_ = R"(
+%var = OpVariable %ptr_ssbo_block StorageBuffer
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_2 = OpConstant %int 2
+%int_3 = OpConstant %int 3
+%int_4 = OpConstant %int 4
+%int2_0 = OpConstantComposite %int2 %int_0 %int_0
+%float_0 = OpConstant %float 0
+%float2_0 = OpConstantComposite %float2 %float_0 %float_0
+
+%short_func_ty = OpTypeFunction %void %short
+%char_func_ty = OpTypeFunction %void %char
+%half_func_ty = OpTypeFunction %void %half
+)";
+  generator.add_at_the_end_ = R"(
+%short_func = OpFunction %void None %short_func_ty
+%short_param = OpFunctionParameter %short
+%short_func_entry = OpLabel
+OpReturn
+OpFunctionEnd
+%char_func = OpFunction %void None %char_func_ty
+%char_param = OpFunctionParameter %char
+%char_func_entry = OpLabel
+OpReturn
+OpFunctionEnd
+%half_func = OpFunction %void None %half_func_ty
+%half_param = OpFunctionParameter %half
+%half_func_entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  return generator;
+}
+
+TEST_F(ValidateSmallTypeUses, BadCharPhi) {
+  CodeGenerator generator = GetSmallTypesGenerator();
+  generator.entry_points_[0].body += R"(
+OpBranch %next_block
+%next_block = OpLabel
+%phi = OpPhi %char %ld_char %foo_entry
+)";
+
+  CompileSuccessfully(generator.Build());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Invalid use of 8- or 16-bit result"));
+}
+
+TEST_F(ValidateSmallTypeUses, BadShortPhi) {
+  CodeGenerator generator = GetSmallTypesGenerator();
+  generator.entry_points_[0].body += R"(
+OpBranch %next_block
+%next_block = OpLabel
+%phi = OpPhi %short %ld_short %foo_entry
+)";
+
+  CompileSuccessfully(generator.Build());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Invalid use of 8- or 16-bit result"));
+}
+
+TEST_F(ValidateSmallTypeUses, BadHalfPhi) {
+  CodeGenerator generator = GetSmallTypesGenerator();
+  generator.entry_points_[0].body += R"(
+OpBranch %next_block
+%next_block = OpLabel
+%phi = OpPhi %half %ld_half %foo_entry
+)";
+
+  CompileSuccessfully(generator.Build());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Invalid use of 8- or 16-bit result"));
+}
+
+using ValidateGoodUses = spvtest::ValidateBase<std::string>;
+
+TEST_P(ValidateGoodUses, Inst) {
+  const std::string inst = GetParam();
+  CodeGenerator generator = GetSmallTypesGenerator();
+  generator.entry_points_[0].body += inst + "\n";
+
+  CompileSuccessfully(generator.Build());
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    SmallTypeUsesValid, ValidateGoodUses,
+    Values(
+        "%inst = OpIAdd %int %short_to_int %int_0",
+        "%inst = OpIAdd %int %short_to_uint %int_0",
+        "%inst = OpIAdd %int2 %short2_to_int2 %int2_0",
+        "%inst = OpIAdd %int2 %short2_to_uint2 %int2_0",
+        "%inst = OpIAdd %int %char_to_int %int_0",
+        "%inst = OpIAdd %int %char_to_uint %int_0",
+        "%inst = OpIAdd %int2 %char2_to_int2 %int2_0",
+        "%inst = OpIAdd %int2 %char2_to_uint2 %int2_0",
+        "%inst = OpUConvert %int %ld_short",
+        "%inst = OpSConvert %int %ld_short",
+        "%inst = OpUConvert %char %ld_short",
+        "%inst = OpSConvert %char %ld_short",
+        "%inst = OpUConvert %int %ld_char", "%inst = OpSConvert %int %ld_char",
+        "%inst = OpUConvert %short %ld_char",
+        "%inst = OpSConvert %short %ld_char",
+        "%inst = OpUConvert %int2 %ld_short2",
+        "%inst = OpSConvert %int2 %ld_short2",
+        "%inst = OpUConvert %char2 %ld_short2",
+        "%inst = OpSConvert %char2 %ld_short2",
+        "%inst = OpUConvert %int2 %ld_char2",
+        "%inst = OpSConvert %int2 %ld_char2",
+        "%inst = OpUConvert %short2 %ld_char2",
+        "%inst = OpSConvert %short2 %ld_char2",
+        "OpStore %short_gep %int_to_short", "OpStore %short_gep %int_to_ushort",
+        "OpStore %short_gep %char_to_short",
+        "OpStore %short_gep %char_to_ushort",
+        "OpStore %short2_gep %int2_to_short2",
+        "OpStore %short2_gep %int2_to_ushort2",
+        "OpStore %short2_gep %char2_to_short2",
+        "OpStore %short2_gep %char2_to_ushort2",
+        "OpStore %char_gep %int_to_char", "OpStore %char_gep %int_to_uchar",
+        "OpStore %char_gep %short_to_char", "OpStore %char_gep %short_to_uchar",
+        "OpStore %char2_gep %int2_to_char2",
+        "OpStore %char2_gep %int2_to_uchar2",
+        "OpStore %char2_gep %short2_to_char2",
+        "OpStore %char2_gep %short2_to_uchar2",
+        "OpStore %int_gep %short_to_int", "OpStore %int_gep %short_to_uint",
+        "OpStore %int_gep %char_to_int", "OpStore %int2_gep %char2_to_uint2",
+        "OpStore %int2_gep %short2_to_int2",
+        "OpStore %int2_gep %short2_to_uint2",
+        "OpStore %int2_gep %char2_to_int2", "OpStore %int2_gep %char2_to_uint2",
+        "%inst = OpFAdd %float %half_to_float %float_0",
+        "%inst = OpFAdd %float2 %half2_to_float2 %float2_0",
+        "%inst = OpFConvert %float %ld_half",
+        "%inst = OpFConvert %float2 %ld_half2",
+        "OpStore %half_gep %float_to_half", "OpStore %half_gep %ld_half",
+        "OpStore %half2_gep %float2_to_half2", "OpStore %half2_gep %ld_half2",
+        "OpStore %float_gep %half_to_float",
+        "OpStore %float2_gep %half2_to_float2"));
+
+using ValidateBadUses = spvtest::ValidateBase<std::string>;
+
+TEST_P(ValidateBadUses, Inst) {
+  const std::string inst = GetParam();
+  CodeGenerator generator = GetSmallTypesGenerator();
+  generator.entry_points_[0].body += inst + "\n";
+
+  CompileSuccessfully(generator.Build());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Invalid use of 8- or 16-bit result"));
+}
+
+// A smattering of unacceptable use cases. Far too vast to cover exhaustively.
+INSTANTIATE_TEST_SUITE_P(
+    SmallTypeUsesInvalid, ValidateBadUses,
+    Values("%inst = OpIAdd %short %ld_short %ld_short",
+           "%inst = OpIAdd %short %char_to_short %char_to_short",
+           "%inst = OpIAdd %short %char_to_ushort %char_to_ushort",
+           "%inst = OpIAdd %short %int_to_short %int_to_short",
+           "%inst = OpIAdd %short %int_to_ushort %int_to_ushort",
+           "%inst = OpIAdd %short2 %ld_short2 %ld_short2",
+           "%inst = OpIAdd %short2 %char2_to_short2 %char2_to_short2",
+           "%inst = OpIAdd %short2 %char2_to_ushort2 %char2_to_ushort2",
+           "%inst = OpIAdd %short2 %int2_to_short2 %int2_to_short2",
+           "%inst = OpIAdd %short2 %int2_to_ushort2 %int2_to_ushort2",
+           "%inst = OpIEqual %bool %ld_short %ld_short",
+           "%inst = OpIEqual %bool %char_to_short %char_to_short",
+           "%inst = OpIEqual %bool %char_to_ushort %char_to_ushort",
+           "%inst = OpIEqual %bool %int_to_short %int_to_short",
+           "%inst = OpIEqual %bool %int_to_ushort %int_to_ushort",
+           "%inst = OpIEqual %bool2 %ld_short2 %ld_short2",
+           "%inst = OpIEqual %bool2 %char2_to_short2 %char2_to_short2",
+           "%inst = OpIEqual %bool2 %char2_to_ushort2 %char2_to_ushort2",
+           "%inst = OpIEqual %bool2 %int2_to_short2 %int2_to_short2",
+           "%inst = OpIEqual %bool2 %int2_to_ushort2 %int2_to_ushort2",
+           "%inst = OpFAdd %half %ld_half %ld_half",
+           "%inst = OpFAdd %half %float_to_half %float_to_half",
+           "%inst = OpFAdd %half2 %ld_half2 %ld_half2",
+           "%inst = OpFAdd %half2 %float2_to_half2 %float2_to_half2",
+           "%inst = OpFOrdGreaterThan %bool %ld_half %ld_half",
+           "%inst = OpFOrdGreaterThan %bool %float_to_half %float_to_half",
+           "%inst = OpFOrdGreaterThan %bool2 %ld_half2 %ld_half2",
+           "%inst = OpFOrdGreaterThan %bool2 %float2_to_half2 %float2_to_half2",
+           "%inst = OpFunctionCall %void %short_func %ld_short",
+           "%inst = OpFunctionCall %void %short_func %char_to_short",
+           "%inst = OpFunctionCall %void %short_func %char_to_ushort",
+           "%inst = OpFunctionCall %void %short_func %int_to_short",
+           "%inst = OpFunctionCall %void %short_func %int_to_ushort",
+           "%inst = OpFunctionCall %void %char_func %ld_char",
+           "%inst = OpFunctionCall %void %char_func %short_to_char",
+           "%inst = OpFunctionCall %void %char_func %short_to_uchar",
+           "%inst = OpFunctionCall %void %char_func %int_to_char",
+           "%inst = OpFunctionCall %void %char_func %int_to_uchar",
+           "%inst = OpFunctionCall %void %half_func %ld_half",
+           "%inst = OpFunctionCall %void %half_func %float_to_half"));
+
+}  // namespace
+}  // namespace val
+}  // namespace spvtools