Add validation for OpArrayLength. (#2117)

The validation rules for OpArrayLength are not checked by the validator.
This with add them.

Fixes https://crbug.com/907451.
diff --git a/source/val/validate_memory.cpp b/source/val/validate_memory.cpp
index fb9d253..c08d7e6 100644
--- a/source/val/validate_memory.cpp
+++ b/source/val/validate_memory.cpp
@@ -842,6 +842,61 @@
   return ValidateAccessChain(_, inst);
 }
 
+spv_result_t ValidateArrayLength(ValidationState_t& state,
+                                 const Instruction* inst) {
+  std::string instr_name =
+      "Op" + std::string(spvOpcodeString(static_cast<SpvOp>(inst->opcode())));
+
+  // Result type must be a 32-bit unsigned int.
+  auto result_type = state.FindDef(inst->type_id());
+  if (result_type->opcode() != SpvOpTypeInt ||
+      result_type->GetOperandAs<uint32_t>(1) != 32 ||
+      result_type->GetOperandAs<uint32_t>(2) != 0) {
+    return state.diag(SPV_ERROR_INVALID_ID, inst)
+           << "The Result Type of " << instr_name << " <id> '"
+           << state.getIdName(inst->id())
+           << "' must be OpTypeInt with width 32 and signedness 0.";
+  }
+
+  // The structure that is passed in must be an pointer to a structure, whose
+  // last element is a runtime array.
+  auto pointer = state.FindDef(inst->GetOperandAs<uint32_t>(2));
+  auto pointer_type = state.FindDef(pointer->type_id());
+  if (pointer_type->opcode() != SpvOpTypePointer) {
+    return state.diag(SPV_ERROR_INVALID_ID, inst)
+           << "The Struture's type in " << instr_name << " <id> '"
+           << state.getIdName(inst->id())
+           << "' must be a pointer to an OpTypeStruct.";
+  }
+
+  auto structure_type = state.FindDef(pointer_type->GetOperandAs<uint32_t>(2));
+  if (structure_type->opcode() != SpvOpTypeStruct) {
+    return state.diag(SPV_ERROR_INVALID_ID, inst)
+           << "The Struture's type in " << instr_name << " <id> '"
+           << state.getIdName(inst->id())
+           << "' must be a pointer to an OpTypeStruct.";
+  }
+
+  auto num_of_members = structure_type->operands().size() - 1;
+  auto last_member =
+      state.FindDef(structure_type->GetOperandAs<uint32_t>(num_of_members));
+  if (last_member->opcode() != SpvOpTypeRuntimeArray) {
+    return state.diag(SPV_ERROR_INVALID_ID, inst)
+           << "The Struture's last member in " << instr_name << " <id> '"
+           << state.getIdName(inst->id()) << "' must be an OpTypeRuntimeArray.";
+  }
+
+  // The array member must the the index of the last element (the run time
+  // array).
+  if (inst->GetOperandAs<uint32_t>(3) != num_of_members - 1) {
+    return state.diag(SPV_ERROR_INVALID_ID, inst)
+           << "The array member in " << instr_name << " <id> '"
+           << state.getIdName(inst->id())
+           << "' must be an the last member of the struct.";
+  }
+  return SPV_SUCCESS;
+}
+
 }  // namespace
 
 spv_result_t MemoryPass(ValidationState_t& _, const Instruction* inst) {
@@ -867,8 +922,10 @@
     case SpvOpInBoundsPtrAccessChain:
       if (auto error = ValidateAccessChain(_, inst)) return error;
       break;
-    case SpvOpImageTexelPointer:
     case SpvOpArrayLength:
+      if (auto error = ValidateArrayLength(_, inst)) return error;
+      break;
+    case SpvOpImageTexelPointer:
     case SpvOpGenericPtrMemSemantics:
     default:
       break;
@@ -876,6 +933,5 @@
 
   return SPV_SUCCESS;
 }
-
 }  // namespace val
 }  // namespace spvtools
diff --git a/test/val/val_memory_test.cpp b/test/val/val_memory_test.cpp
index 8dafa0d..a2e8bf3 100644
--- a/test/val/val_memory_test.cpp
+++ b/test/val/val_memory_test.cpp
@@ -543,6 +543,301 @@
           "%5 = OpVariable %_ptr_Input_float Input %float_1\n"));
 }
 
+TEST_F(ValidateMemory, ArrayLenCorrectResultType) {
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %1 "main"
+               OpExecutionMode %1 OriginUpperLeft
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+     %uint = OpTypeInt 32 0
+%_runtimearr_float = OpTypeRuntimeArray %float
+  %_struct_7 = OpTypeStruct %_runtimearr_float
+%_ptr_Function__struct_7 = OpTypePointer Function %_struct_7
+          %1 = OpFunction %void None %3
+          %9 = OpLabel
+         %10 = OpVariable %_ptr_Function__struct_7 Function
+         %11 = OpArrayLength %uint %10 0
+               OpReturn
+               OpFunctionEnd
+
+)";
+
+  CompileSuccessfully(spirv.c_str());
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateMemory, ArrayLenIndexCorrectWith2Members) {
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %1 "main"
+               OpExecutionMode %1 OriginUpperLeft
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+     %uint = OpTypeInt 32 0
+%_runtimearr_float = OpTypeRuntimeArray %float
+  %_struct_7 = OpTypeStruct %float %_runtimearr_float
+%_ptr_Function__struct_7  = OpTypePointer Function %_struct_7
+          %1 = OpFunction %void None %3
+          %9 = OpLabel
+         %10 = OpVariable %_ptr_Function__struct_7  Function
+         %11 = OpArrayLength %uint %10 1
+               OpReturn
+               OpFunctionEnd
+
+)";
+
+  CompileSuccessfully(spirv.c_str());
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateMemory, ArrayLenResultNotIntType) {
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %1 "main"
+               OpExecutionMode %1 OriginUpperLeft
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+%_runtimearr_float = OpTypeRuntimeArray %float
+  %_struct_6 = OpTypeStruct %_runtimearr_float
+%_ptr_Function__struct_6 = OpTypePointer Function %_struct_6
+          %1 = OpFunction %void None %3
+          %8 = OpLabel
+          %9 = OpVariable %_ptr_Function__struct_6 Function
+         %10 = OpArrayLength %float %9 0
+               OpReturn
+               OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv.c_str());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "The Result Type of OpArrayLength <id> '10' must be OpTypeInt with "
+          "width 32 and signedness 0.\n  %10 = OpArrayLength %float %9 0\n"));
+}
+
+TEST_F(ValidateMemory, ArrayLenResultNot32bits) {
+  std::string spirv = R"(
+               OpCapability Shader
+               OpCapability Int16
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %1 "main"
+               OpExecutionMode %1 OriginUpperLeft
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+     %ushort = OpTypeInt 16 0
+%_runtimearr_float = OpTypeRuntimeArray %float
+  %_struct_7 = OpTypeStruct %_runtimearr_float
+%_ptr_Function__struct_7 = OpTypePointer Function %_struct_7
+          %1 = OpFunction %void None %3
+          %9 = OpLabel
+         %10 = OpVariable %_ptr_Function__struct_7 Function
+         %11 = OpArrayLength %ushort %10 0
+               OpReturn
+               OpFunctionEnd
+
+)";
+
+  CompileSuccessfully(spirv.c_str());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "The Result Type of OpArrayLength <id> '11' must be OpTypeInt with "
+          "width 32 and signedness 0.\n  %11 = OpArrayLength %ushort %10 0\n"));
+}
+
+TEST_F(ValidateMemory, ArrayLenResultSigned) {
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %1 "main"
+               OpExecutionMode %1 OriginUpperLeft
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+     %int = OpTypeInt 32 1
+%_runtimearr_float = OpTypeRuntimeArray %float
+  %_struct_7 = OpTypeStruct %_runtimearr_float
+%_ptr_Function__struct_7 = OpTypePointer Function %_struct_7
+          %1 = OpFunction %void None %3
+          %9 = OpLabel
+         %10 = OpVariable %_ptr_Function__struct_7 Function
+         %11 = OpArrayLength %int %10 0
+               OpReturn
+               OpFunctionEnd
+
+)";
+
+  CompileSuccessfully(spirv.c_str());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "The Result Type of OpArrayLength <id> '11' must be OpTypeInt with "
+          "width 32 and signedness 0.\n  %11 = OpArrayLength %int %10 0\n"));
+}
+
+TEST_F(ValidateMemory, ArrayLenInputNotStruct) {
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %1 "main"
+               OpExecutionMode %1 OriginUpperLeft
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+     %uint = OpTypeInt 32 0
+%_runtimearr_float = OpTypeRuntimeArray %float
+  %_struct_7 = OpTypeStruct %_runtimearr_float
+%_ptr_Function_float = OpTypePointer Function %float
+          %1 = OpFunction %void None %3
+          %9 = OpLabel
+         %10 = OpVariable %_ptr_Function_float Function
+         %11 = OpArrayLength %uint %10 0
+               OpReturn
+               OpFunctionEnd
+
+)";
+
+  CompileSuccessfully(spirv.c_str());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("The Struture's type in OpArrayLength <id> '11' must "
+                        "be a pointer to an OpTypeStruct."));
+}
+
+TEST_F(ValidateMemory, ArrayLenInputLastMemberNoRTA) {
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %1 "main"
+               OpExecutionMode %1 OriginUpperLeft
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+     %uint = OpTypeInt 32 0
+%_runtimearr_float = OpTypeRuntimeArray %float
+  %_struct_7 = OpTypeStruct %float
+%_ptr_Function__struct_7  = OpTypePointer Function %_struct_7
+          %1 = OpFunction %void None %3
+          %9 = OpLabel
+         %10 = OpVariable %_ptr_Function__struct_7  Function
+         %11 = OpArrayLength %uint %10 0
+               OpReturn
+               OpFunctionEnd
+
+)";
+
+  CompileSuccessfully(spirv.c_str());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("The Struture's last member in OpArrayLength <id> '11' must be "
+                "an OpTypeRuntimeArray.\n  %11 = OpArrayLength %uint %10 0\n"));
+}
+
+TEST_F(ValidateMemory, ArrayLenInputLastMemberNoRTA2) {
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %1 "main"
+               OpExecutionMode %1 OriginUpperLeft
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+     %uint = OpTypeInt 32 0
+%_runtimearr_float = OpTypeRuntimeArray %float
+  %_struct_7 = OpTypeStruct %_runtimearr_float %float
+%_ptr_Function__struct_7  = OpTypePointer Function %_struct_7
+          %1 = OpFunction %void None %3
+          %9 = OpLabel
+         %10 = OpVariable %_ptr_Function__struct_7  Function
+         %11 = OpArrayLength %uint %10 1
+               OpReturn
+               OpFunctionEnd
+
+)";
+
+  CompileSuccessfully(spirv.c_str());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("The Struture's last member in OpArrayLength <id> '11' must be "
+                "an OpTypeRuntimeArray.\n  %11 = OpArrayLength %uint %10 1\n"));
+}
+
+TEST_F(ValidateMemory, ArrayLenIndexNotLastMember) {
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %1 "main"
+               OpExecutionMode %1 OriginUpperLeft
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+     %uint = OpTypeInt 32 0
+%_runtimearr_float = OpTypeRuntimeArray %float
+  %_struct_7 = OpTypeStruct %float %_runtimearr_float
+%_ptr_Function__struct_7  = OpTypePointer Function %_struct_7
+          %1 = OpFunction %void None %3
+          %9 = OpLabel
+         %10 = OpVariable %_ptr_Function__struct_7  Function
+         %11 = OpArrayLength %uint %10 0
+               OpReturn
+               OpFunctionEnd
+
+)";
+
+  CompileSuccessfully(spirv.c_str());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "The array member in OpArrayLength <id> '11' must be an the last "
+          "member of the struct.\n  %11 = OpArrayLength %uint %10 0\n"));
+}
+
+TEST_F(ValidateMemory, ArrayLenIndexNotPointerToStruct) {
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %1 "main"
+               OpExecutionMode %1 OriginUpperLeft
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+     %uint = OpTypeInt 32 0
+%_runtimearr_float = OpTypeRuntimeArray %float
+  %_struct_7 = OpTypeStruct %float %_runtimearr_float
+%_ptr_Function__struct_7  = OpTypePointer Function %_struct_7
+          %1 = OpFunction %void None %3
+          %9 = OpLabel
+         %10 = OpVariable %_ptr_Function__struct_7  Function
+         %11 = OpLoad %_struct_7 %10
+         %12 = OpArrayLength %uint %11 0
+               OpReturn
+               OpFunctionEnd
+
+)";
+
+  CompileSuccessfully(spirv.c_str());
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "The Struture's type in OpArrayLength <id> '12' must be a pointer to "
+          "an OpTypeStruct.\n  %12 = OpArrayLength %uint %11 0\n"));
+}
 }  // namespace
 }  // namespace val
 }  // namespace spvtools