// Copyright (c) 2015-2016 The Khronos Group Inc.
//
// 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 <sstream>
#include <string>
#include <vector>

#include "gmock/gmock.h"
#include "test/test_fixture.h"
#include "test/unit_spirv.h"
#include "test/val/val_fixtures.h"

// NOTE: The tests in this file are ONLY testing ID usage, there for the input
// SPIR-V does not follow the logical layout rules from the spec in all cases in
// order to makes the tests smaller. Validation of the whole module is handled
// in stages, ID validation is only one of these stages. All validation stages
// are stand alone.

namespace spvtools {
namespace val {
namespace {

using spvtest::ScopedContext;
using ::testing::HasSubstr;
using ::testing::ValuesIn;

class ValidateIdWithMessage : public spvtest::ValidateBase<bool> {
 public:
  ValidateIdWithMessage() {
    const bool use_friendly_names = GetParam();
    spvValidatorOptionsSetFriendlyNames(options_, use_friendly_names);
  }

  std::string make_message(const char* msg);
};

std::string kOpCapabilitySetupWithoutVector16 = R"(
     OpCapability Shader
     OpCapability Linkage
     OpCapability Addresses
     OpCapability Int8
     OpCapability Int16
     OpCapability Int64
     OpCapability Float64
     OpCapability LiteralSampler
     OpCapability Pipes
     OpCapability DeviceEnqueue
)";

std::string kOpCapabilitySetup = R"(
     OpCapability Shader
     OpCapability Linkage
     OpCapability Addresses
     OpCapability Int8
     OpCapability Int16
     OpCapability Int64
     OpCapability Float64
     OpCapability LiteralSampler
     OpCapability Pipes
     OpCapability DeviceEnqueue
     OpCapability Vector16
)";

std::string kOpVariablePtrSetUp = R"(
     OpCapability VariablePointers
     OpExtension "SPV_KHR_variable_pointers"
)";

std::string kGLSL450MemoryModel =
    kOpCapabilitySetup + kOpVariablePtrSetUp + R"(
     OpMemoryModel Logical GLSL450
)";

std::string kGLSL450MemoryModelWithoutVector16 =
    kOpCapabilitySetupWithoutVector16 + kOpVariablePtrSetUp + R"(
     OpMemoryModel Logical GLSL450
)";

std::string kNoKernelGLSL450MemoryModel = R"(
     OpCapability Shader
     OpCapability Linkage
     OpCapability Addresses
     OpCapability Int8
     OpCapability Int16
     OpCapability Int64
     OpCapability Float64
     OpMemoryModel Logical GLSL450
)";

std::string kOpenCLMemoryModel32 = R"(
     OpCapability Addresses
     OpCapability Linkage
     OpCapability Kernel
%1 = OpExtInstImport "OpenCL.std"
     OpMemoryModel Physical32 OpenCL
)";

std::string kOpenCLMemoryModel64 = R"(
     OpCapability Addresses
     OpCapability Linkage
     OpCapability Kernel
     OpCapability Int64
%1 = OpExtInstImport "OpenCL.std"
     OpMemoryModel Physical64 OpenCL
)";

std::string sampledImageSetup = R"(
                    %void = OpTypeVoid
            %typeFuncVoid = OpTypeFunction %void
                   %float = OpTypeFloat 32
                 %v4float = OpTypeVector %float 4
              %image_type = OpTypeImage %float 2D 0 0 0 1 Unknown
%_ptr_UniformConstant_img = OpTypePointer UniformConstant %image_type
                     %tex = OpVariable %_ptr_UniformConstant_img UniformConstant
            %sampler_type = OpTypeSampler
%_ptr_UniformConstant_sam = OpTypePointer UniformConstant %sampler_type
                       %s = OpVariable %_ptr_UniformConstant_sam UniformConstant
      %sampled_image_type = OpTypeSampledImage %image_type
                 %v2float = OpTypeVector %float 2
                 %float_1 = OpConstant %float 1
                 %float_2 = OpConstant %float 2
           %const_vec_1_1 = OpConstantComposite %v2float %float_1 %float_1
           %const_vec_2_2 = OpConstantComposite %v2float %float_2 %float_2
               %bool_type = OpTypeBool
               %spec_true = OpSpecConstantTrue %bool_type
                    %main = OpFunction %void None %typeFuncVoid
                 %label_1 = OpLabel
              %image_inst = OpLoad %image_type %tex
            %sampler_inst = OpLoad %sampler_type %s
)";

std::string BranchConditionalSetup = R"(
               OpCapability Shader
          %1 = OpExtInstImport "GLSL.std.450"
               OpMemoryModel Logical GLSL450
               OpEntryPoint Fragment %main "main"
               OpExecutionMode %main OriginUpperLeft
               OpSource GLSL 140
               OpName %main "main"

             ; type definitions
       %bool = OpTypeBool
       %uint = OpTypeInt 32 0
        %int = OpTypeInt 32 1
      %float = OpTypeFloat 32
    %v4float = OpTypeVector %float 4

             ; constants
       %true = OpConstantTrue %bool
         %i0 = OpConstant %int 0
         %i1 = OpConstant %int 1
         %f0 = OpConstant %float 0
         %f1 = OpConstant %float 1


             ; main function header
       %void = OpTypeVoid
   %voidfunc = OpTypeFunction %void
       %main = OpFunction %void None %voidfunc
      %lmain = OpLabel
)";

std::string BranchConditionalTail = R"(
   %target_t = OpLabel
               OpNop
               OpBranch %end
   %target_f = OpLabel
               OpNop
               OpBranch %end

        %end = OpLabel

               OpReturn
               OpFunctionEnd
)";

// Transform an expected validation message to either use friendly names (as
// provided in the message) or replace the friendly names by the corresponding
// id.  The same flag used to configure the validator to output friendly names
// or not is used here.
std::string ValidateIdWithMessage::make_message(const char* msg) {
  const bool use_friendly_names = GetParam();
  if (use_friendly_names) {
    return msg;
  }

  std::string message(msg);
  std::ostringstream result;

  size_t next = 0;
  while (next < message.size()) {
    // Parse 'num[%name]'
    size_t open_quote = message.find('\'', next);

    if (open_quote == std::string::npos) {
      break;
    }

    // Copy up to the first quote
    result.write(msg + next, open_quote - next);
    // Handle apostrophes
    if (!isdigit(message[open_quote + 1])) {
      result << '\'';
      next = open_quote + 1;
      continue;
    }

    size_t open_bracket = message.find('[', open_quote + 1);
    assert(open_bracket != std::string::npos);

    size_t close_bracket = message.find(']', open_bracket + 1);
    assert(close_bracket != std::string::npos);

    size_t close_quote = close_bracket + 1;
    assert(close_quote < message.size() && message[close_quote] == '\'');

    // Change to 'num[%num]' because friendly names are not being used.
    result.write(msg + open_quote, open_bracket - open_quote + 1);
    result << '%';
    result.write(msg + open_quote + 1, open_bracket - open_quote - 1);
    result << "]'";

    // Continue to the next id, or end of string.
    next = close_quote + 1;
  }

  return result.str();
}

// TODO: OpUndef

TEST_P(ValidateIdWithMessage, OpName) {
  std::string spirv = kGLSL450MemoryModel + R"(
     OpName %2 "name"
%1 = OpTypeInt 32 0
%2 = OpTypePointer UniformConstant %1
%3 = OpVariable %2 UniformConstant)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpMemberNameGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
     OpMemberName %2 0 "foo"
%1 = OpTypeInt 32 0
%2 = OpTypeStruct %1)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpMemberNameTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
     OpMemberName %1 0 "foo"
%1 = OpTypeInt 32 0)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpMemberName Type <id> '1[%uint]' is not a struct type.")));
}
TEST_P(ValidateIdWithMessage, OpMemberNameMemberBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
     OpMemberName %1 1 "foo"
%2 = OpTypeInt 32 0
%1 = OpTypeStruct %2)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpMemberName Member <id> '1[%_struct_1]' index is larger "
                  "than Type <id> '1[%_struct_1]'s member count.")));
}

TEST_P(ValidateIdWithMessage, OpLineGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpString "/path/to/source.file"
     OpLine %1 0 0
%2 = OpTypeInt 32 0
%3 = OpTypePointer Input %2
%4 = OpVariable %3 Input)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpLineFileBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
  %1 = OpTypeInt 32 0
     OpLine %1 0 0
  )";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpLine Target <id> '1[%uint]' is not an OpString.")));
}

TEST_P(ValidateIdWithMessage, OpDecorateGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
     OpDecorate %2 GLSLShared
%1 = OpTypeInt 64 0
%2 = OpTypeStruct %1 %1)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpDecorateBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
OpDecorate %1 GLSLShared)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("forward referenced IDs have not been defined")));
}

TEST_P(ValidateIdWithMessage, OpMemberDecorateGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
     OpMemberDecorate %2 0 RelaxedPrecision
%1 = OpTypeInt 32 0
%2 = OpTypeStruct %1 %1)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpMemberDecorateBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
     OpMemberDecorate %1 0 RelaxedPrecision
%1 = OpTypeInt 32 0)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpMemberDecorate Structure type <id> '1[%uint]' is "
                  "not a struct type.")));
}
TEST_P(ValidateIdWithMessage, OpMemberDecorateMemberBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
     OpMemberDecorate %1 3 RelaxedPrecision
%int = OpTypeInt 32 0
%1 = OpTypeStruct %int %int)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "Index 3 provided in OpMemberDecorate for struct <id> "
                  "'1[%_struct_1]' is out of bounds. The structure has 2 "
                  "members. Largest valid index is 1.")));
}

TEST_P(ValidateIdWithMessage, OpGroupDecorateGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpDecorationGroup
     OpDecorate %1 RelaxedPrecision
     OpDecorate %1 GLSLShared
     OpGroupDecorate %1 %3 %4
%2 = OpTypeInt 32 0
%3 = OpConstant %2 42
%4 = OpConstant %2 23)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpDecorationGroupBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpDecorationGroup
     OpDecorate %1 RelaxedPrecision
     OpDecorate %1 GLSLShared
     OpMemberDecorate %1 0 Constant
    )";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "Result id of OpDecorationGroup can only "
                  "be targeted by OpName, OpGroupDecorate, "
                  "OpDecorate, OpDecorateId, and OpGroupMemberDecorate")));
}
TEST_P(ValidateIdWithMessage, OpGroupDecorateDecorationGroupBad) {
  std::string spirv = R"(
    OpCapability Shader
    OpCapability Linkage
    %1 = OpExtInstImport "GLSL.std.450"
    OpMemoryModel Logical GLSL450
    OpGroupDecorate %1 %2 %3
%2 = OpTypeInt 32 0
%3 = OpConstant %2 42)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpGroupDecorate Decoration group <id> '1[%1]' is not "
                  "a decoration group.")));
}
TEST_P(ValidateIdWithMessage, OpGroupDecorateTargetBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpDecorationGroup
     OpDecorate %1 RelaxedPrecision
     OpDecorate %1 GLSLShared
     OpGroupDecorate %1 %3
%2 = OpTypeInt 32 0)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("forward referenced IDs have not been defined")));
}
TEST_P(ValidateIdWithMessage, OpGroupMemberDecorateDecorationGroupBad) {
  std::string spirv = R"(
    OpCapability Shader
    OpCapability Linkage
    %1 = OpExtInstImport "GLSL.std.450"
    OpMemoryModel Logical GLSL450
    OpGroupMemberDecorate %1 %2 0
%2 = OpTypeInt 32 0)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpGroupMemberDecorate Decoration group <id> '1[%1]' "
                  "is not a decoration group.")));
}
TEST_P(ValidateIdWithMessage, OpGroupMemberDecorateIdNotStructBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
     %1 = OpDecorationGroup
     OpGroupMemberDecorate %1 %2 0
%2 = OpTypeInt 32 0)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpGroupMemberDecorate Structure type <id> '2[%uint]' "
                  "is not a struct type.")));
}
TEST_P(ValidateIdWithMessage, OpGroupMemberDecorateIndexOutOfBoundBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
  OpDecorate %1 Offset 0
  %1 = OpDecorationGroup
  OpGroupMemberDecorate %1 %struct 3
%float  = OpTypeFloat 32
%struct = OpTypeStruct %float %float %float
)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "Index 3 provided in OpGroupMemberDecorate for struct "
                  "<id> '2[%_struct_2]' is out of bounds. The structure "
                  "has 3 members. Largest valid index is 2.")));
}

// TODO: OpExtInst

TEST_P(ValidateIdWithMessage, OpEntryPointGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
     OpEntryPoint GLCompute %3 ""
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%3 = OpFunction %1 None %2
%4 = OpLabel
     OpReturn
     OpFunctionEnd
)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpEntryPointFunctionBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
     OpEntryPoint GLCompute %1 ""
%1 = OpTypeVoid)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpEntryPoint Entry Point <id> '1[%void]' is not a "
                  "function.")));
}
TEST_P(ValidateIdWithMessage, OpEntryPointParameterCountBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
     OpEntryPoint GLCompute %1 ""
%2 = OpTypeVoid
%3 = OpTypeFunction %2 %2
%1 = OpFunction %2 None %3
%4 = OpLabel
     OpReturn
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("OpEntryPoint Entry Point <id> '1[%1]'s function "
                             "parameter count is not zero")));
}
TEST_P(ValidateIdWithMessage, OpEntryPointReturnTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
     OpEntryPoint GLCompute %1 ""
%2 = OpTypeInt 32 0
%ret = OpConstant %2 0
%3 = OpTypeFunction %2
%1 = OpFunction %2 None %3
%4 = OpLabel
     OpReturnValue %ret
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("OpEntryPoint Entry Point <id> '1[%1]'s function "
                             "return type is not void.")));
}
TEST_P(ValidateIdWithMessage, OpEntryPointParameterCountBadInVulkan) {
  std::string spirv = R"(
     OpCapability Shader
     OpMemoryModel Logical GLSL450
     OpEntryPoint GLCompute %1 ""
%2 = OpTypeVoid
%3 = OpTypeFunction %2 %2
%1 = OpFunction %2 None %3
%4 = OpLabel
     OpReturn
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_0);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
  EXPECT_THAT(getDiagnosticString(),
              AnyVUID("VUID-StandaloneSpirv-None-04633"));
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("OpEntryPoint Entry Point <id> '1[%1]'s function "
                             "parameter count is not zero")));
}
TEST_P(ValidateIdWithMessage, OpEntryPointReturnTypeBadInVulkan) {
  std::string spirv = R"(
     OpCapability Shader
     OpMemoryModel Logical GLSL450
     OpEntryPoint GLCompute %1 ""
%2 = OpTypeInt 32 0
%ret = OpConstant %2 0
%3 = OpTypeFunction %2
%1 = OpFunction %2 None %3
%4 = OpLabel
     OpReturnValue %ret
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_0);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
  EXPECT_THAT(getDiagnosticString(),
              AnyVUID("VUID-StandaloneSpirv-None-04633"));
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("OpEntryPoint Entry Point <id> '1[%1]'s function "
                             "return type is not void.")));
}

TEST_P(ValidateIdWithMessage, OpEntryPointInterfaceIsNotVariableTypeBad) {
  std::string spirv = R"(
               OpCapability Shader
               OpCapability Geometry
               OpMemoryModel Logical GLSL450
               OpEntryPoint Geometry %main "main" %ptr_builtin_1
               OpExecutionMode %main InputPoints
               OpExecutionMode %main OutputPoints
               OpMemberDecorate %struct_1 0 BuiltIn InvocationId
      %int = OpTypeInt 32 1
     %void = OpTypeVoid
     %func = OpTypeFunction %void
 %struct_1 = OpTypeStruct %int
%ptr_builtin_1 = OpTypePointer Input %struct_1
       %main = OpFunction %void None %func
          %5 = OpLabel
               OpReturn
               OpFunctionEnd
  )";
  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "Interfaces passed to OpEntryPoint must be of type "
                  "OpTypeVariable. Found OpTypePointer.")));
}

TEST_P(ValidateIdWithMessage, OpEntryPointInterfaceStorageClassBad) {
  std::string spirv = R"(
               OpCapability Shader
               OpCapability Geometry
               OpMemoryModel Logical GLSL450
               OpEntryPoint Geometry %main "main" %in_1
               OpExecutionMode %main InputPoints
               OpExecutionMode %main OutputPoints
               OpMemberDecorate %struct_1 0 BuiltIn InvocationId
      %int = OpTypeInt 32 1
     %void = OpTypeVoid
     %func = OpTypeFunction %void
 %struct_1 = OpTypeStruct %int
%ptr_builtin_1 = OpTypePointer Uniform %struct_1
       %in_1 = OpVariable %ptr_builtin_1 Uniform
       %main = OpFunction %void None %func
          %5 = OpLabel
               OpReturn
               OpFunctionEnd
  )";
  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpEntryPoint interfaces must be OpVariables with "
                  "Storage Class of Input(1) or Output(3). Found Storage "
                  "Class 2 for Entry Point id 1.")));
}

TEST_P(ValidateIdWithMessage, OpExecutionModeGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
     OpEntryPoint GLCompute %3 ""
     OpExecutionMode %3 LocalSize 1 1 1
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%3 = OpFunction %1 None %2
%4 = OpLabel
     OpReturn
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpExecutionModeEntryPointMissing) {
  std::string spirv = kGLSL450MemoryModel + R"(
     OpExecutionMode %3 LocalSize 1 1 1
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%3 = OpFunction %1 None %2
%4 = OpLabel
     OpReturn
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpExecutionMode Entry Point <id> '1[%1]' is not the "
                  "Entry Point operand of an OpEntryPoint.")));
}

TEST_P(ValidateIdWithMessage, OpExecutionModeEntryPointBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
     OpEntryPoint GLCompute %3 "" %a
     OpExecutionMode %a LocalSize 1 1 1
%void = OpTypeVoid
%ptr = OpTypePointer Input %void
%a = OpVariable %ptr Input
%2 = OpTypeFunction %void
%3 = OpFunction %void None %2
%4 = OpLabel
     OpReturn
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpExecutionMode Entry Point <id> '2[%2]' is not the "
                  "Entry Point operand of an OpEntryPoint.")));
}

TEST_P(ValidateIdWithMessage, OpTypeVectorFloat) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpTypeVectorInt) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeVector %1 4)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpTypeVectorUInt) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 64 0
%2 = OpTypeVector %1 4)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpTypeVectorBool) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeBool
%2 = OpTypeVector %1 4)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpTypeVectorComponentTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypePointer UniformConstant %1
%3 = OpTypeVector %2 4)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpTypeVector Component Type <id> "
                  "'2[%_ptr_UniformConstant_float]' is not a scalar type.")));
}

TEST_P(ValidateIdWithMessage, OpTypeVectorColumnCountLessThanTwoBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 1)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message(
          "Illegal number of components (1) for TypeVector\n  %v1float = "
          "OpTypeVector %float 1\n")));
}

TEST_P(ValidateIdWithMessage, OpTypeVectorColumnCountGreaterThanFourBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 5)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message(
          "Illegal number of components (5) for TypeVector\n  %v5float = "
          "OpTypeVector %float 5\n")));
}

TEST_P(ValidateIdWithMessage, OpTypeVectorColumnCountEightWithoutVector16Bad) {
  std::string spirv = kGLSL450MemoryModelWithoutVector16 + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 8)";

  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "Having 8 components for TypeVector requires the Vector16 "
                  "capability\n  %v8float = OpTypeVector %float 8\n")));
}

TEST_P(ValidateIdWithMessage,
       OpTypeVectorColumnCountSixteenWithoutVector16Bad) {
  std::string spirv = kGLSL450MemoryModelWithoutVector16 + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 16)";

  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "Having 16 components for TypeVector requires the Vector16 "
                  "capability\n  %v16float = OpTypeVector %float 16\n")));
}

TEST_P(ValidateIdWithMessage, OpTypeVectorColumnCountOfEightWithVector16Good) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 8)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage,
       OpTypeVectorColumnCountOfSixteenWithVector16Good) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 16)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpTypeMatrixGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 2
%3 = OpTypeMatrix %2 3)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpTypeMatrixColumnTypeNonVectorBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeMatrix %1 3)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "olumns in a matrix must be of type vector.\n  %mat3float = "
                  "OpTypeMatrix %float 3\n")));
}

TEST_P(ValidateIdWithMessage, OpTypeMatrixVectorTypeNonFloatBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 16 0
%2 = OpTypeVector %1 2
%3 = OpTypeMatrix %2 2)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "Matrix types can only be parameterized with floating-point "
                  "types.\n  %mat2v2ushort = OpTypeMatrix %v2ushort 2\n")));
}

TEST_P(ValidateIdWithMessage, OpTypeMatrixColumnCountLessThanTwoBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 2
%3 = OpTypeMatrix %2 1)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message(
          "Matrix types can only be parameterized as having only 2, 3, "
          "or 4 columns.\n  %mat1v2float = OpTypeMatrix %v2float 1\n")));
}

TEST_P(ValidateIdWithMessage, OpTypeMatrixColumnCountGreaterThanFourBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 2
%3 = OpTypeMatrix %2 8)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message(
          "Matrix types can only be parameterized as having only 2, 3, "
          "or 4 columns.\n  %mat8v2float = OpTypeMatrix %v2float 8\n")));
}

TEST_P(ValidateIdWithMessage, OpTypeSamplerGood) {
  // In Rev31, OpTypeSampler takes no arguments.
  std::string spirv = kGLSL450MemoryModel + R"(
%s = OpTypeSampler)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpTypeArrayGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 1
%3 = OpTypeArray %1 %2)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpTypeArrayElementTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 1
%3 = OpTypeArray %2 %2)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpTypeArray Element Type <id> '2[%uint_1]' is not a "
                  "type.")));
}

// Signed or unsigned.
enum Signed { kSigned, kUnsigned };

// Creates an assembly module declaring OpTypeArray with the given length.
std::string MakeArrayLength(const std::string& len, Signed isSigned, int width,
                            int max_int_width = 64,
                            bool use_vulkan_memory_model = false) {
  std::ostringstream ss;
  ss << R"(
    OpCapability Shader
  )";
  if (use_vulkan_memory_model) {
    ss << " OpCapability VulkanMemoryModel\n";
  }
  if (width == 16) {
    ss << " OpCapability Int16\n";
  }
  if (max_int_width > 32) {
    ss << "\n  OpCapability Int64\n";
  }
  if (use_vulkan_memory_model) {
    ss << " OpExtension \"SPV_KHR_vulkan_memory_model\"\n";
    ss << "OpMemoryModel Logical Vulkan\n";
  } else {
    ss << "OpMemoryModel Logical GLSL450\n";
  }
  ss << "OpEntryPoint GLCompute %main \"main\"\n";
  ss << "OpExecutionMode %main LocalSize 1 1 1\n";
  ss << " %t = OpTypeInt " << width << (isSigned == kSigned ? " 1" : " 0");
  ss << " %l = OpConstant %t " << len;
  ss << " %a = OpTypeArray %t %l";
  ss << " %void = OpTypeVoid \n"
        " %voidfn = OpTypeFunction %void \n"
        " %main = OpFunction %void None %voidfn \n"
        " %entry = OpLabel\n"
        " OpReturn\n"
        " OpFunctionEnd\n";
  return ss.str();
}

// Tests OpTypeArray.  Parameter is the width (in bits) of the array-length's
// type.
class OpTypeArrayLengthTest
    : public spvtest::TextToBinaryTestBase<::testing::TestWithParam<int>> {
 protected:
  OpTypeArrayLengthTest()
      : env_(SPV_ENV_UNIVERSAL_1_0),
        position_(spv_position_t{0, 0, 0}),
        diagnostic_(spvDiagnosticCreate(&position_, "")) {}

  ~OpTypeArrayLengthTest() override { spvDiagnosticDestroy(diagnostic_); }

  // Runs spvValidate() on v, printing any errors via spvDiagnosticPrint().
  spv_result_t Val(const SpirvVector& v, const std::string& expected_err = "") {
    spv_const_binary_t cbinary{v.data(), v.size()};
    spvDiagnosticDestroy(diagnostic_);
    diagnostic_ = nullptr;
    const auto status =
        spvValidate(ScopedContext(env_).context, &cbinary, &diagnostic_);
    if (status != SPV_SUCCESS) {
      spvDiagnosticPrint(diagnostic_);
      EXPECT_THAT(std::string(diagnostic_->error),
                  testing::ContainsRegex(expected_err));
    }
    return status;
  }

 protected:
  spv_target_env env_;

 private:
  spv_position_t position_;  // For creating diagnostic_.
  spv_diagnostic diagnostic_;
};

TEST_P(OpTypeArrayLengthTest, LengthPositiveSmall) {
  const int width = GetParam();
  EXPECT_EQ(SPV_SUCCESS,
            Val(CompileSuccessfully(MakeArrayLength("1", kSigned, width))));
  EXPECT_EQ(SPV_SUCCESS,
            Val(CompileSuccessfully(MakeArrayLength("1", kUnsigned, width))));
  EXPECT_EQ(SPV_SUCCESS,
            Val(CompileSuccessfully(MakeArrayLength("2", kSigned, width))));
  EXPECT_EQ(SPV_SUCCESS,
            Val(CompileSuccessfully(MakeArrayLength("2", kUnsigned, width))));
  EXPECT_EQ(SPV_SUCCESS,
            Val(CompileSuccessfully(MakeArrayLength("55", kSigned, width))));
  EXPECT_EQ(SPV_SUCCESS,
            Val(CompileSuccessfully(MakeArrayLength("55", kUnsigned, width))));
  const std::string fpad(width / 4 - 1, 'F');
  EXPECT_EQ(
      SPV_SUCCESS,
      Val(CompileSuccessfully(MakeArrayLength("0x7" + fpad, kSigned, width))))
      << MakeArrayLength("0x7" + fpad, kSigned, width);
}

TEST_P(OpTypeArrayLengthTest, LengthZero) {
  const int width = GetParam();
  EXPECT_EQ(SPV_ERROR_INVALID_ID,
            Val(CompileSuccessfully(MakeArrayLength("0", kSigned, width)),
                "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at "
                "least 1."));
  EXPECT_EQ(SPV_ERROR_INVALID_ID,
            Val(CompileSuccessfully(MakeArrayLength("0", kUnsigned, width)),
                "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at "
                "least 1."));
}

TEST_P(OpTypeArrayLengthTest, LengthNegative) {
  const int width = GetParam();
  EXPECT_EQ(SPV_ERROR_INVALID_ID,
            Val(CompileSuccessfully(MakeArrayLength("-1", kSigned, width)),
                "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at "
                "least 1."));
  EXPECT_EQ(SPV_ERROR_INVALID_ID,
            Val(CompileSuccessfully(MakeArrayLength("-2", kSigned, width)),
                "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at "
                "least 1."));
  EXPECT_EQ(SPV_ERROR_INVALID_ID,
            Val(CompileSuccessfully(MakeArrayLength("-123", kSigned, width)),
                "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at "
                "least 1."));
  const std::string neg_max = "0x8" + std::string(width / 4 - 1, '0');
  EXPECT_EQ(SPV_ERROR_INVALID_ID,
            Val(CompileSuccessfully(MakeArrayLength(neg_max, kSigned, width)),
                "OpTypeArray Length <id> '3\\[%.*\\]' default value must be at "
                "least 1."));
}

// Returns the string form of an integer of the form 0x80....0 of the
// given bit width.
std::string big_num_ending_0(int bit_width) {
  return "0x8" + std::string(bit_width / 4 - 1, '0');
}

// Returns the string form of an integer of the form 0x80..001 of the
// given bit width.
std::string big_num_ending_1(int bit_width) {
  return "0x8" + std::string(bit_width / 4 - 2, '0') + "1";
}

TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding0InVulkan) {
  env_ = SPV_ENV_VULKAN_1_0;
  const int width = GetParam();
  for (int max_int_width : {32, 64}) {
    if (width > max_int_width) {
      // Not valid to even make the OpConstant in this case.
      continue;
    }
    const auto module = CompileSuccessfully(MakeArrayLength(
        big_num_ending_0(width), kUnsigned, width, max_int_width));
    EXPECT_EQ(SPV_SUCCESS, Val(module));
  }
}

TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding1InVulkan) {
  env_ = SPV_ENV_VULKAN_1_0;
  const int width = GetParam();
  for (int max_int_width : {32, 64}) {
    if (width > max_int_width) {
      // Not valid to even make the OpConstant in this case.
      continue;
    }
    const auto module = CompileSuccessfully(MakeArrayLength(
        big_num_ending_1(width), kUnsigned, width, max_int_width));
    EXPECT_EQ(SPV_SUCCESS, Val(module));
  }
}

// The only valid widths for integers are 8, 16, 32, and 64.
// Since the Int8 capability requires the Kernel capability, and the Kernel
// capability prohibits usage of signed integers, we can skip 8-bit integers
// here since the purpose of these tests is to check the validity of
// OpTypeArray, not OpTypeInt.
INSTANTIATE_TEST_SUITE_P(Widths, OpTypeArrayLengthTest,
                         ValuesIn(std::vector<int>{16, 32, 64}));

TEST_P(ValidateIdWithMessage, OpTypeArrayLengthNull) {
  std::string spirv = kGLSL450MemoryModel + R"(
%i32 = OpTypeInt 32 0
%len = OpConstantNull %i32
%ary = OpTypeArray %i32 %len)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message("OpTypeArray Length <id> '2[%2]' default "
                                     "value must be at least 1.")));
}

TEST_P(ValidateIdWithMessage, OpTypeArrayLengthSpecConst) {
  std::string spirv = kGLSL450MemoryModel + R"(
%i32 = OpTypeInt 32 0
%len = OpSpecConstant %i32 2
%ary = OpTypeArray %i32 %len)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpTypeArrayLengthSpecConstOp) {
  std::string spirv = kGLSL450MemoryModel + R"(
%i32 = OpTypeInt 32 0
%c1 = OpConstant %i32 1
%c2 = OpConstant %i32 2
%len = OpSpecConstantOp %i32 IAdd %c1 %c2
%ary = OpTypeArray %i32 %len)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpTypeRuntimeArrayGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeRuntimeArray %1)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpTypeRuntimeArrayBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 0
%3 = OpTypeRuntimeArray %2)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpTypeRuntimeArray Element Type <id> '2[%uint_0]' is not a "
                  "type.")));
}
// TODO: Object of this type can only be created with OpVariable using the
// Uniform Storage Class

TEST_P(ValidateIdWithMessage, OpTypeStructGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeFloat 64
%3 = OpTypePointer Input %1
%4 = OpTypeStruct %1 %2 %3)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpTypeStructMemberTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeFloat 64
%3 = OpConstant %2 0.0
%4 = OpTypeStruct %1 %2 %3)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpTypeStruct Member Type <id> '3[%double_0]' is not "
                  "a type.")));
}

TEST_P(ValidateIdWithMessage, OpTypeStructOpaqueTypeBad) {
  std::string spirv = R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint Vertex %main "main"
          %1 = OpTypeSampler
          %2 = OpTypeStruct %1
       %void = OpTypeVoid
          %3 = OpTypeFunction %void
       %main = OpFunction %void None %3
          %5 = OpLabel
               OpReturn
               OpFunctionEnd
)";
  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_0);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
  EXPECT_THAT(getDiagnosticString(),
              AnyVUID("VUID-StandaloneSpirv-None-04667"));
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("OpTypeStruct must not contain an opaque type")));
}

TEST_P(ValidateIdWithMessage, OpTypePointerGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypePointer Input %1)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpTypePointerBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 0
%3 = OpTypePointer Input %2)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("OpTypePointer Type <id> '2[%uint_0]' is not a "
                             "type.")));
}

TEST_P(ValidateIdWithMessage, OpTypeFunctionGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeFunction %1)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpTypeFunctionReturnTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 0
%3 = OpTypeFunction %2)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpTypeFunction Return Type <id> '2[%uint_0]' is not "
                  "a type.")));
}
TEST_P(ValidateIdWithMessage, OpTypeFunctionParameterBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpConstant %2 0
%4 = OpTypeFunction %1 %2 %3)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpTypeFunction Parameter Type <id> '3[%uint_0]' is not a "
                  "type.")));
}

TEST_P(ValidateIdWithMessage, OpTypeFunctionParameterTypeVoidBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%4 = OpTypeFunction %1 %2 %1)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpTypeFunction Parameter Type <id> '1[%void]' cannot "
                  "be OpTypeVoid.")));
}

TEST_P(ValidateIdWithMessage, OpTypePipeGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 16
%3 = OpTypePipe ReadOnly)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpConstantTrueGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeBool
%2 = OpConstantTrue %1)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpConstantTrueBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpConstantTrue %1)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpConstantTrue Result Type <id> '1[%void]' is not a boolean "
                  "type.")));
}

TEST_P(ValidateIdWithMessage, OpConstantFalseGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeBool
%2 = OpConstantTrue %1)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpConstantFalseBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpConstantFalse %1)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message(
          "OpConstantFalse Result Type <id> '1[%void]' is not a boolean "
          "type.")));
}

TEST_P(ValidateIdWithMessage, OpConstantGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 1)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpConstantBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpConstant !1 !0)";
  // The expected failure code is implementation dependent (currently
  // INVALID_BINARY because the binary parser catches these cases) and may
  // change over time, but this must always fail.
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpConstantCompositeVectorGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%3 = OpConstant %1 3.14
%4 = OpConstantComposite %2 %3 %3 %3 %3)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpConstantCompositeVectorWithUndefGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%3 = OpConstant %1 3.14
%9 = OpUndef %1
%4 = OpConstantComposite %2 %3 %3 %3 %9)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpConstantCompositeVectorResultTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%3 = OpConstant %1 3.14
%4 = OpConstantComposite %1 %3 %3 %3 %3)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpConstantComposite Result Type <id> '1[%float]' is not a "
                  "composite type.")));
}
TEST_P(ValidateIdWithMessage, OpConstantCompositeVectorConstituentTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%4 = OpTypeInt 32 0
%3 = OpConstant %1 3.14
%5 = OpConstant %4 42 ; bad type for constant value
%6 = OpConstantComposite %2 %3 %5 %3 %3)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpConstantComposite Constituent <id> '5[%uint_42]'s type "
                  "does not match Result Type <id> '2[%v4float]'s vector "
                  "element type.")));
}
TEST_P(ValidateIdWithMessage,
       OpConstantCompositeVectorConstituentUndefTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%4 = OpTypeInt 32 0
%3 = OpConstant %1 3.14
%5 = OpUndef %4 ; bad type for undef value
%6 = OpConstantComposite %2 %3 %5 %3 %3)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message(
          "OpConstantComposite Constituent <id> '5[%5]'s type does not "
          "match Result Type <id> '2[%v4float]'s vector element type.")));
}
TEST_P(ValidateIdWithMessage, OpConstantCompositeMatrixGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
 %1 = OpTypeFloat 32
 %2 = OpTypeVector %1 4
 %3 = OpTypeMatrix %2 4
 %4 = OpConstant %1 1.0
 %5 = OpConstant %1 0.0
 %6 = OpConstantComposite %2 %4 %5 %5 %5
 %7 = OpConstantComposite %2 %5 %4 %5 %5
 %8 = OpConstantComposite %2 %5 %5 %4 %5
 %9 = OpConstantComposite %2 %5 %5 %5 %4
%10 = OpConstantComposite %3 %6 %7 %8 %9)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpConstantCompositeMatrixUndefGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
 %1 = OpTypeFloat 32
 %2 = OpTypeVector %1 4
 %3 = OpTypeMatrix %2 4
 %4 = OpConstant %1 1.0
 %5 = OpConstant %1 0.0
 %6 = OpConstantComposite %2 %4 %5 %5 %5
 %7 = OpConstantComposite %2 %5 %4 %5 %5
 %8 = OpConstantComposite %2 %5 %5 %4 %5
 %9 = OpUndef %2
%10 = OpConstantComposite %3 %6 %7 %8 %9)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpConstantCompositeMatrixConstituentTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
 %1 = OpTypeFloat 32
 %2 = OpTypeVector %1 4
%11 = OpTypeVector %1 3
 %3 = OpTypeMatrix %2 4
 %4 = OpConstant %1 1.0
 %5 = OpConstant %1 0.0
 %6 = OpConstantComposite %2 %4 %5 %5 %5
 %7 = OpConstantComposite %2 %5 %4 %5 %5
 %8 = OpConstantComposite %2 %5 %5 %4 %5
 %9 = OpConstantComposite %11 %5 %5 %5
%10 = OpConstantComposite %3 %6 %7 %8 %9)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpConstantComposite Constituent <id> '10[%10]' vector "
                  "component count does not match Result Type <id> "
                  "'4[%mat4v4float]'s vector component count.")));
}
TEST_P(ValidateIdWithMessage,
       OpConstantCompositeMatrixConstituentUndefTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
 %1 = OpTypeFloat 32
 %2 = OpTypeVector %1 4
%11 = OpTypeVector %1 3
 %3 = OpTypeMatrix %2 4
 %4 = OpConstant %1 1.0
 %5 = OpConstant %1 0.0
 %6 = OpConstantComposite %2 %4 %5 %5 %5
 %7 = OpConstantComposite %2 %5 %4 %5 %5
 %8 = OpConstantComposite %2 %5 %5 %4 %5
 %9 = OpUndef %11
%10 = OpConstantComposite %3 %6 %7 %8 %9)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpConstantComposite Constituent <id> '10[%10]' vector "
                  "component count does not match Result Type <id> "
                  "'4[%mat4v4float]'s vector component count.")));
}
TEST_P(ValidateIdWithMessage, OpConstantCompositeArrayGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 4
%3 = OpTypeArray %1 %2
%4 = OpConstantComposite %3 %2 %2 %2 %2)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpConstantCompositeArrayWithUndefGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 4
%9 = OpUndef %1
%3 = OpTypeArray %1 %2
%4 = OpConstantComposite %3 %2 %2 %2 %9)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpConstantCompositeArrayConstConstituentTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 4
%3 = OpTypeArray %1 %2
%4 = OpConstantComposite %3 %2 %2 %2 %1)";  // Uses a type as operand
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message("Operand '1[%uint]' cannot be a "
                                     "type")));
}
TEST_P(ValidateIdWithMessage, OpConstantCompositeArrayConstConstituentBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 4
%3 = OpTypeArray %1 %2
%4 = OpTypePointer Uniform %1
%5 = OpVariable %4 Uniform
%6 = OpConstantComposite %3 %2 %2 %2 %5)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpConstantComposite Constituent <id> '5[%5]' is not a "
                  "constant or undef.")));
}
TEST_P(ValidateIdWithMessage, OpConstantCompositeArrayConstituentTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 4
%3 = OpTypeArray %1 %2
%5 = OpTypeFloat 32
%6 = OpConstant %5 3.14 ; bad type for const value
%4 = OpConstantComposite %3 %2 %2 %2 %6)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpConstantComposite Constituent <id> "
                  "'5[%float_3_1400001]'s type does not match Result "
                  "Type <id> '3[%_arr_uint_uint_4]'s array element "
                  "type.")));
}
TEST_P(ValidateIdWithMessage, OpConstantCompositeArrayConstituentUndefTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 4
%3 = OpTypeArray %1 %2
%5 = OpTypeFloat 32
%6 = OpUndef %5 ; bad type for undef
%4 = OpConstantComposite %3 %2 %2 %2 %6)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("OpConstantComposite Constituent <id> "
                             "'5[%5]'s type does not match Result "
                             "Type <id> '3[%_arr_uint_uint_4]'s array element "
                             "type.")));
}
TEST_P(ValidateIdWithMessage, OpConstantCompositeStructGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeInt 64 0
%3 = OpTypeStruct %1 %1 %2
%4 = OpConstant %1 42
%5 = OpConstant %2 4300000000
%6 = OpConstantComposite %3 %4 %4 %5)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpConstantCompositeStructUndefGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeInt 64 0
%3 = OpTypeStruct %1 %1 %2
%4 = OpConstant %1 42
%5 = OpUndef %2
%6 = OpConstantComposite %3 %4 %4 %5)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpConstantCompositeStructMemberTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeInt 64 0
%3 = OpTypeStruct %1 %1 %2
%4 = OpConstant %1 42
%5 = OpConstant %2 4300000000
%6 = OpConstantComposite %3 %4 %5 %4)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpConstantComposite Constituent <id> "
                  "'5[%ulong_4300000000]' type does not match the "
                  "Result Type <id> '3[%_struct_3]'s member type.")));
}

TEST_P(ValidateIdWithMessage, OpConstantCompositeStructMemberUndefTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeInt 64 0
%3 = OpTypeStruct %1 %1 %2
%4 = OpConstant %1 42
%5 = OpUndef %2
%6 = OpConstantComposite %3 %4 %5 %4)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpConstantComposite Constituent <id> '5[%5]' type "
                  "does not match the Result Type <id> '3[%_struct_3]'s "
                  "member type.")));
}

TEST_P(ValidateIdWithMessage, OpConstantSamplerGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%float = OpTypeFloat 32
%samplerType = OpTypeSampler
%3 = OpConstantSampler %samplerType ClampToEdge 0 Nearest)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpConstantSamplerResultTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpConstantSampler %1 Clamp 0 Nearest)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message(
          "OpConstantSampler Result Type <id> '1[%float]' is not a sampler "
          "type.")));
}

TEST_P(ValidateIdWithMessage, OpConstantNullGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
 %1 = OpTypeBool
 %2 = OpConstantNull %1
 %3 = OpTypeInt 32 0
 %4 = OpConstantNull %3
 %5 = OpTypeFloat 32
 %6 = OpConstantNull %5
 %7 = OpTypePointer UniformConstant %3
 %8 = OpConstantNull %7
 %9 = OpTypeEvent
%10 = OpConstantNull %9
%11 = OpTypeDeviceEvent
%12 = OpConstantNull %11
%13 = OpTypeReserveId
%14 = OpConstantNull %13
%15 = OpTypeQueue
%16 = OpConstantNull %15
%17 = OpTypeVector %5 2
%18 = OpConstantNull %17
%19 = OpTypeMatrix %17 2
%20 = OpConstantNull %19
%25 = OpConstant %3 8
%21 = OpTypeArray %3 %25
%22 = OpConstantNull %21
%23 = OpTypeStruct %3 %5 %1
%24 = OpConstantNull %23
%26 = OpTypeArray %17 %25
%27 = OpConstantNull %26
%28 = OpTypeStruct %7 %26 %26 %1
%29 = OpConstantNull %28
)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpConstantNullBasicBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpConstantNull %1)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message(
          "OpConstantNull Result Type <id> '1[%void]' cannot have a null "
          "value.")));
}

TEST_P(ValidateIdWithMessage, OpConstantNullArrayBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%2 = OpTypeInt 32 0
%3 = OpTypeSampler
%4 = OpConstant %2 4
%5 = OpTypeArray %3 %4
%6 = OpConstantNull %5)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message(
          "OpConstantNull Result Type <id> '4[%_arr_2_uint_4]' cannot have a "
          "null value.")));
}

TEST_P(ValidateIdWithMessage, OpConstantNullStructBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%2 = OpTypeSampler
%3 = OpTypeStruct %2 %2
%4 = OpConstantNull %3)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("OpConstantNull Result Type <id> '2[%_struct_2]' "
                             "cannot have a null value.")));
}

TEST_P(ValidateIdWithMessage, OpConstantNullRuntimeArrayBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%bool = OpTypeBool
%array = OpTypeRuntimeArray %bool
%null = OpConstantNull %array)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message(
          "OpConstantNull Result Type <id> '2[%_runtimearr_bool]' cannot have "
          "a null value.")));
}

TEST_P(ValidateIdWithMessage, OpSpecConstantTrueGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeBool
%2 = OpSpecConstantTrue %1)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpSpecConstantTrueBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpSpecConstantTrue %1)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpSpecConstantTrue Result Type <id> '1[%void]' is not "
                  "a boolean type")));
}

TEST_P(ValidateIdWithMessage, OpSpecConstantFalseGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeBool
%2 = OpSpecConstantFalse %1)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpSpecConstantFalseBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpSpecConstantFalse %1)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpSpecConstantFalse Result Type <id> '1[%void]' is not "
                  "a boolean type")));
}

TEST_P(ValidateIdWithMessage, OpSpecConstantGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpSpecConstant %1 42)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpSpecConstantBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpSpecConstant !1 !4)";
  // The expected failure code is implementation dependent (currently
  // INVALID_BINARY because the binary parser catches these cases) and may
  // change over time, but this must always fail.
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("Type Id 1 is not a scalar numeric type")));
}

// Valid: SpecConstantComposite specializes to a vector.
TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeVectorGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%3 = OpSpecConstant %1 3.14
%4 = OpConstant %1 3.14
%5 = OpSpecConstantComposite %2 %3 %3 %4 %4)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

// Valid: Vector of floats and Undefs.
TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeVectorWithUndefGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%3 = OpSpecConstant %1 3.14
%5 = OpConstant %1 3.14
%9 = OpUndef %1
%4 = OpSpecConstantComposite %2 %3 %5 %3 %9)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

// Invalid: result type is float.
TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeVectorResultTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%3 = OpSpecConstant %1 3.14
%4 = OpSpecConstantComposite %1 %3 %3 %3 %3)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message("is not a composite type")));
}

// Invalid: Vector contains a mix of Int and Float.
TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeVectorConstituentTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%4 = OpTypeInt 32 0
%3 = OpSpecConstant %1 3.14
%5 = OpConstant %4 42 ; bad type for constant value
%6 = OpSpecConstantComposite %2 %3 %5 %3 %3)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpSpecConstantComposite Constituent <id> "
                  "'5[%uint_42]'s type does not match Result Type <id> "
                  "'2[%v4float]'s vector element type.")));
}

// Invalid: Constituent is not a constant
TEST_P(ValidateIdWithMessage,
       OpSpecConstantCompositeVectorConstituentNotConstantBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%3 = OpTypeInt 32 0
%4 = OpSpecConstant %1 3.14
%5 = OpTypePointer Uniform %1
%6 = OpVariable %5 Uniform
%7 = OpSpecConstantComposite %2 %6 %4 %4 %4)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpSpecConstantComposite Constituent <id> '6[%6]' is "
                  "not a constant or undef.")));
}

// Invalid: Vector contains a mix of Undef-int and Float.
TEST_P(ValidateIdWithMessage,
       OpSpecConstantCompositeVectorConstituentUndefTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 4
%4 = OpTypeInt 32 0
%3 = OpSpecConstant %1 3.14
%5 = OpUndef %4 ; bad type for undef value
%6 = OpSpecConstantComposite %2 %3 %5 %3 %3)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpSpecConstantComposite Constituent <id> '5[%5]'s "
                  "type does not match Result Type <id> '2[%v4float]'s "
                  "vector element type.")));
}

// Invalid: Vector expects 3 components, but 4 specified.
TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeVectorNumComponentsBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeVector %1 3
%3 = OpConstant %1 3.14
%5 = OpSpecConstant %1 4.0
%6 = OpSpecConstantComposite %2 %3 %5 %3 %3)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpSpecConstantComposite Constituent <id> count does "
                  "not match Result Type <id> '2[%v3float]'s vector "
                  "component count.")));
}

// Valid: 4x4 matrix of floats
TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeMatrixGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
 %1 = OpTypeFloat 32
 %2 = OpTypeVector %1 4
 %3 = OpTypeMatrix %2 4
 %4 = OpConstant %1 1.0
 %5 = OpSpecConstant %1 0.0
 %6 = OpSpecConstantComposite %2 %4 %5 %5 %5
 %7 = OpSpecConstantComposite %2 %5 %4 %5 %5
 %8 = OpSpecConstantComposite %2 %5 %5 %4 %5
 %9 = OpSpecConstantComposite %2 %5 %5 %5 %4
%10 = OpSpecConstantComposite %3 %6 %7 %8 %9)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

// Valid: Matrix in which one column is Undef
TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeMatrixUndefGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
 %1 = OpTypeFloat 32
 %2 = OpTypeVector %1 4
 %3 = OpTypeMatrix %2 4
 %4 = OpConstant %1 1.0
 %5 = OpSpecConstant %1 0.0
 %6 = OpSpecConstantComposite %2 %4 %5 %5 %5
 %7 = OpSpecConstantComposite %2 %5 %4 %5 %5
 %8 = OpSpecConstantComposite %2 %5 %5 %4 %5
 %9 = OpUndef %2
%10 = OpSpecConstantComposite %3 %6 %7 %8 %9)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

// Invalid: Matrix in which the sizes of column vectors are not equal.
TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeMatrixConstituentTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
 %1 = OpTypeFloat 32
 %2 = OpTypeVector %1 4
 %3 = OpTypeVector %1 3
 %4 = OpTypeMatrix %2 4
 %5 = OpSpecConstant %1 1.0
 %6 = OpConstant %1 0.0
 %7 = OpSpecConstantComposite %2 %5 %6 %6 %6
 %8 = OpSpecConstantComposite %2 %6 %5 %6 %6
 %9 = OpSpecConstantComposite %2 %6 %6 %5 %6
 %10 = OpSpecConstantComposite %3 %6 %6 %6
%11 = OpSpecConstantComposite %4 %7 %8 %9 %10)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpSpecConstantComposite Constituent <id> '10[%10]' "
                  "vector component count does not match Result Type "
                  "<id> '4[%mat4v4float]'s vector component count.")));
}

// Invalid: Matrix type expects 4 columns but only 3 specified.
TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeMatrixNumColsBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
 %1 = OpTypeFloat 32
 %2 = OpTypeVector %1 4
 %3 = OpTypeMatrix %2 4
 %4 = OpSpecConstant %1 1.0
 %5 = OpConstant %1 0.0
 %6 = OpSpecConstantComposite %2 %4 %5 %5 %5
 %7 = OpSpecConstantComposite %2 %5 %4 %5 %5
 %8 = OpSpecConstantComposite %2 %5 %5 %4 %5
%10 = OpSpecConstantComposite %3 %6 %7 %8)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpSpecConstantComposite Constituent <id> count does "
                  "not match Result Type <id> '3[%mat4v4float]'s matrix column "
                  "count.")));
}

// Invalid: Composite contains a non-const/undef component
TEST_P(ValidateIdWithMessage,
       OpSpecConstantCompositeMatrixConstituentNotConstBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
 %1 = OpTypeFloat 32
 %2 = OpConstant %1 0.0
 %3 = OpTypeVector %1 4
 %4 = OpTypeMatrix %3 4
 %5 = OpSpecConstantComposite %3 %2 %2 %2 %2
 %6 = OpTypePointer Uniform %1
 %7 = OpVariable %6 Uniform
 %8 = OpSpecConstantComposite %4 %5 %5 %5 %7)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpSpecConstantComposite Constituent <id> '7[%7]' is "
                  "not a constant or undef.")));
}

// Invalid: Composite contains a column that is *not* a vector (it's an array)
TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeMatrixColTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
 %1 = OpTypeFloat 32
 %2 = OpTypeInt 32 0
 %3 = OpSpecConstant %2 4
 %4 = OpConstant %1 0.0
 %5 = OpTypeVector %1 4
 %6 = OpTypeArray %2 %3
 %7 = OpTypeMatrix %5 4
 %8  = OpSpecConstantComposite %6 %3 %3 %3 %3
 %9  = OpSpecConstantComposite %5 %4 %4 %4 %4
 %10 = OpSpecConstantComposite %7 %9 %9 %9 %8)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpSpecConstantComposite Constituent <id> '8[%8]' type "
                  "does not match Result Type <id> '7[%mat4v4float]'s "
                  "matrix column type.")));
}

// Invalid: Matrix with an Undef column of the wrong size.
TEST_P(ValidateIdWithMessage,
       OpSpecConstantCompositeMatrixConstituentUndefTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
 %1 = OpTypeFloat 32
 %2 = OpTypeVector %1 4
 %3 = OpTypeVector %1 3
 %4 = OpTypeMatrix %2 4
 %5 = OpSpecConstant %1 1.0
 %6 = OpSpecConstant %1 0.0
 %7 = OpSpecConstantComposite %2 %5 %6 %6 %6
 %8 = OpSpecConstantComposite %2 %6 %5 %6 %6
 %9 = OpSpecConstantComposite %2 %6 %6 %5 %6
 %10 = OpUndef %3
 %11 = OpSpecConstantComposite %4 %7 %8 %9 %10)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpSpecConstantComposite Constituent <id> '10[%10]' "
                  "vector component count does not match Result Type "
                  "<id> '4[%mat4v4float]'s vector component count.")));
}

// Invalid: Matrix in which some columns are Int and some are Float.
TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeMatrixColumnTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
 %1 = OpTypeInt 32 0
 %2 = OpTypeFloat 32
 %3 = OpTypeVector %1 2
 %4 = OpTypeVector %2 2
 %5 = OpTypeMatrix %4 2
 %6 = OpSpecConstant %1 42
 %7 = OpConstant %2 3.14
 %8 = OpSpecConstantComposite %3 %6 %6
 %9 = OpSpecConstantComposite %4 %7 %7
%10 = OpSpecConstantComposite %5 %8 %9)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpSpecConstantComposite Constituent <id> '8[%8]' "
                  "component type does not match Result Type <id> "
                  "'5[%mat2v2float]'s matrix column component type.")));
}

// Valid: Array of integers
TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeArrayGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpSpecConstant %1 4
%5 = OpConstant %1 5
%3 = OpTypeArray %1 %2
%6 = OpTypeArray %1 %5
%4 = OpSpecConstantComposite %3 %2 %2 %2 %2
%7 = OpSpecConstantComposite %3 %5 %5 %5 %5)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

// Invalid: Expecting an array of 4 components, but 3 specified.
TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeArrayNumComponentsBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 4
%3 = OpTypeArray %1 %2
%4 = OpSpecConstantComposite %3 %2 %2 %2)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpSpecConstantComposite Constituent count does not "
                  "match Result Type <id> '3[%_arr_uint_uint_4]'s array "
                  "length.")));
}

// Valid: Array of Integers and Undef-int
TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeArrayWithUndefGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpSpecConstant %1 4
%9 = OpUndef %1
%3 = OpTypeArray %1 %2
%4 = OpSpecConstantComposite %3 %2 %2 %2 %9)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

// Invalid: Array uses a type as operand.
TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeArrayConstConstituentBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 4
%3 = OpTypeArray %1 %2
%4 = OpTypePointer Uniform %1
%5 = OpVariable %4 Uniform
%6 = OpSpecConstantComposite %3 %2 %2 %2 %5)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpSpecConstantComposite Constituent <id> '5[%5]' is "
                  "not a constant or undef.")));
}

// Invalid: Array has a mix of Int and Float components.
TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeArrayConstituentTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpConstant %1 4
%3 = OpTypeArray %1 %2
%4 = OpTypeFloat 32
%5 = OpSpecConstant %4 3.14 ; bad type for const value
%6 = OpSpecConstantComposite %3 %2 %2 %2 %5)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpSpecConstantComposite Constituent <id> '5[%5]'s "
                  "type does not match Result Type <id> "
                  "'3[%_arr_uint_uint_4]'s array element type.")));
}

// Invalid: Array has a mix of Int and Undef-float.
TEST_P(ValidateIdWithMessage,
       OpSpecConstantCompositeArrayConstituentUndefTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpSpecConstant %1 4
%3 = OpTypeArray %1 %2
%5 = OpTypeFloat 32
%6 = OpUndef %5 ; bad type for undef
%4 = OpSpecConstantComposite %3 %2 %2 %2 %6)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpSpecConstantComposite Constituent <id> '5[%5]'s "
                  "type does not match Result Type <id> "
                  "'3[%_arr_uint_2]'s array element type.")));
}

// Valid: Struct of {Int32,Int32,Int64}.
TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeStructGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeInt 64 0
%3 = OpTypeStruct %1 %1 %2
%4 = OpConstant %1 42
%5 = OpSpecConstant %2 4300000000
%6 = OpSpecConstantComposite %3 %4 %4 %5)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

// Invalid: missing one int32 struct member.
TEST_P(ValidateIdWithMessage,
       OpSpecConstantCompositeStructMissingComponentBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%3 = OpTypeStruct %1 %1 %1
%4 = OpConstant %1 42
%5 = OpSpecConstant %1 430
%6 = OpSpecConstantComposite %3 %4 %5)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("OpSpecConstantComposite Constituent <id> "
                             "'2[%_struct_2]' count does not match Result Type "
                             "<id> '2[%_struct_2]'s struct member count.")));
}

// Valid: Struct uses Undef-int64.
TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeStructUndefGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeInt 64 0
%3 = OpTypeStruct %1 %1 %2
%4 = OpSpecConstant %1 42
%5 = OpUndef %2
%6 = OpSpecConstantComposite %3 %4 %4 %5)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

// Invalid: Composite contains non-const/undef component.
TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeStructNonConstBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeInt 64 0
%3 = OpTypeStruct %1 %1 %2
%4 = OpSpecConstant %1 42
%5 = OpUndef %2
%6 = OpTypePointer Uniform %1
%7 = OpVariable %6 Uniform
%8 = OpSpecConstantComposite %3 %4 %7 %5)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpSpecConstantComposite Constituent <id> '7[%7]' is "
                  "not a constant or undef.")));
}

// Invalid: Struct component type does not match expected specialization type.
// Second component was expected to be Int32, but got Int64.
TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeStructMemberTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeInt 64 0
%3 = OpTypeStruct %1 %1 %2
%4 = OpConstant %1 42
%5 = OpSpecConstant %2 4300000000
%6 = OpSpecConstantComposite %3 %4 %5 %4)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpSpecConstantComposite Constituent <id> '5[%5]' type "
                  "does not match the Result Type <id> '3[%_struct_3]'s "
                  "member type.")));
}

// Invalid: Undef-int64 used when Int32 was expected.
TEST_P(ValidateIdWithMessage, OpSpecConstantCompositeStructMemberUndefTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeInt 64 0
%3 = OpTypeStruct %1 %1 %2
%4 = OpSpecConstant %1 42
%5 = OpUndef %2
%6 = OpSpecConstantComposite %3 %4 %5 %4)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpSpecConstantComposite Constituent <id> '5[%5]' type "
                  "does not match the Result Type <id> '3[%_struct_3]'s "
                  "member type.")));
}

// TODO: OpSpecConstantOp

TEST_P(ValidateIdWithMessage, OpVariableGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypePointer Input %1
%3 = OpVariable %2 Input)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpVariableInitializerConstantGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypePointer Output %1
%3 = OpConstant %1 42
%4 = OpVariable %2 Output %3)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpVariableInitializerGlobalVariableGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypePointer Uniform %1
%3 = OpVariable %2 Uniform
%4 = OpTypePointer Private %2 ; pointer to pointer
%5 = OpVariable %4 Private %3
)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
// TODO: Positive test OpVariable with OpConstantNull of OpTypePointer
TEST_P(ValidateIdWithMessage, OpVariableResultTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpVariable %1 Input)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpVariable Result Type <id> '1[%uint]' is not a pointer "
                  "type.")));
}
TEST_P(ValidateIdWithMessage, OpVariableInitializerIsTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypePointer Input %1
%3 = OpVariable %2 Input %2)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message("Operand '2[%_ptr_Input_uint]' "
                                     "cannot be a type")));
}

TEST_P(ValidateIdWithMessage, OpVariableInitializerIsFunctionVarBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%int = OpTypeInt 32 0
%ptrint = OpTypePointer Function %int
%ptrptrint = OpTypePointer Function %ptrint
%void = OpTypeVoid
%fnty = OpTypeFunction %void
%main = OpFunction %void None %fnty
%entry = OpLabel
%var = OpVariable %ptrint Function
%varinit = OpVariable %ptrptrint Function %var ; Can't initialize function variable.
OpReturn
OpFunctionEnd
)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpVariable Initializer <id> '8[%8]' is not a constant "
                  "or module-scope variable")));
}

TEST_P(ValidateIdWithMessage, OpVariableInitializerIsModuleVarGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%int = OpTypeInt 32 0
%ptrint = OpTypePointer Uniform %int
%mvar = OpVariable %ptrint Uniform
%ptrptrint = OpTypePointer Function %ptrint
%void = OpTypeVoid
%fnty = OpTypeFunction %void
%main = OpFunction %void None %fnty
%entry = OpLabel
%goodvar = OpVariable %ptrptrint Function %mvar ; This is ok
OpReturn
OpFunctionEnd
)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpVariableContainsBoolBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%bool = OpTypeBool
%int = OpTypeInt 32 0
%block = OpTypeStruct %bool %int
%_ptr_Uniform_block = OpTypePointer Uniform %block
%var = OpVariable %_ptr_Uniform_block Uniform
%void = OpTypeVoid
%fnty = OpTypeFunction %void
%main = OpFunction %void None %fnty
%entry = OpLabel
%load = OpLoad %block %var
OpReturn
OpFunctionEnd
)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message(
          "If OpTypeBool is stored in conjunction with OpVariable, it can only "
          "be used with non-externally visible shader Storage Classes: "
          "Workgroup, CrossWorkgroup, Private, Function, Input, Output, "
          "RayPayloadKHR, IncomingRayPayloadKHR, HitAttributeKHR, "
          "CallableDataKHR, or IncomingCallableDataKHR")));
}

TEST_P(ValidateIdWithMessage, OpVariableContainsBoolPrivateGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%bool = OpTypeBool
%int = OpTypeInt 32 0
%block = OpTypeStruct %bool %int
%_ptr_Private_block = OpTypePointer Private %block
%var = OpVariable %_ptr_Private_block Private
%void = OpTypeVoid
%fnty = OpTypeFunction %void
%main = OpFunction %void None %fnty
%entry = OpLabel
%load = OpLoad %block %var
OpReturn
OpFunctionEnd
)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpVariableContainsBoolPointerGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%bool = OpTypeBool
%boolptr = OpTypePointer Uniform %bool
%int = OpTypeInt 32 0
%block = OpTypeStruct %boolptr %int
%_ptr_Uniform_block = OpTypePointer Uniform %block
%var = OpVariable %_ptr_Uniform_block Uniform
%void = OpTypeVoid
%fnty = OpTypeFunction %void
%main = OpFunction %void None %fnty
%entry = OpLabel
%load = OpLoad %block %var
OpReturn
OpFunctionEnd
)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpVariableContainsBuiltinBoolGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
OpMemberDecorate %input 0 BuiltIn FrontFacing
%bool = OpTypeBool
%input = OpTypeStruct %bool
%_ptr_input = OpTypePointer Input %input
%var = OpVariable %_ptr_input Input
%void = OpTypeVoid
%fnty = OpTypeFunction %void
%main = OpFunction %void None %fnty
%entry = OpLabel
%load = OpLoad %input %var
OpReturn
OpFunctionEnd
)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpVariableContainsNoBuiltinBoolBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%bool = OpTypeBool
%input = OpTypeStruct %bool
%_ptr_input = OpTypePointer Input %input
%var = OpVariable %_ptr_input Input
%void = OpTypeVoid
%fnty = OpTypeFunction %void
%main = OpFunction %void None %fnty
%entry = OpLabel
%load = OpLoad %input %var
OpReturn
OpFunctionEnd
)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message(
          "If OpTypeBool is stored in conjunction with OpVariable using Input "
          "or Output Storage Classes it requires a BuiltIn decoration")));
}

TEST_P(ValidateIdWithMessage, OpVariableContainsNoBuiltinBoolBadVulkan) {
  std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %var
OpExecutionMode %main OriginUpperLeft
%bool = OpTypeBool
%input = OpTypeStruct %bool
%_ptr_input = OpTypePointer Input %input
%var = OpVariable %_ptr_input Input
%void = OpTypeVoid
%fnty = OpTypeFunction %void
%main = OpFunction %void None %fnty
%entry = OpLabel
%load = OpLoad %input %var
OpReturn
OpFunctionEnd
)";
  CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_0);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
  EXPECT_THAT(getDiagnosticString(),
              AnyVUID("VUID-StandaloneSpirv-Input-07290"));
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message(
          "If OpTypeBool is stored in conjunction with OpVariable using Input "
          "or Output Storage Classes it requires a BuiltIn decoration")));
}

TEST_P(ValidateIdWithMessage, OpVariableContainsRayPayloadBoolGood) {
  std::string spirv = R"(
OpCapability RayTracingNV
OpCapability Shader
OpCapability Linkage
OpExtension "SPV_NV_ray_tracing"
OpMemoryModel Logical GLSL450
%bool = OpTypeBool
%PerRayData = OpTypeStruct %bool
%_ptr_PerRayData = OpTypePointer RayPayloadNV %PerRayData
%var = OpVariable %_ptr_PerRayData RayPayloadNV
%void = OpTypeVoid
%fnty = OpTypeFunction %void
%main = OpFunction %void None %fnty
%entry = OpLabel
%load = OpLoad %PerRayData %var
OpReturn
OpFunctionEnd
)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpVariablePointerNoVariablePointersBad) {
  const std::string spirv = R"(
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
%void = OpTypeVoid
%int = OpTypeInt 32 0
%_ptr_workgroup_int = OpTypePointer Workgroup %int
%_ptr_function_ptr = OpTypePointer Function %_ptr_workgroup_int
%voidfn = OpTypeFunction %void
%func = OpFunction %void None %voidfn
%entry = OpLabel
%var = OpVariable %_ptr_function_ptr Function
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message(
          "In Logical addressing, variables may not allocate a pointer type")));
}

TEST_P(ValidateIdWithMessage,
       OpVariablePointerNoVariablePointersRelaxedLogicalGood) {
  const std::string spirv = R"(
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
%void = OpTypeVoid
%int = OpTypeInt 32 0
%_ptr_workgroup_int = OpTypePointer Workgroup %int
%_ptr_function_ptr = OpTypePointer Function %_ptr_workgroup_int
%voidfn = OpTypeFunction %void
%func = OpFunction %void None %voidfn
%entry = OpLabel
%var = OpVariable %_ptr_function_ptr Function
OpReturn
OpFunctionEnd
)";

  auto options = getValidatorOptions();
  options->relax_logical_pointer = true;
  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpFunctionWithNonMemoryObject) {
  // DXC generates code that looks like when given something like:
  //   T t;
  //   t.s.fn_1();
  // This needs to be accepted before legalization takes place, so we
  // will include it with the relaxed logical pointer.

  const std::string spirv = R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint Vertex %1 "main"
               OpSource HLSL 600
        %int = OpTypeInt 32 1
      %int_0 = OpConstant %int 0
       %void = OpTypeVoid
          %9 = OpTypeFunction %void
  %_struct_5 = OpTypeStruct
  %_struct_6 = OpTypeStruct %_struct_5
%_ptr_Function__struct_6 = OpTypePointer Function %_struct_6
%_ptr_Function__struct_5 = OpTypePointer Function %_struct_5
         %23 = OpTypeFunction %void %_ptr_Function__struct_5
          %1 = OpFunction %void None %9
         %10 = OpLabel
         %11 = OpVariable %_ptr_Function__struct_6 Function
         %20 = OpAccessChain %_ptr_Function__struct_5 %11 %int_0
         %21 = OpFunctionCall %void %12 %20
               OpReturn
               OpFunctionEnd
         %12 = OpFunction %void None %23
         %13 = OpFunctionParameter %_ptr_Function__struct_5
         %14 = OpLabel
               OpReturn
               OpFunctionEnd
)";

  auto options = getValidatorOptions();
  options->relax_logical_pointer = true;
  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage,
       OpVariablePointerVariablePointersStorageBufferGood) {
  const std::string spirv = R"(
OpCapability Shader
OpCapability Linkage
OpCapability VariablePointersStorageBuffer
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
%void = OpTypeVoid
%int = OpTypeInt 32 0
%_ptr_workgroup_int = OpTypePointer Workgroup %int
%_ptr_function_ptr = OpTypePointer Function %_ptr_workgroup_int
%voidfn = OpTypeFunction %void
%func = OpFunction %void None %voidfn
%entry = OpLabel
%var = OpVariable %_ptr_function_ptr Function
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpVariablePointerVariablePointersGood) {
  const std::string spirv = R"(
OpCapability Shader
OpCapability Linkage
OpCapability VariablePointers
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
%void = OpTypeVoid
%int = OpTypeInt 32 0
%_ptr_workgroup_int = OpTypePointer Workgroup %int
%_ptr_function_ptr = OpTypePointer Function %_ptr_workgroup_int
%voidfn = OpTypeFunction %void
%func = OpFunction %void None %voidfn
%entry = OpLabel
%var = OpVariable %_ptr_function_ptr Function
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpVariablePointerVariablePointersBad) {
  const std::string spirv = R"(
OpCapability Shader
OpCapability VariablePointers
OpExtension "SPV_KHR_variable_pointers"
OpMemoryModel Logical GLSL450
%void = OpTypeVoid
%int = OpTypeInt 32 0
%_ptr_workgroup_int = OpTypePointer Workgroup %int
%_ptr_uniform_ptr = OpTypePointer Uniform %_ptr_workgroup_int
%var = OpVariable %_ptr_uniform_ptr Uniform
)";

  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "In Logical addressing with variable pointers, "
                  "variables that allocate pointers must be in Function "
                  "or Private storage classes")));
}

TEST_P(ValidateIdWithMessage, OpLoadGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
 %1 = OpTypeVoid
 %2 = OpTypeInt 32 0
 %3 = OpTypePointer UniformConstant %2
 %4 = OpTypeFunction %1
 %5 = OpVariable %3 UniformConstant
 %6 = OpFunction %1 None %4
 %7 = OpLabel
 %8 = OpLoad %2 %5
      OpReturn
      OpFunctionEnd
)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

// TODO: Add tests that exercise VariablePointersStorageBuffer instead of
// VariablePointers.
void createVariablePointerSpirvProgram(std::ostringstream* spirv,
                                       std::string result_strategy,
                                       bool use_varptr_cap,
                                       bool add_helper_function) {
  *spirv << "OpCapability Shader ";
  if (use_varptr_cap) {
    *spirv << "OpCapability VariablePointers ";
    *spirv << "OpExtension \"SPV_KHR_variable_pointers\" ";
  }
  *spirv << "OpExtension \"SPV_KHR_storage_buffer_storage_class\" ";
  *spirv << R"(
    OpMemoryModel Logical GLSL450
    OpEntryPoint GLCompute %main "main"
    %void      = OpTypeVoid
    %voidf     = OpTypeFunction %void
    %bool      = OpTypeBool
    %i32       = OpTypeInt 32 1
    %f32       = OpTypeFloat 32
    %f32ptr    = OpTypePointer StorageBuffer %f32
    %i         = OpConstant %i32 1
    %zero      = OpConstant %i32 0
    %float_1   = OpConstant %f32 1.0
    %ptr1      = OpVariable %f32ptr StorageBuffer
    %ptr2      = OpVariable %f32ptr StorageBuffer
  )";
  if (add_helper_function) {
    *spirv << R"(
      ; ////////////////////////////////////////////////////////////
      ;;;; Function that returns a pointer
      ; ////////////////////////////////////////////////////////////
      %selector_func_type  = OpTypeFunction %f32ptr %bool %f32ptr %f32ptr
      %choose_input_func   = OpFunction %f32ptr None %selector_func_type
      %is_neg_param        = OpFunctionParameter %bool
      %first_ptr_param     = OpFunctionParameter %f32ptr
      %second_ptr_param    = OpFunctionParameter %f32ptr
      %selector_func_begin = OpLabel
      %result_ptr          = OpSelect %f32ptr %is_neg_param %first_ptr_param %second_ptr_param
      OpReturnValue %result_ptr
      OpFunctionEnd
    )";
  }
  *spirv << R"(
    %main      = OpFunction %void None %voidf
    %label     = OpLabel
  )";
  *spirv << result_strategy;
  *spirv << R"(
    OpReturn
    OpFunctionEnd
  )";
}

// With the VariablePointer Capability, OpLoad should allow loading a
// VaiablePointer. In this test the variable pointer is obtained by an OpSelect
TEST_P(ValidateIdWithMessage, OpLoadVarPtrOpSelectGood) {
  std::string result_strategy = R"(
    %isneg     = OpSLessThan %bool %i %zero
    %varptr    = OpSelect %f32ptr %isneg %ptr1 %ptr2
    %result    = OpLoad %f32 %varptr
  )";

  std::ostringstream spirv;
  createVariablePointerSpirvProgram(&spirv, result_strategy,
                                    true /* Add VariablePointers Capability? */,
                                    false /* Use Helper Function? */);
  CompileSuccessfully(spirv.str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

// Without the VariablePointers Capability, OpLoad will not allow loading
// through a variable pointer.
// Disabled since using OpSelect with pointers without VariablePointers will
// fail LogicalsPass.
TEST_P(ValidateIdWithMessage, DISABLED_OpLoadVarPtrOpSelectBad) {
  std::string result_strategy = R"(
    %isneg     = OpSLessThan %bool %i %zero
    %varptr    = OpSelect %f32ptr %isneg %ptr1 %ptr2
    %result    = OpLoad %f32 %varptr
  )";

  std::ostringstream spirv;
  createVariablePointerSpirvProgram(&spirv, result_strategy,
                                    false /* Add VariablePointers Capability?*/,
                                    false /* Use Helper Function? */);
  CompileSuccessfully(spirv.str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message("is not a logical pointer.")));
}

// With the VariablePointer Capability, OpLoad should allow loading a
// VaiablePointer. In this test the variable pointer is obtained by an OpPhi
TEST_P(ValidateIdWithMessage, OpLoadVarPtrOpPhiGood) {
  std::string result_strategy = R"(
    %is_neg      = OpSLessThan %bool %i %zero
    OpSelectionMerge %end_label None
    OpBranchConditional %is_neg %take_ptr_1 %take_ptr_2
    %take_ptr_1 = OpLabel
    OpBranch      %end_label
    %take_ptr_2 = OpLabel
    OpBranch      %end_label
    %end_label  = OpLabel
    %varptr     = OpPhi %f32ptr %ptr1 %take_ptr_1 %ptr2 %take_ptr_2
    %result     = OpLoad %f32 %varptr
  )";

  std::ostringstream spirv;
  createVariablePointerSpirvProgram(&spirv, result_strategy,
                                    true /* Add VariablePointers Capability?*/,
                                    false /* Use Helper Function? */);
  CompileSuccessfully(spirv.str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

// Without the VariablePointers Capability, OpPhi can have a pointer result
// type.
TEST_P(ValidateIdWithMessage, OpPhiBad) {
  std::string result_strategy = R"(
    %is_neg      = OpSLessThan %bool %i %zero
    OpSelectionMerge %end_label None
    OpBranchConditional %is_neg %take_ptr_1 %take_ptr_2
    %take_ptr_1 = OpLabel
    OpBranch      %end_label
    %take_ptr_2 = OpLabel
    OpBranch      %end_label
    %end_label  = OpLabel
    %varptr     = OpPhi %f32ptr %ptr1 %take_ptr_1 %ptr2 %take_ptr_2
    %result     = OpLoad %f32 %varptr
  )";

  std::ostringstream spirv;
  createVariablePointerSpirvProgram(&spirv, result_strategy,
                                    false /* Add VariablePointers Capability?*/,
                                    false /* Use Helper Function? */);
  CompileSuccessfully(spirv.str());
  EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "Using pointers with OpPhi requires capability "
                  "VariablePointers or VariablePointersStorageBuffer")));
}

// With the VariablePointer Capability, OpLoad should allow loading through a
// VaiablePointer. In this test the variable pointer is obtained from an
// OpFunctionCall (return value from a function)
TEST_P(ValidateIdWithMessage, OpLoadVarPtrOpFunctionCallGood) {
  std::ostringstream spirv;
  std::string result_strategy = R"(
    %isneg     = OpSLessThan %bool %i %zero
    %varptr    = OpFunctionCall %f32ptr %choose_input_func %isneg %ptr1 %ptr2
    %result    = OpLoad %f32 %varptr
  )";

  createVariablePointerSpirvProgram(&spirv, result_strategy,
                                    true /* Add VariablePointers Capability?*/,
                                    true /* Use Helper Function? */);
  CompileSuccessfully(spirv.str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpLoadResultTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer UniformConstant %2
%4 = OpTypeFunction %1
%5 = OpVariable %3 UniformConstant
%6 = OpFunction %1 None %4
%7 = OpLabel
%8 = OpLoad %3 %5
     OpReturn
     OpFunctionEnd
)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("OpLoad Result Type <id> "
                             "'3[%_ptr_UniformConstant_uint]' does not match "
                             "Pointer <id> '5[%5]'s type.")));
}

TEST_P(ValidateIdWithMessage, OpLoadPointerBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer UniformConstant %2
%4 = OpTypeFunction %1
%5 = OpFunction %1 None %4
%6 = OpLabel
%7 = OpLoad %2 %8
     OpReturn
     OpFunctionEnd
)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  // Prove that SSA checks trigger for a bad Id value.
  // The next test case show the not-a-logical-pointer case.
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message("ID '8[%8]' has not been "
                                     "defined")));
}

// Disabled as bitcasting type to object is now not valid.
TEST_P(ValidateIdWithMessage, DISABLED_OpLoadLogicalPointerBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypeFloat 32
%4 = OpTypePointer UniformConstant %2
%5 = OpTypePointer UniformConstant %3
%6 = OpTypeFunction %1
%7 = OpFunction %1 None %6
%8 = OpLabel
%9 = OpBitcast %5 %4 ; Not valid in logical addressing
%10 = OpLoad %3 %9 ; Should trigger message
     OpReturn
     OpFunctionEnd
)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  // Once we start checking bitcasts, we might catch that
  // as the error first, instead of catching it here.
  // I don't know if it's possible to generate a bad case
  // if/when the validator is complete.
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpLoad Pointer <id> '9' is not a logical pointer.")));
}

TEST_P(ValidateIdWithMessage, OpStoreGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Uniform %2
%4 = OpTypeFunction %1
%5 = OpConstant %2 42
%6 = OpVariable %3 Uniform
%7 = OpFunction %1 None %4
%8 = OpLabel
     OpStore %6 %5
     OpReturn
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpStorePointerBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer UniformConstant %2
%4 = OpTypeFunction %1
%5 = OpConstant %2 42
%6 = OpVariable %3 UniformConstant
%7 = OpConstant %2 0
%8 = OpFunction %1 None %4
%9 = OpLabel
     OpStore %7 %5
     OpReturn
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpStore Pointer <id> '7[%uint_0]' is not a logical "
                  "pointer.")));
}

// Disabled as bitcasting type to object is now not valid.
TEST_P(ValidateIdWithMessage, DISABLED_OpStoreLogicalPointerBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypeFloat 32
%4 = OpTypePointer UniformConstant %2
%5 = OpTypePointer UniformConstant %3
%6 = OpTypeFunction %1
%7 = OpConstantNull %5
%8 = OpFunction %1 None %6
%9 = OpLabel
%10 = OpBitcast %5 %4 ; Not valid in logical addressing
%11 = OpStore %10 %7 ; Should trigger message
     OpReturn
     OpFunctionEnd
)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpStore Pointer <id> '10' is not a logical pointer.")));
}

// Without the VariablePointer Capability, OpStore should may not store
// through a variable pointer.
// Disabled since using OpSelect with pointers without VariablePointers will
// fail LogicalsPass.
TEST_P(ValidateIdWithMessage, DISABLED_OpStoreVarPtrBad) {
  std::string result_strategy = R"(
    %isneg     = OpSLessThan %bool %i %zero
    %varptr    = OpSelect %f32ptr %isneg %ptr1 %ptr2
                 OpStore %varptr %float_1
  )";

  std::ostringstream spirv;
  createVariablePointerSpirvProgram(
      &spirv, result_strategy, false /* Add VariablePointers Capability? */,
      false /* Use Helper Function? */);
  CompileSuccessfully(spirv.str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message("is not a logical pointer.")));
}

// With the VariablePointer Capability, OpStore should allow storing through a
// variable pointer.
TEST_P(ValidateIdWithMessage, OpStoreVarPtrGood) {
  std::string result_strategy = R"(
    %isneg     = OpSLessThan %bool %i %zero
    %varptr    = OpSelect %f32ptr %isneg %ptr1 %ptr2
                 OpStore %varptr %float_1
  )";

  std::ostringstream spirv;
  createVariablePointerSpirvProgram(&spirv, result_strategy,
                                    true /* Add VariablePointers Capability? */,
                                    false /* Use Helper Function? */);
  CompileSuccessfully(spirv.str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpStoreObjectGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Uniform %2
%4 = OpTypeFunction %1
%5 = OpConstant %2 42
%6 = OpVariable %3 Uniform
%7 = OpFunction %1 None %4
%8 = OpLabel
%9 = OpFunctionCall %1 %10
     OpStore %6 %9
     OpReturn
     OpFunctionEnd
%10 = OpFunction %1 None %4
%11 = OpLabel
      OpReturn
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("OpStore Object <id> '9[%9]'s type is void.")));
}
TEST_P(ValidateIdWithMessage, OpStoreTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%9 = OpTypeFloat 32
%3 = OpTypePointer Uniform %2
%4 = OpTypeFunction %1
%5 = OpConstant %9 3.14
%6 = OpVariable %3 Uniform
%7 = OpFunction %1 None %4
%8 = OpLabel
     OpStore %6 %5
     OpReturn
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpStore Pointer <id> '7[%7]'s type does not match "
                  "Object <id> '6[%float_3_1400001]'s type.")));
}

// The next series of test check test a relaxation of the rules for stores to
// structs.  The first test checks that we get a failure when the option is not
// set to relax the rule.
// TODO: Add tests for layout compatible arrays and matricies when the validator
//       relaxes the rules for them as well.  Also need test to check for layout
//       decorations specific to those types.
TEST_P(ValidateIdWithMessage, OpStoreTypeBadStruct) {
  std::string spirv = kGLSL450MemoryModel + R"(
     OpMemberDecorate %1 0 Offset 0
     OpMemberDecorate %1 1 Offset 4
     OpMemberDecorate %2 0 Offset 0
     OpMemberDecorate %2 1 Offset 4
%3 = OpTypeVoid
%4 = OpTypeFloat 32
%1 = OpTypeStruct %4 %4
%5 = OpTypePointer Uniform %1
%2 = OpTypeStruct %4 %4
%6 = OpTypeFunction %3
%7 = OpConstant %4 3.14
%8 = OpVariable %5 Uniform
%9 = OpFunction %3 None %6
%10 = OpLabel
%11 = OpCompositeConstruct %2 %7 %7
      OpStore %8 %11
      OpReturn
      OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpStore Pointer <id> '8[%8]'s type does not match "
                  "Object <id> '11[%11]'s type.")));
}

// Same code as the last test.  The difference is that we relax the rule.
// Because the structs %3 and %5 are defined the same way.
TEST_P(ValidateIdWithMessage, OpStoreTypeRelaxedStruct) {
  std::string spirv = kGLSL450MemoryModel + R"(
     OpMemberDecorate %1 0 Offset 0
     OpMemberDecorate %1 1 Offset 4
     OpMemberDecorate %2 0 Offset 0
     OpMemberDecorate %2 1 Offset 4
%3 = OpTypeVoid
%4 = OpTypeFloat 32
%1 = OpTypeStruct %4 %4
%5 = OpTypePointer Uniform %1
%2 = OpTypeStruct %4 %4
%6 = OpTypeFunction %3
%7 = OpConstant %4 3.14
%8 = OpVariable %5 Uniform
%9 = OpFunction %3 None %6
%10 = OpLabel
%11 = OpCompositeConstruct %2 %7 %7
      OpStore %8 %11
      OpReturn
      OpFunctionEnd)";
  spvValidatorOptionsSetRelaxStoreStruct(options_, true);
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

// Same code as the last test except for an extra decoration on one of the
// members. With the relaxed rules, the code is still valid.
TEST_P(ValidateIdWithMessage, OpStoreTypeRelaxedStructWithExtraDecoration) {
  std::string spirv = kGLSL450MemoryModel + R"(
     OpMemberDecorate %1 0 Offset 0
     OpMemberDecorate %1 1 Offset 4
     OpMemberDecorate %1 0 RelaxedPrecision
     OpMemberDecorate %2 0 Offset 0
     OpMemberDecorate %2 1 Offset 4
%3 = OpTypeVoid
%4 = OpTypeFloat 32
%1 = OpTypeStruct %4 %4
%5 = OpTypePointer Uniform %1
%2 = OpTypeStruct %4 %4
%6 = OpTypeFunction %3
%7 = OpConstant %4 3.14
%8 = OpVariable %5 Uniform
%9 = OpFunction %3 None %6
%10 = OpLabel
%11 = OpCompositeConstruct %2 %7 %7
      OpStore %8 %11
      OpReturn
      OpFunctionEnd)";
  spvValidatorOptionsSetRelaxStoreStruct(options_, true);
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

// This test check that we recursively traverse the struct to check if they are
// interchangable.
TEST_P(ValidateIdWithMessage, OpStoreTypeRelaxedNestedStruct) {
  std::string spirv = kGLSL450MemoryModel + R"(
     OpMemberDecorate %1 0 Offset 0
     OpMemberDecorate %1 1 Offset 4
     OpMemberDecorate %2 0 Offset 0
     OpMemberDecorate %2 1 Offset 8
     OpMemberDecorate %3 0 Offset 0
     OpMemberDecorate %3 1 Offset 4
     OpMemberDecorate %4 0 Offset 0
     OpMemberDecorate %4 1 Offset 8
%5 = OpTypeVoid
%6 = OpTypeInt 32 0
%7 = OpTypeFloat 32
%1 = OpTypeStruct %7 %6
%2 = OpTypeStruct %1 %1
%8 = OpTypePointer Uniform %2
%3 = OpTypeStruct %7 %6
%4 = OpTypeStruct %3 %3
%9 = OpTypeFunction %5
%10 = OpConstant %6 7
%11 = OpConstant %7 3.14
%12 = OpConstantComposite %3 %11 %10
%13 = OpVariable %8 Uniform
%14 = OpFunction %5 None %9
%15 = OpLabel
%16 = OpCompositeConstruct %4 %12 %12
      OpStore %13 %16
      OpReturn
      OpFunctionEnd)";
  spvValidatorOptionsSetRelaxStoreStruct(options_, true);
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

// This test check that the even with the relaxed rules an error is identified
// if the members of the struct are in a different order.
TEST_P(ValidateIdWithMessage, OpStoreTypeBadRelaxedStruct1) {
  std::string spirv = kGLSL450MemoryModel + R"(
     OpMemberDecorate %1 0 Offset 0
     OpMemberDecorate %1 1 Offset 4
     OpMemberDecorate %2 0 Offset 0
     OpMemberDecorate %2 1 Offset 8
     OpMemberDecorate %3 0 Offset 0
     OpMemberDecorate %3 1 Offset 4
     OpMemberDecorate %4 0 Offset 0
     OpMemberDecorate %4 1 Offset 8
%5 = OpTypeVoid
%6 = OpTypeInt 32 0
%7 = OpTypeFloat 32
%1 = OpTypeStruct %6 %7
%2 = OpTypeStruct %1 %1
%8 = OpTypePointer Uniform %2
%3 = OpTypeStruct %7 %6
%4 = OpTypeStruct %3 %3
%9 = OpTypeFunction %5
%10 = OpConstant %6 7
%11 = OpConstant %7 3.14
%12 = OpConstantComposite %3 %11 %10
%13 = OpVariable %8 Uniform
%14 = OpFunction %5 None %9
%15 = OpLabel
%16 = OpCompositeConstruct %4 %12 %12
      OpStore %13 %16
      OpReturn
      OpFunctionEnd)";
  spvValidatorOptionsSetRelaxStoreStruct(options_, true);
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message(
          "OpStore Pointer <id> '13[%13]'s layout does not match Object "
          "<id> '16[%16]'s layout.")));
}

// This test check that the even with the relaxed rules an error is identified
// if the members of the struct are at different offsets.
TEST_P(ValidateIdWithMessage, OpStoreTypeBadRelaxedStruct2) {
  std::string spirv = kGLSL450MemoryModel + R"(
     OpMemberDecorate %1 0 Offset 4
     OpMemberDecorate %1 1 Offset 0
     OpMemberDecorate %2 0 Offset 0
     OpMemberDecorate %2 1 Offset 8
     OpMemberDecorate %3 0 Offset 0
     OpMemberDecorate %3 1 Offset 4
     OpMemberDecorate %4 0 Offset 0
     OpMemberDecorate %4 1 Offset 8
%5 = OpTypeVoid
%6 = OpTypeInt 32 0
%7 = OpTypeFloat 32
%1 = OpTypeStruct %7 %6
%2 = OpTypeStruct %1 %1
%8 = OpTypePointer Uniform %2
%3 = OpTypeStruct %7 %6
%4 = OpTypeStruct %3 %3
%9 = OpTypeFunction %5
%10 = OpConstant %6 7
%11 = OpConstant %7 3.14
%12 = OpConstantComposite %3 %11 %10
%13 = OpVariable %8 Uniform
%14 = OpFunction %5 None %9
%15 = OpLabel
%16 = OpCompositeConstruct %4 %12 %12
      OpStore %13 %16
      OpReturn
      OpFunctionEnd)";
  spvValidatorOptionsSetRelaxStoreStruct(options_, true);
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message(
          "OpStore Pointer <id> '13[%13]'s layout does not match Object "
          "<id> '16[%16]'s layout.")));
}

TEST_P(ValidateIdWithMessage, OpStoreTypeRelaxedLogicalPointerReturnPointer) {
  const std::string spirv = R"(
     OpCapability Shader
     OpCapability Linkage
     OpMemoryModel Logical GLSL450
%1 = OpTypeInt 32 1
%2 = OpTypePointer Function %1
%3 = OpTypeFunction %2 %2
%4 = OpFunction %2 None %3
%5 = OpFunctionParameter %2
%6 = OpLabel
     OpReturnValue %5
     OpFunctionEnd)";

  spvValidatorOptionsSetRelaxLogicalPointer(options_, true);
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpStoreTypeRelaxedLogicalPointerAllocPointer) {
  const std::string spirv = R"(
      OpCapability Shader
      OpCapability Linkage
      OpMemoryModel Logical GLSL450
 %1 = OpTypeVoid
 %2 = OpTypeInt 32 1
 %3 = OpTypeFunction %1          ; void(void)
 %4 = OpTypePointer Uniform %2   ; int*
 %5 = OpTypePointer Private %4   ; int** (Private)
 %6 = OpTypePointer Function %4  ; int** (Function)
 %7 = OpVariable %5 Private
 %8 = OpFunction %1 None %3
 %9 = OpLabel
%10 = OpVariable %6 Function
      OpReturn
      OpFunctionEnd)";

  spvValidatorOptionsSetRelaxLogicalPointer(options_, true);
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpStoreVoid) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Uniform %2
%4 = OpTypeFunction %1
%6 = OpVariable %3 Uniform
%7 = OpFunction %1 None %4
%8 = OpLabel
%9 = OpFunctionCall %1 %7
     OpStore %6 %9
     OpReturn
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("OpStore Object <id> '8[%8]'s type is void.")));
}

TEST_P(ValidateIdWithMessage, OpStoreLabel) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Uniform %2
%4 = OpTypeFunction %1
%6 = OpVariable %3 Uniform
%7 = OpFunction %1 None %4
%8 = OpLabel
     OpStore %6 %8
     OpReturn
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message("Operand '7[%7]' requires a type")));
}

// TODO: enable when this bug is fixed:
// https://cvs.khronos.org/bugzilla/show_bug.cgi?id=15404
TEST_P(ValidateIdWithMessage, DISABLED_OpStoreFunction) {
  std::string spirv = kGLSL450MemoryModel + R"(
%2 = OpTypeInt 32 0
%3 = OpTypePointer UniformConstant %2
%4 = OpTypeFunction %2
%5 = OpConstant %2 123
%6 = OpVariable %3 UniformConstant
%7 = OpFunction %2 None %4
%8 = OpLabel
     OpStore %6 %7
     OpReturnValue %5
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpStoreBuiltin) {
  std::string spirv = R"(
               OpCapability Shader
          %1 = OpExtInstImport "GLSL.std.450"
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
               OpExecutionMode %main LocalSize 1 1 1
               OpSource GLSL 450
               OpName %main "main"

               OpName %gl_GlobalInvocationID "gl_GlobalInvocationID"
               OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId

        %int = OpTypeInt 32 1
       %uint = OpTypeInt 32 0
     %v3uint = OpTypeVector %uint 3
%_ptr_Input_v3uint = OpTypePointer Input %v3uint
%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input

       %zero = OpConstant %uint 0
 %v3uint_000 = OpConstantComposite %v3uint %zero %zero %zero

       %void = OpTypeVoid
   %voidfunc = OpTypeFunction %void
       %main = OpFunction %void None %voidfunc
      %lmain = OpLabel

               OpStore %gl_GlobalInvocationID %v3uint_000

               OpReturn
               OpFunctionEnd
  )";

  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message("storage class is read-only")));
}

TEST_P(ValidateIdWithMessage, OpCopyMemoryGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
 %1 = OpTypeVoid
 %2 = OpTypeInt 32 0
 %3 = OpTypePointer UniformConstant %2
 %4 = OpConstant %2 42
 %5 = OpVariable %3 UniformConstant %4
 %6 = OpTypePointer Function %2
 %7 = OpTypeFunction %1
 %8 = OpFunction %1 None %7
 %9 = OpLabel
%10 = OpVariable %6 Function
      OpCopyMemory %10 %5 None
      OpReturn
      OpFunctionEnd
)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpCopyMemoryNonPointerTarget) {
  const std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Uniform %2
%4 = OpTypeFunction %1 %2 %3
%5 = OpFunction %1 None %4
%6 = OpFunctionParameter %2
%7 = OpFunctionParameter %3
%8 = OpLabel
OpCopyMemory %6 %7
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("Target operand <id> '6[%6]' is not a pointer.")));
}

TEST_P(ValidateIdWithMessage, OpCopyMemoryNonPointerSource) {
  const std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Uniform %2
%4 = OpTypeFunction %1 %2 %3
%5 = OpFunction %1 None %4
%6 = OpFunctionParameter %2
%7 = OpFunctionParameter %3
%8 = OpLabel
OpCopyMemory %7 %6
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("Source operand <id> '6[%6]' is not a pointer.")));
}

TEST_P(ValidateIdWithMessage, OpCopyMemoryBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
 %1 = OpTypeVoid
 %2 = OpTypeInt 32 0
 %3 = OpTypePointer UniformConstant %2
 %4 = OpConstant %2 42
 %5 = OpVariable %3 UniformConstant %4
%11 = OpTypeFloat 32
 %6 = OpTypePointer Function %11
 %7 = OpTypeFunction %1
 %8 = OpFunction %1 None %7
 %9 = OpLabel
%10 = OpVariable %6 Function
      OpCopyMemory %10 %5 None
      OpReturn
      OpFunctionEnd
)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message("Target <id> '5[%5]'s type does not match "
                                     "Source <id> '2[%uint]'s type.")));
}

TEST_P(ValidateIdWithMessage, OpCopyMemoryVoidTarget) {
  const std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Uniform %1
%4 = OpTypePointer Uniform %2
%5 = OpTypeFunction %1 %3 %4
%6 = OpFunction %1 None %5
%7 = OpFunctionParameter %3
%8 = OpFunctionParameter %4
%9 = OpLabel
OpCopyMemory %7 %8
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("Target operand <id> '7[%7]' cannot be a void "
                             "pointer.")));
}

TEST_P(ValidateIdWithMessage, OpCopyMemoryVoidSource) {
  const std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Uniform %1
%4 = OpTypePointer Uniform %2
%5 = OpTypeFunction %1 %3 %4
%6 = OpFunction %1 None %5
%7 = OpFunctionParameter %3
%8 = OpFunctionParameter %4
%9 = OpLabel
OpCopyMemory %8 %7
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("Source operand <id> '7[%7]' cannot be a void "
                             "pointer.")));
}

TEST_P(ValidateIdWithMessage, OpCopyMemorySizedGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
 %1 = OpTypeVoid
 %2 = OpTypeInt 32 0
 %3 = OpTypePointer UniformConstant %2
 %4 = OpTypePointer Function %2
 %5 = OpConstant %2 4
 %6 = OpVariable %3 UniformConstant %5
 %7 = OpTypeFunction %1
 %8 = OpFunction %1 None %7
 %9 = OpLabel
%10 = OpVariable %4 Function
      OpCopyMemorySized %10 %6 %5 None
      OpReturn
      OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpCopyMemorySizedTargetBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer UniformConstant %2
%4 = OpTypePointer Function %2
%5 = OpConstant %2 4
%6 = OpVariable %3 UniformConstant %5
%7 = OpTypeFunction %1
%8 = OpFunction %1 None %7
%9 = OpLabel
     OpCopyMemorySized %5 %5 %5 None
     OpReturn
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "Target operand <id> '5[%uint_4]' is not a pointer.")));
}
TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSourceBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer UniformConstant %2
%4 = OpTypePointer Function %2
%5 = OpConstant %2 4
%6 = OpTypeFunction %1
%7 = OpFunction %1 None %6
%8 = OpLabel
%9 = OpVariable %4 Function
     OpCopyMemorySized %9 %5 %5 None
     OpReturn
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "Source operand <id> '5[%uint_4]' is not a pointer.")));
}
TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
 %1 = OpTypeVoid
 %2 = OpTypeInt 32 0
 %3 = OpTypePointer UniformConstant %2
 %4 = OpTypePointer Function %2
 %5 = OpConstant %2 4
 %6 = OpVariable %3 UniformConstant %5
 %7 = OpTypeFunction %1
 %8 = OpFunction %1 None %7
 %9 = OpLabel
%10 = OpVariable %4 Function
      OpCopyMemorySized %10 %6 %6 None
      OpReturn
      OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "Size operand <id> '6[%6]' must be a scalar integer type.")));
}
TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
 %1 = OpTypeVoid
 %2 = OpTypeInt 32 0
 %3 = OpTypePointer UniformConstant %2
 %4 = OpTypePointer Function %2
 %5 = OpConstant %2 4
 %6 = OpVariable %3 UniformConstant %5
 %7 = OpTypeFunction %1
%11 = OpTypeFloat 32
%12 = OpConstant %11 1.0
 %8 = OpFunction %1 None %7
 %9 = OpLabel
%10 = OpVariable %4 Function
      OpCopyMemorySized %10 %6 %12 None
      OpReturn
      OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "Size operand <id> '9[%float_1]' must be a scalar integer "
                  "type.")));
}

TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantNull) {
  const std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpConstantNull %2
%4 = OpTypePointer Uniform %2
%5 = OpTypeFloat 32
%6 = OpTypePointer UniformConstant %5
%7 = OpTypeFunction %1 %4 %6
%8 = OpFunction %1 None %7
%9 = OpFunctionParameter %4
%10 = OpFunctionParameter %6
%11 = OpLabel
OpCopyMemorySized %9 %10 %3
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("Size operand <id> '3[%3]' cannot be a constant "
                             "zero.")));
}

TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantZero) {
  const std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpConstant %2 0
%4 = OpTypePointer Uniform %2
%5 = OpTypeFloat 32
%6 = OpTypePointer UniformConstant %5
%7 = OpTypeFunction %1 %4 %6
%8 = OpFunction %1 None %7
%9 = OpFunctionParameter %4
%10 = OpFunctionParameter %6
%11 = OpLabel
OpCopyMemorySized %9 %10 %3
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "Size operand <id> '3[%uint_0]' cannot be a constant "
                  "zero.")));
}

TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantZero64) {
  const std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 64 0
%3 = OpConstant %2 0
%4 = OpTypePointer Uniform %2
%5 = OpTypeFloat 32
%6 = OpTypePointer UniformConstant %5
%7 = OpTypeFunction %1 %4 %6
%8 = OpFunction %1 None %7
%9 = OpFunctionParameter %4
%10 = OpFunctionParameter %6
%11 = OpLabel
OpCopyMemorySized %9 %10 %3
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "Size operand <id> '3[%ulong_0]' cannot be a constant "
                  "zero.")));
}

TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantNegative) {
  const std::string spirv = kNoKernelGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 1
%3 = OpConstant %2 -1
%4 = OpTypePointer Uniform %2
%5 = OpTypeFloat 32
%6 = OpTypePointer UniformConstant %5
%7 = OpTypeFunction %1 %4 %6
%8 = OpFunction %1 None %7
%9 = OpFunctionParameter %4
%10 = OpFunctionParameter %6
%11 = OpLabel
OpCopyMemorySized %9 %10 %3
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "Size operand <id> '3[%int_n1]' cannot have the sign bit set "
                  "to 1.")));
}

TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantNegative64) {
  const std::string spirv = kNoKernelGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 64 1
%3 = OpConstant %2 -1
%4 = OpTypePointer Uniform %2
%5 = OpTypeFloat 32
%6 = OpTypePointer UniformConstant %5
%7 = OpTypeFunction %1 %4 %6
%8 = OpFunction %1 None %7
%9 = OpFunctionParameter %4
%10 = OpFunctionParameter %6
%11 = OpLabel
OpCopyMemorySized %9 %10 %3
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message(
          "Size operand <id> '3[%long_n1]' cannot have the sign bit set "
          "to 1.")));
}

TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeUnsignedNegative) {
  const std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpConstant %2 2147483648
%4 = OpTypePointer Uniform %2
%5 = OpTypeFloat 32
%6 = OpTypePointer UniformConstant %5
%7 = OpTypeFunction %1 %4 %6
%8 = OpFunction %1 None %7
%9 = OpFunctionParameter %4
%10 = OpFunctionParameter %6
%11 = OpLabel
OpCopyMemorySized %9 %10 %3
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpCopyMemorySizedSizeUnsignedNegative64) {
  const std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 64 0
%3 = OpConstant %2 9223372036854775808
%4 = OpTypePointer Uniform %2
%5 = OpTypeFloat 32
%6 = OpTypePointer UniformConstant %5
%7 = OpTypeFunction %1 %4 %6
%8 = OpFunction %1 None %7
%9 = OpFunctionParameter %4
%10 = OpFunctionParameter %6
%11 = OpLabel
OpCopyMemorySized %9 %10 %3
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

const char kDeeplyNestedStructureSetup[] = R"(
%void = OpTypeVoid
%void_f  = OpTypeFunction %void
%int = OpTypeInt 32 0
%float = OpTypeFloat 32
%v3float = OpTypeVector %float 3
%mat4x3 = OpTypeMatrix %v3float 4
%_ptr_Private_mat4x3 = OpTypePointer Private %mat4x3
%_ptr_Private_float = OpTypePointer Private %float
%my_matrix = OpVariable %_ptr_Private_mat4x3 Private
%my_float_var = OpVariable %_ptr_Private_float Private
%_ptr_Function_float = OpTypePointer Function %float
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%int_2 = OpConstant %int 2
%int_3 = OpConstant %int 3
%int_5 = OpConstant %int 5

; Making the following nested structures.
;
; struct S {
;   bool b;
;   vec4 v[5];
;   int i;
;   mat4x3 m[5];
; }
; uniform blockName {
;   S s;
;   bool cond;
;   RunTimeArray arr;
; }

%f32arr = OpTypeRuntimeArray %float
%v4float = OpTypeVector %float 4
%array5_mat4x3 = OpTypeArray %mat4x3 %int_5
%array5_vec4 = OpTypeArray %v4float %int_5
%_ptr_Uniform_float = OpTypePointer Uniform %float
%_ptr_Function_vec4 = OpTypePointer Function %v4float
%_ptr_Uniform_vec4 = OpTypePointer Uniform %v4float
%struct_s = OpTypeStruct %int %array5_vec4 %int %array5_mat4x3
%struct_blockName = OpTypeStruct %struct_s %int %f32arr
%_ptr_Uniform_blockName = OpTypePointer Uniform %struct_blockName
%_ptr_Uniform_struct_s = OpTypePointer Uniform %struct_s
%_ptr_Uniform_array5_mat4x3 = OpTypePointer Uniform %array5_mat4x3
%_ptr_Uniform_mat4x3 = OpTypePointer Uniform %mat4x3
%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float
%blockName_var = OpVariable %_ptr_Uniform_blockName Uniform
%spec_int = OpSpecConstant %int 2
%float_0 = OpConstant %float 0
%func = OpFunction %void None %void_f
%my_label = OpLabel
)";

// In what follows, Access Chain Instruction refers to one of the following:
// OpAccessChain, OpInBoundsAccessChain, OpPtrAccessChain, and
// OpInBoundsPtrAccessChain
using AccessChainInstructionTest = spvtest::ValidateBase<std::string>;

// Determines whether the access chain instruction requires the 'element id'
// argument.
bool AccessChainRequiresElemId(const std::string& instr) {
  return (instr == "OpPtrAccessChain" || instr == "OpInBoundsPtrAccessChain");
}

// Valid: Access a float in a matrix using an access chain instruction.
TEST_P(AccessChainInstructionTest, AccessChainGood) {
  const std::string instr = GetParam();
  const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
  std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup +
                      "%float_entry = " + instr +
                      R"( %_ptr_Private_float %my_matrix )" + elem +
                      R"(%int_0 %int_1
              OpReturn
              OpFunctionEnd
          )";
  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

// Invalid. The result type of an access chain instruction must be a pointer.
TEST_P(AccessChainInstructionTest, AccessChainResultTypeBad) {
  const std::string instr = GetParam();
  const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
  std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%float_entry = )" +
                      instr +
                      R"( %float %my_matrix )" + elem +
                      R"(%int_0 %int_1
OpReturn
OpFunctionEnd
  )";

  const std::string expected_err = "The Result Type of " + instr +
                                   " <id> '36[%36]' must be "
                                   "OpTypePointer. Found OpTypeFloat.";
  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
}

// Invalid. The base type of an access chain instruction must be a pointer.
TEST_P(AccessChainInstructionTest, AccessChainBaseTypeVoidBad) {
  const std::string instr = GetParam();
  const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
  std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%float_entry = )" +
                      instr + " %_ptr_Private_float %void " + elem +
                      R"(%int_0 %int_1
OpReturn
OpFunctionEnd
  )";
  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand '1[%void]' cannot be a "
                                               "type"));
}

// Invalid. The base type of an access chain instruction must be a pointer.
TEST_P(AccessChainInstructionTest, AccessChainBaseTypeNonPtrVariableBad) {
  const std::string instr = GetParam();
  const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
  std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%entry = )" +
                      instr + R"( %_ptr_Private_float %_ptr_Private_float )" +
                      elem +
                      R"(%int_0 %int_1
OpReturn
OpFunctionEnd
  )";
  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr("Operand '8[%_ptr_Private_float]' cannot be a type"));
}

// Invalid: The storage class of Base and Result do not match.
TEST_P(AccessChainInstructionTest,
       AccessChainResultAndBaseStorageClassDoesntMatchBad) {
  const std::string instr = GetParam();
  const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
  std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%entry = )" +
                      instr + R"( %_ptr_Function_float %my_matrix )" + elem +
                      R"(%int_0 %int_1
OpReturn
OpFunctionEnd
  )";
  const std::string expected_err =
      "The result pointer storage class and base pointer storage class in " +
      instr + " do not match.";
  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
}

// Invalid. The base type of an access chain instruction must point to a
// composite object.
TEST_P(AccessChainInstructionTest,
       AccessChainBasePtrNotPointingToCompositeBad) {
  const std::string instr = GetParam();
  const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
  std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%entry = )" +
                      instr + R"( %_ptr_Private_float %my_float_var )" + elem +
                      R"(%int_0
OpReturn
OpFunctionEnd
  )";
  const std::string expected_err = instr +
                                   " reached non-composite type while "
                                   "indexes still remain to be traversed.";
  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
}

// Valid. No Indexes were passed to the access chain instruction. The Result
// Type is the same as the Base type.
TEST_P(AccessChainInstructionTest, AccessChainNoIndexesGood) {
  const std::string instr = GetParam();
  const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
  std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%entry = )" +
                      instr + R"( %_ptr_Private_float %my_float_var )" + elem +
                      R"(
OpReturn
OpFunctionEnd
  )";
  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

// Invalid. No Indexes were passed to the access chain instruction, but the
// Result Type is different from the Base type.
TEST_P(AccessChainInstructionTest, AccessChainNoIndexesBad) {
  const std::string instr = GetParam();
  const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
  std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%entry = )" +
                      instr + R"( %_ptr_Private_mat4x3 %my_float_var )" + elem +
                      R"(
OpReturn
OpFunctionEnd
  )";
  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr("result type (OpTypeMatrix) does not match the type that "
                "results from indexing into the base <id> (OpTypeFloat)."));
}

// Valid: 255 indexes passed to the access chain instruction. Limit is 255.
TEST_P(AccessChainInstructionTest, AccessChainTooManyIndexesGood) {
  const std::string instr = GetParam();
  const std::string elem = AccessChainRequiresElemId(instr) ? " %int_0 " : "";
  const std::string arrayStride =
      " OpDecorate %_ptr_Uniform_deep_struct ArrayStride 8 ";
  int depth = 255;
  std::string header =
      kGLSL450MemoryModel + arrayStride + kDeeplyNestedStructureSetup;
  header.erase(header.find("%func"));
  std::ostringstream spirv;
  spirv << header << "\n";

  // Build nested structures. Struct 'i' contains struct 'i-1'
  spirv << "%s_depth_1 = OpTypeStruct %float\n";
  for (int i = 2; i <= depth; ++i) {
    spirv << "%s_depth_" << i << " = OpTypeStruct %s_depth_" << i - 1 << "\n";
  }

  // Define Pointer and Variable to use for the AccessChain instruction.
  spirv << "%_ptr_Uniform_deep_struct = OpTypePointer Uniform %s_depth_"
        << depth << "\n";
  spirv << "%deep_var = OpVariable %_ptr_Uniform_deep_struct Uniform\n";

  // Function Start
  spirv << R"(
  %func = OpFunction %void None %void_f
  %my_label = OpLabel
  )";

  // AccessChain with 'n' indexes (n = depth)
  spirv << "%entry = " << instr << " %_ptr_Uniform_float %deep_var" << elem;
  for (int i = 0; i < depth; ++i) {
    spirv << " %int_0";
  }

  // Function end
  spirv << R"(
    OpReturn
    OpFunctionEnd
  )";
  CompileSuccessfully(spirv.str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

// Invalid: 256 indexes passed to the access chain instruction. Limit is 255.
TEST_P(AccessChainInstructionTest, AccessChainTooManyIndexesBad) {
  const std::string instr = GetParam();
  const std::string elem = AccessChainRequiresElemId(instr) ? " %int_0 " : "";
  std::ostringstream spirv;
  spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup;
  spirv << "%entry = " << instr << " %_ptr_Private_float %my_matrix" << elem;
  for (int i = 0; i < 256; ++i) {
    spirv << " %int_0";
  }
  spirv << R"(
    OpReturn
    OpFunctionEnd
  )";
  const std::string expected_err = "The number of indexes in " + instr +
                                   " may not exceed 255. Found 256 indexes.";
  CompileSuccessfully(spirv.str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
}

// Valid: 10 indexes passed to the access chain instruction. (Custom limit: 10)
TEST_P(AccessChainInstructionTest, CustomizedAccessChainTooManyIndexesGood) {
  const std::string instr = GetParam();
  const std::string elem = AccessChainRequiresElemId(instr) ? " %int_0 " : "";
  const std::string arrayStride =
      " OpDecorate %_ptr_Uniform_deep_struct ArrayStride 8 ";
  int depth = 10;
  std::string header =
      kGLSL450MemoryModel + arrayStride + kDeeplyNestedStructureSetup;
  header.erase(header.find("%func"));
  std::ostringstream spirv;
  spirv << header << "\n";

  // Build nested structures. Struct 'i' contains struct 'i-1'
  spirv << "%s_depth_1 = OpTypeStruct %float\n";
  for (int i = 2; i <= depth; ++i) {
    spirv << "%s_depth_" << i << " = OpTypeStruct %s_depth_" << i - 1 << "\n";
  }

  // Define Pointer and Variable to use for the AccessChain instruction.
  spirv << "%_ptr_Uniform_deep_struct = OpTypePointer Uniform %s_depth_"
        << depth << "\n";
  spirv << "%deep_var = OpVariable %_ptr_Uniform_deep_struct Uniform\n";

  // Function Start
  spirv << R"(
  %func = OpFunction %void None %void_f
  %my_label = OpLabel
  )";

  // AccessChain with 'n' indexes (n = depth)
  spirv << "%entry = " << instr << " %_ptr_Uniform_float %deep_var" << elem;
  for (int i = 0; i < depth; ++i) {
    spirv << " %int_0";
  }

  // Function end
  spirv << R"(
    OpReturn
    OpFunctionEnd
  )";

  spvValidatorOptionsSetUniversalLimit(
      options_, spv_validator_limit_max_access_chain_indexes, 10u);
  CompileSuccessfully(spirv.str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

// Invalid: 11 indexes passed to the access chain instruction. Custom Limit:10
TEST_P(AccessChainInstructionTest, CustomizedAccessChainTooManyIndexesBad) {
  const std::string instr = GetParam();
  const std::string elem = AccessChainRequiresElemId(instr) ? " %int_0 " : "";
  std::ostringstream spirv;
  spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup;
  spirv << "%entry = " << instr << " %_ptr_Private_float %my_matrix" << elem;
  for (int i = 0; i < 11; ++i) {
    spirv << " %int_0";
  }
  spirv << R"(
    OpReturn
    OpFunctionEnd
  )";
  const std::string expected_err = "The number of indexes in " + instr +
                                   " may not exceed 10. Found 11 indexes.";
  spvValidatorOptionsSetUniversalLimit(
      options_, spv_validator_limit_max_access_chain_indexes, 10u);
  CompileSuccessfully(spirv.str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
}

// Invalid: Index passed to the access chain instruction is float (must be
// integer).
TEST_P(AccessChainInstructionTest, AccessChainUndefinedIndexBad) {
  const std::string instr = GetParam();
  const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
  std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%entry = )" +
                      instr + R"( %_ptr_Private_float %my_matrix )" + elem +
                      R"(%float_0 %int_1
OpReturn
OpFunctionEnd
  )";
  const std::string expected_err =
      "Indexes passed to " + instr + " must be of type integer.";
  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
}

// Invalid: The index argument that indexes into a struct must be of type
// OpConstant.
TEST_P(AccessChainInstructionTest, AccessChainStructIndexNotConstantBad) {
  const std::string instr = GetParam();
  const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
  std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%f = )" +
                      instr + R"( %_ptr_Uniform_float %blockName_var )" + elem +
                      R"(%int_0 %spec_int %int_2
OpReturn
OpFunctionEnd
  )";
  const std::string expected_err =
      "The <id> passed to " + instr +
      " to index into a structure must be an OpConstant.";
  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
}

// Invalid: Indexing up to a vec4 granularity, but result type expected float.
TEST_P(AccessChainInstructionTest,
       AccessChainStructResultTypeDoesntMatchIndexedTypeBad) {
  const std::string instr = GetParam();
  const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
  std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%entry = )" +
                      instr + R"( %_ptr_Uniform_float %blockName_var )" + elem +
                      R"(%int_0 %int_1 %int_2
OpReturn
OpFunctionEnd
  )";
  const std::string expected_err = instr +
                                   " result type (OpTypeFloat) does not match "
                                   "the type that results from indexing into "
                                   "the base <id> (OpTypeVector).";
  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
}

// Invalid: Reach non-composite type (bool) when unused indexes remain.
TEST_P(AccessChainInstructionTest, AccessChainStructTooManyIndexesBad) {
  const std::string instr = GetParam();
  const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
  std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%entry = )" +
                      instr + R"( %_ptr_Uniform_float %blockName_var )" + elem +
                      R"(%int_0 %int_2 %int_2
OpReturn
OpFunctionEnd
  )";
  const std::string expected_err = instr +
                                   " reached non-composite type while "
                                   "indexes still remain to be traversed.";
  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
}

// Invalid: Trying to find index 3 of the struct that has only 3 members.
TEST_P(AccessChainInstructionTest, AccessChainStructIndexOutOfBoundBad) {
  const std::string instr = GetParam();
  const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
  std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%entry = )" +
                      instr + R"( %_ptr_Uniform_float %blockName_var )" + elem +
                      R"(%int_3 %int_2 %int_2
OpReturn
OpFunctionEnd
  )";
  const std::string expected_err = "Index is out of bounds: " + instr +
                                   " can not find index 3 into the structure "
                                   "<id> '25[%_struct_25]'. This structure "
                                   "has 3 members. Largest valid index is 2.";
  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
}

// Valid: Tests that we can index into Struct, Array, Matrix, and Vector!
TEST_P(AccessChainInstructionTest, AccessChainIndexIntoAllTypesGood) {
  // indexes that we are passing are: 0, 3, 1, 2, 0
  // 0 will select the struct_s within the base struct (blockName)
  // 3 will select the Array that contains 5 matrices
  // 1 will select the Matrix that is at index 1 of the array
  // 2 will select the column (which is a vector) within the matrix at index 2
  // 0 will select the element at the index 0 of the vector. (which is a float).
  const std::string instr = GetParam();
  const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
  const std::string arrayStride =
      " OpDecorate %_ptr_Uniform_blockName ArrayStride 8 ";
  std::ostringstream spirv;
  spirv << kGLSL450MemoryModel << arrayStride << kDeeplyNestedStructureSetup
        << std::endl;
  spirv << "%ss = " << instr << " %_ptr_Uniform_struct_s %blockName_var "
        << elem << "%int_0" << std::endl;
  spirv << "%sa = " << instr << " %_ptr_Uniform_array5_mat4x3 %blockName_var "
        << elem << "%int_0 %int_3" << std::endl;
  spirv << "%sm = " << instr << " %_ptr_Uniform_mat4x3 %blockName_var " << elem
        << "%int_0 %int_3 %int_1" << std::endl;
  spirv << "%sc = " << instr << " %_ptr_Uniform_v3float %blockName_var " << elem
        << "%int_0 %int_3 %int_1 %int_2" << std::endl;
  spirv << "%entry = " << instr << " %_ptr_Uniform_float %blockName_var "
        << elem << "%int_0 %int_3 %int_1 %int_2 %int_0" << std::endl;
  spirv << R"(
OpReturn
OpFunctionEnd
  )";
  CompileSuccessfully(spirv.str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

// Valid: Access an element of OpTypeRuntimeArray.
TEST_P(AccessChainInstructionTest, AccessChainIndexIntoRuntimeArrayGood) {
  const std::string instr = GetParam();
  const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
  const std::string arrayStride =
      " OpDecorate %_ptr_Uniform_blockName ArrayStride 8 ";
  std::string spirv = kGLSL450MemoryModel + arrayStride +
                      kDeeplyNestedStructureSetup + R"(
%runtime_arr_entry = )" + instr +
                      R"( %_ptr_Uniform_float %blockName_var )" + elem +
                      R"(%int_2 %int_0
OpReturn
OpFunctionEnd
  )";
  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

// Invalid: Unused index when accessing OpTypeRuntimeArray.
TEST_P(AccessChainInstructionTest, AccessChainIndexIntoRuntimeArrayBad) {
  const std::string instr = GetParam();
  const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
  std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%runtime_arr_entry = )" +
                      instr + R"( %_ptr_Uniform_float %blockName_var )" + elem +
                      R"(%int_2 %int_0 %int_1
OpReturn
OpFunctionEnd
  )";
  const std::string expected_err =
      instr +
      " reached non-composite type while indexes still remain to be traversed.";
  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
}

// Invalid: Reached scalar type before arguments to the access chain instruction
// finished.
TEST_P(AccessChainInstructionTest, AccessChainMatrixMoreArgsThanNeededBad) {
  const std::string instr = GetParam();
  const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
  std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%entry = )" +
                      instr + R"( %_ptr_Private_float %my_matrix )" + elem +
                      R"(%int_0 %int_1 %int_0
OpReturn
OpFunctionEnd
  )";
  const std::string expected_err = instr +
                                   " reached non-composite type while "
                                   "indexes still remain to be traversed.";
  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
}

// Invalid: The result type and the type indexed into do not match.
TEST_P(AccessChainInstructionTest,
       AccessChainResultTypeDoesntMatchIndexedTypeBad) {
  const std::string instr = GetParam();
  const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
  std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%entry = )" +
                      instr + R"( %_ptr_Private_mat4x3 %my_matrix )" + elem +
                      R"(%int_0 %int_1
OpReturn
OpFunctionEnd
  )";
  const std::string expected_err = instr +
                                   " result type (OpTypeMatrix) does not match "
                                   "the type that results from indexing into "
                                   "the base <id> (OpTypeFloat).";
  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err));
}

// Run tests for Access Chain Instructions.
INSTANTIATE_TEST_SUITE_P(
    CheckAccessChainInstructions, AccessChainInstructionTest,
    ::testing::Values("OpAccessChain", "OpInBoundsAccessChain",
                      "OpPtrAccessChain", "OpInBoundsPtrAccessChain"));

// TODO: OpArrayLength
// TODO: OpImagePointer
// TODO: OpGenericPtrMemSemantics

TEST_P(ValidateIdWithMessage, OpFunctionGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypeFunction %1 %2 %2
%4 = OpFunction %1 None %3
%5 = OpLabel
     OpReturn
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpFunctionResultTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpConstant %2 42
%4 = OpTypeFunction %1 %2 %2
%5 = OpFunction %2 None %4
%6 = OpLabel
     OpReturnValue %3
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("OpFunction Result Type <id> '2[%uint]' does not "
                             "match the Function Type's return type <id> "
                             "'1[%void]'.")));
}
TEST_P(ValidateIdWithMessage, OpReturnValueTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeInt 32 0
%2 = OpTypeFloat 32
%3 = OpConstant %2 0
%4 = OpTypeFunction %1
%5 = OpFunction %1 None %4
%6 = OpLabel
     OpReturnValue %3
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpReturnValue Value <id> '3[%float_0]'s type does "
                  "not match OpFunction's return type.")));
}
TEST_P(ValidateIdWithMessage, OpFunctionFunctionTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%4 = OpFunction %1 None %2
%5 = OpLabel
     OpReturn
OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpFunction Function Type <id> '2[%uint]' is not a function "
                  "type.")));
}

TEST_P(ValidateIdWithMessage, OpFunctionUseBad) {
  const std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeFloat 32
%2 = OpTypeFunction %1
%3 = OpFunction %1 None %2
%4 = OpLabel
OpReturnValue %3
OpFunctionEnd
)";

  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("Invalid use of function result id '3[%3]'.")));
}

TEST_P(ValidateIdWithMessage, OpFunctionParameterGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypeFunction %1 %2
%4 = OpFunction %1 None %3
%5 = OpFunctionParameter %2
%6 = OpLabel
     OpReturn
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpFunctionParameterMultipleGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypeFunction %1 %2 %2
%4 = OpFunction %1 None %3
%5 = OpFunctionParameter %2
%6 = OpFunctionParameter %2
%7 = OpLabel
     OpReturn
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpFunctionParameterResultTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypeFunction %1 %2
%4 = OpFunction %1 None %3
%5 = OpFunctionParameter %1
%6 = OpLabel
     OpReturn
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message(
          "OpFunctionParameter Result Type <id> '1[%void]' does not "
          "match the OpTypeFunction parameter type of the same index.")));
}

TEST_P(ValidateIdWithMessage, OpFunctionCallGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypeFunction %2 %2
%4 = OpTypeFunction %1
%5 = OpConstant %2 42 ;21

%6 = OpFunction %2 None %3
%7 = OpFunctionParameter %2
%8 = OpLabel
     OpReturnValue %7
     OpFunctionEnd

%10 = OpFunction %1 None %4
%11 = OpLabel
%12 = OpFunctionCall %2 %6 %5
      OpReturn
      OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpFunctionCallResultTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypeFunction %2 %2
%4 = OpTypeFunction %1
%5 = OpConstant %2 42 ;21

%6 = OpFunction %2 None %3
%7 = OpFunctionParameter %2
%8 = OpLabel
%9 = OpIAdd %2 %7 %7
     OpReturnValue %9
     OpFunctionEnd

%10 = OpFunction %1 None %4
%11 = OpLabel
%12 = OpFunctionCall %1 %6 %5
      OpReturn
      OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("OpFunctionCall Result Type <id> '1[%void]'s type "
                             "does not match Function <id> '2[%uint]'s return "
                             "type.")));
}
TEST_P(ValidateIdWithMessage, OpFunctionCallFunctionBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypeFunction %2 %2
%4 = OpTypeFunction %1
%5 = OpConstant %2 42 ;21

%10 = OpFunction %1 None %4
%11 = OpLabel
%12 = OpFunctionCall %2 %5 %5
      OpReturn
      OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpFunctionCall Function <id> '5[%uint_42]' is not a "
                  "function.")));
}
TEST_P(ValidateIdWithMessage, OpFunctionCallArgumentTypeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypeFunction %2 %2
%4 = OpTypeFunction %1
%5 = OpConstant %2 42

%13 = OpTypeFloat 32
%14 = OpConstant %13 3.14

%6 = OpFunction %2 None %3
%7 = OpFunctionParameter %2
%8 = OpLabel
%9 = OpIAdd %2 %7 %7
     OpReturnValue %9
     OpFunctionEnd

%10 = OpFunction %1 None %4
%11 = OpLabel
%12 = OpFunctionCall %2 %6 %14
      OpReturn
      OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpFunctionCall Argument <id> '7[%float_3_1400001]'s "
                  "type does not match Function <id> '2[%uint]'s "
                  "parameter type.")));
}

// Valid: OpSampledImage result <id> is used in the same block by
// OpImageSampleImplictLod
TEST_P(ValidateIdWithMessage, OpSampledImageGood) {
  std::string spirv = kGLSL450MemoryModel + sampledImageSetup + R"(
%smpld_img = OpSampledImage %sampled_image_type %image_inst %sampler_inst
%si_lod    = OpImageSampleImplicitLod %v4float %smpld_img %const_vec_1_1
    OpReturn
    OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

// Invalid: OpSampledImage result <id> is defined in one block and used in a
// different block.
TEST_P(ValidateIdWithMessage, OpSampledImageUsedInDifferentBlockBad) {
  std::string spirv = kGLSL450MemoryModel + sampledImageSetup + R"(
%smpld_img = OpSampledImage %sampled_image_type %image_inst %sampler_inst
OpBranch %label_2
%label_2 = OpLabel
%si_lod  = OpImageSampleImplicitLod %v4float %smpld_img %const_vec_1_1
OpReturn
OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message(
          "All OpSampledImage instructions must be in the same block in "
          "which their Result <id> are consumed. OpSampledImage Result "
          "Type <id> '23[%23]' has a consumer in a different basic "
          "block. The consumer instruction <id> is '25[%25]'.")));
}

// Invalid: OpSampledImage result <id> is used by OpSelect
// Note: According to the Spec, OpSelect parameters must be either a scalar or a
// vector. Therefore, OpTypeSampledImage is an illegal parameter for OpSelect.
// However, the OpSelect validation does not catch this today. Therefore, it is
// caught by the OpSampledImage validation. If the OpSelect validation code is
// updated, the error message for this test may change.
//
// Disabled since OpSelect catches this now.
TEST_P(ValidateIdWithMessage, DISABLED_OpSampledImageUsedInOpSelectBad) {
  std::string spirv = kGLSL450MemoryModel + sampledImageSetup + R"(
%smpld_img  = OpSampledImage %sampled_image_type %image_inst %sampler_inst
%select_img = OpSelect %sampled_image_type %spec_true %smpld_img %smpld_img
OpReturn
OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "Result <id> from OpSampledImage instruction must not "
                  "appear as operands of OpSelect. Found result <id> "
                  "'23' as an operand of <id> '24'.")));
}

TEST_P(ValidateIdWithMessage, OpCopyObjectSampledImageGood) {
  std::string spirv = kGLSL450MemoryModel + sampledImageSetup + R"(
%smpld_img  = OpSampledImage %sampled_image_type %image_inst %sampler_inst
%smpld_img2 = OpCopyObject %sampled_image_type %smpld_img
%image_inst2 = OpCopyObject %image_type %image_inst
OpReturn
OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

// Valid: Get a float in a matrix using CompositeExtract.
// Valid: Insert float into a matrix using CompositeInsert.
TEST_P(ValidateIdWithMessage, CompositeExtractInsertGood) {
  std::ostringstream spirv;
  spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup << std::endl;
  spirv << "%matrix = OpLoad %mat4x3 %my_matrix" << std::endl;
  spirv << "%float_entry = OpCompositeExtract  %float %matrix 0 1" << std::endl;

  // To test CompositeInsert, insert the object back in after extraction.
  spirv << "%new_composite = OpCompositeInsert %mat4x3 %float_entry %matrix 0 1"
        << std::endl;
  spirv << R"(OpReturn
              OpFunctionEnd)";
  CompileSuccessfully(spirv.str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

#if 0
TEST_P(ValidateIdWithMessage, OpFunctionCallArgumentCountBar) {
  const char *spirv = R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypeFunction %2 %2
%4 = OpTypeFunction %1
%5 = OpConstant %2 42 ;21

%6 = OpFunction %2 None %3
%7 = OpFunctionParameter %2
%8 = OpLabel
%9 = OpLoad %2 %7
     OpReturnValue %9
     OpFunctionEnd

%10 = OpFunction %1 None %4
%11 = OpLabel
      OpReturn
%12 = OpFunctionCall %2 %6 %5
      OpFunctionEnd)";
  CHECK(spirv, SPV_ERROR_INVALID_ID);
}
#endif

// TODO: The many things that changed with how images are used.
// TODO: OpTextureSample
// TODO: OpTextureSampleDref
// TODO: OpTextureSampleLod
// TODO: OpTextureSampleProj
// TODO: OpTextureSampleGrad
// TODO: OpTextureSampleOffset
// TODO: OpTextureSampleProjLod
// TODO: OpTextureSampleProjGrad
// TODO: OpTextureSampleLodOffset
// TODO: OpTextureSampleProjOffset
// TODO: OpTextureSampleGradOffset
// TODO: OpTextureSampleProjLodOffset
// TODO: OpTextureSampleProjGradOffset
// TODO: OpTextureFetchTexelLod
// TODO: OpTextureFetchTexelOffset
// TODO: OpTextureFetchSample
// TODO: OpTextureFetchTexel
// TODO: OpTextureGather
// TODO: OpTextureGatherOffset
// TODO: OpTextureGatherOffsets
// TODO: OpTextureQuerySizeLod
// TODO: OpTextureQuerySize
// TODO: OpTextureQueryLevels
// TODO: OpTextureQuerySamples
// TODO: OpConvertUToF
// TODO: OpConvertFToS
// TODO: OpConvertSToF
// TODO: OpConvertUToF
// TODO: OpUConvert
// TODO: OpSConvert
// TODO: OpFConvert
// TODO: OpConvertPtrToU
// TODO: OpConvertUToPtr
// TODO: OpPtrCastToGeneric
// TODO: OpGenericCastToPtr
// TODO: OpBitcast
// TODO: OpGenericCastToPtrExplicit
// TODO: OpSatConvertSToU
// TODO: OpSatConvertUToS
// TODO: OpVectorExtractDynamic
// TODO: OpVectorInsertDynamic

TEST_P(ValidateIdWithMessage, OpVectorShuffleIntGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%int = OpTypeInt 32 0
%ivec3 = OpTypeVector %int 3
%ivec4 = OpTypeVector %int 4
%ptr_ivec3 = OpTypePointer Function %ivec3
%undef = OpUndef %ivec4
%int_42 = OpConstant %int 42
%int_0 = OpConstant %int 0
%int_2 = OpConstant %int 2
%1 = OpConstantComposite %ivec3 %int_42 %int_0 %int_2
%2 = OpTypeFunction %ivec3
%3 = OpFunction %ivec3 None %2
%4 = OpLabel
%var = OpVariable %ptr_ivec3 Function %1
%5 = OpLoad %ivec3 %var
%6 = OpVectorShuffle %ivec3 %5 %undef 2 1 0
     OpReturnValue %6
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpVectorShuffleFloatGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%float = OpTypeFloat 32
%vec2 = OpTypeVector %float 2
%vec3 = OpTypeVector %float 3
%vec4 = OpTypeVector %float 4
%ptr_vec2 = OpTypePointer Function %vec2
%ptr_vec3 = OpTypePointer Function %vec3
%float_1 = OpConstant %float 1
%float_2 = OpConstant %float 2
%1 = OpConstantComposite %vec2 %float_2 %float_1
%2 = OpConstantComposite %vec3 %float_1 %float_2 %float_2
%3 = OpTypeFunction %vec4
%4 = OpFunction %vec4 None %3
%5 = OpLabel
%var = OpVariable %ptr_vec2 Function %1
%var2 = OpVariable %ptr_vec3 Function %2
%6 = OpLoad %vec2 %var
%7 = OpLoad %vec3 %var2
%8 = OpVectorShuffle %vec4 %6 %7 4 3 1 0xffffffff
     OpReturnValue %8
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpVectorShuffleScalarResultType) {
  std::string spirv = kGLSL450MemoryModel + R"(
%float = OpTypeFloat 32
%vec2 = OpTypeVector %float 2
%ptr_vec2 = OpTypePointer Function %vec2
%float_1 = OpConstant %float 1
%float_2 = OpConstant %float 2
%1 = OpConstantComposite %vec2 %float_2 %float_1
%2 = OpTypeFunction %float
%3 = OpFunction %float None %2
%4 = OpLabel
%var = OpVariable %ptr_vec2 Function %1
%5 = OpLoad %vec2 %var
%6 = OpVectorShuffle %float %5 %5 0
     OpReturnValue %6
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "Result Type of OpVectorShuffle must be OpTypeVector.")));
}

TEST_P(ValidateIdWithMessage, OpVectorShuffleComponentCount) {
  std::string spirv = kGLSL450MemoryModel + R"(
%int = OpTypeInt 32 0
%ivec3 = OpTypeVector %int 3
%ptr_ivec3 = OpTypePointer Function %ivec3
%int_42 = OpConstant %int 42
%int_0 = OpConstant %int 0
%int_2 = OpConstant %int 2
%1 = OpConstantComposite %ivec3 %int_42 %int_0 %int_2
%2 = OpTypeFunction %ivec3
%3 = OpFunction %ivec3 None %2
%4 = OpLabel
%var = OpVariable %ptr_ivec3 Function %1
%5 = OpLoad %ivec3 %var
%6 = OpVectorShuffle %ivec3 %5 %5 0 1
     OpReturnValue %6
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpVectorShuffle component literals count does not match "
                  "Result Type <id> '2[%v3uint]'s vector component count.")));
}

TEST_P(ValidateIdWithMessage, OpVectorShuffleVector1Type) {
  std::string spirv = kGLSL450MemoryModel + R"(
%int = OpTypeInt 32 0
%ivec2 = OpTypeVector %int 2
%ptr_int = OpTypePointer Function %int
%undef = OpUndef %ivec2
%int_42 = OpConstant %int 42
%2 = OpTypeFunction %ivec2
%3 = OpFunction %ivec2 None %2
%4 = OpLabel
%var = OpVariable %ptr_int Function %int_42
%5 = OpLoad %int %var
%6 = OpVectorShuffle %ivec2 %5 %undef 0 0
     OpReturnValue %6
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("The type of Vector 1 must be OpTypeVector.")));
}

TEST_P(ValidateIdWithMessage, OpVectorShuffleVector2Type) {
  std::string spirv = kGLSL450MemoryModel + R"(
%int = OpTypeInt 32 0
%ivec2 = OpTypeVector %int 2
%ptr_ivec2 = OpTypePointer Function %ivec2
%undef = OpUndef %int
%int_42 = OpConstant %int 42
%1 = OpConstantComposite %ivec2 %int_42 %int_42
%2 = OpTypeFunction %ivec2
%3 = OpFunction %ivec2 None %2
%4 = OpLabel
%var = OpVariable %ptr_ivec2 Function %1
%5 = OpLoad %ivec2 %var
%6 = OpVectorShuffle %ivec2 %5 %undef 0 1
     OpReturnValue %6
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("The type of Vector 2 must be OpTypeVector.")));
}

TEST_P(ValidateIdWithMessage, OpVectorShuffleVector1ComponentType) {
  std::string spirv = kGLSL450MemoryModel + R"(
%int = OpTypeInt 32 0
%ivec3 = OpTypeVector %int 3
%ptr_ivec3 = OpTypePointer Function %ivec3
%int_42 = OpConstant %int 42
%int_0 = OpConstant %int 0
%int_2 = OpConstant %int 2
%float = OpTypeFloat 32
%vec3 = OpTypeVector %float 3
%vec4 = OpTypeVector %float 4
%ptr_vec3 = OpTypePointer Function %vec3
%float_1 = OpConstant %float 1
%float_2 = OpConstant %float 2
%1 = OpConstantComposite %ivec3 %int_42 %int_0 %int_2
%2 = OpConstantComposite %vec3 %float_1 %float_2 %float_2
%3 = OpTypeFunction %vec4
%4 = OpFunction %vec4 None %3
%5 = OpLabel
%var = OpVariable %ptr_ivec3 Function %1
%var2 = OpVariable %ptr_vec3 Function %2
%6 = OpLoad %ivec3 %var
%7 = OpLoad %vec3 %var2
%8 = OpVectorShuffle %vec4 %6 %7 4 3 1 0
     OpReturnValue %8
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "The Component Type of Vector 1 must be the same as "
                  "ResultType.")));
}

TEST_P(ValidateIdWithMessage, OpVectorShuffleVector2ComponentType) {
  std::string spirv = kGLSL450MemoryModel + R"(
%int = OpTypeInt 32 0
%ivec3 = OpTypeVector %int 3
%ptr_ivec3 = OpTypePointer Function %ivec3
%int_42 = OpConstant %int 42
%int_0 = OpConstant %int 0
%int_2 = OpConstant %int 2
%float = OpTypeFloat 32
%vec3 = OpTypeVector %float 3
%vec4 = OpTypeVector %float 4
%ptr_vec3 = OpTypePointer Function %vec3
%float_1 = OpConstant %float 1
%float_2 = OpConstant %float 2
%1 = OpConstantComposite %ivec3 %int_42 %int_0 %int_2
%2 = OpConstantComposite %vec3 %float_1 %float_2 %float_2
%3 = OpTypeFunction %vec4
%4 = OpFunction %vec4 None %3
%5 = OpLabel
%var = OpVariable %ptr_ivec3 Function %1
%var2 = OpVariable %ptr_vec3 Function %2
%6 = OpLoad %vec3 %var2
%7 = OpLoad %ivec3 %var
%8 = OpVectorShuffle %vec4 %6 %7 4 3 1 0
     OpReturnValue %8
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "The Component Type of Vector 2 must be the same as "
                  "ResultType.")));
}

TEST_P(ValidateIdWithMessage, OpVectorShuffleLiterals) {
  std::string spirv = kGLSL450MemoryModel + R"(
%float = OpTypeFloat 32
%vec2 = OpTypeVector %float 2
%vec3 = OpTypeVector %float 3
%vec4 = OpTypeVector %float 4
%ptr_vec2 = OpTypePointer Function %vec2
%ptr_vec3 = OpTypePointer Function %vec3
%float_1 = OpConstant %float 1
%float_2 = OpConstant %float 2
%1 = OpConstantComposite %vec2 %float_2 %float_1
%2 = OpConstantComposite %vec3 %float_1 %float_2 %float_2
%3 = OpTypeFunction %vec4
%4 = OpFunction %vec4 None %3
%5 = OpLabel
%var = OpVariable %ptr_vec2 Function %1
%var2 = OpVariable %ptr_vec3 Function %2
%6 = OpLoad %vec2 %var
%7 = OpLoad %vec3 %var2
%8 = OpVectorShuffle %vec4 %6 %7 0 8 2 6
     OpReturnValue %8
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message(
          "Component index 8 is out of bounds for combined (Vector1 + Vector2) "
          "size of 5.")));
}

// TODO: OpCompositeConstruct
// TODO: OpCompositeExtract
// TODO: OpCompositeInsert
// TODO: OpCopyObject
// TODO: OpTranspose
// TODO: OpSNegate
// TODO: OpFNegate
// TODO: OpNot
// TODO: OpIAdd
// TODO: OpFAdd
// TODO: OpISub
// TODO: OpFSub
// TODO: OpIMul
// TODO: OpFMul
// TODO: OpUDiv
// TODO: OpSDiv
// TODO: OpFDiv
// TODO: OpUMod
// TODO: OpSRem
// TODO: OpSMod
// TODO: OpFRem
// TODO: OpFMod
// TODO: OpVectorTimesScalar
// TODO: OpMatrixTimesScalar
// TODO: OpVectorTimesMatrix
// TODO: OpMatrixTimesVector
// TODO: OpMatrixTimesMatrix
// TODO: OpOuterProduct
// TODO: OpDot
// TODO: OpShiftRightLogical
// TODO: OpShiftRightArithmetic
// TODO: OpShiftLeftLogical
// TODO: OpBitwiseOr
// TODO: OpBitwiseXor
// TODO: OpBitwiseAnd
// TODO: OpAny
// TODO: OpAll
// TODO: OpIsNan
// TODO: OpIsInf
// TODO: OpIsFinite
// TODO: OpIsNormal
// TODO: OpSignBitSet
// TODO: OpLessOrGreater
// TODO: OpOrdered
// TODO: OpUnordered
// TODO: OpLogicalOr
// TODO: OpLogicalXor
// TODO: OpLogicalAnd
// TODO: OpSelect
// TODO: OpIEqual
// TODO: OpFOrdEqual
// TODO: OpFUnordEqual
// TODO: OpINotEqual
// TODO: OpFOrdNotEqual
// TODO: OpFUnordNotEqual
// TODO: OpULessThan
// TODO: OpSLessThan
// TODO: OpFOrdLessThan
// TODO: OpFUnordLessThan
// TODO: OpUGreaterThan
// TODO: OpSGreaterThan
// TODO: OpFOrdGreaterThan
// TODO: OpFUnordGreaterThan
// TODO: OpULessThanEqual
// TODO: OpSLessThanEqual
// TODO: OpFOrdLessThanEqual
// TODO: OpFUnordLessThanEqual
// TODO: OpUGreaterThanEqual
// TODO: OpSGreaterThanEqual
// TODO: OpFOrdGreaterThanEqual
// TODO: OpFUnordGreaterThanEqual
// TODO: OpDPdx
// TODO: OpDPdy
// TODO: OpFWidth
// TODO: OpDPdxFine
// TODO: OpDPdyFine
// TODO: OpFwidthFine
// TODO: OpDPdxCoarse
// TODO: OpDPdyCoarse
// TODO: OpFwidthCoarse
// TODO: OpLoopMerge
// TODO: OpSelectionMerge
// TODO: OpBranch

TEST_P(ValidateIdWithMessage, OpPhiNotAType) {
  std::string spirv = kOpenCLMemoryModel32 + R"(
%2 = OpTypeBool
%3 = OpConstantTrue %2
%4 = OpTypeVoid
%5 = OpTypeFunction %4
%6 = OpFunction %4 None %5
%7 = OpLabel
OpBranch %8
%8 = OpLabel
%9 = OpPhi %3 %3 %7
OpReturn
OpFunctionEnd
  )";

  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message("ID '3[%true]' is not a type "
                                     "id")));
}

TEST_P(ValidateIdWithMessage, OpPhiSamePredecessor) {
  std::string spirv = kOpenCLMemoryModel32 + R"(
%2 = OpTypeBool
%3 = OpConstantTrue %2
%4 = OpTypeVoid
%5 = OpTypeFunction %4
%6 = OpFunction %4 None %5
%7 = OpLabel
OpBranchConditional %3 %8 %8
%8 = OpLabel
%9 = OpPhi %2 %3 %7
OpReturn
OpFunctionEnd
  )";

  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpPhiOddArgumentNumber) {
  std::string spirv = kOpenCLMemoryModel32 + R"(
%2 = OpTypeBool
%3 = OpConstantTrue %2
%4 = OpTypeVoid
%5 = OpTypeFunction %4
%6 = OpFunction %4 None %5
%7 = OpLabel
OpBranch %8
%8 = OpLabel
%9 = OpPhi %2 %3
OpReturn
OpFunctionEnd
  )";

  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("OpPhi does not have an equal number of incoming "
                             "values and basic blocks.")));
}

TEST_P(ValidateIdWithMessage, OpPhiTooFewPredecessors) {
  std::string spirv = kOpenCLMemoryModel32 + R"(
%2 = OpTypeBool
%3 = OpConstantTrue %2
%4 = OpTypeVoid
%5 = OpTypeFunction %4
%6 = OpFunction %4 None %5
%7 = OpLabel
OpBranch %8
%8 = OpLabel
%9 = OpPhi %2
OpReturn
OpFunctionEnd
  )";

  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpPhi's number of incoming blocks (0) does not match "
                  "block's predecessor count (1).")));
}

TEST_P(ValidateIdWithMessage, OpPhiTooManyPredecessors) {
  std::string spirv = kOpenCLMemoryModel32 + R"(
%2 = OpTypeBool
%3 = OpConstantTrue %2
%4 = OpTypeVoid
%5 = OpTypeFunction %4
%6 = OpFunction %4 None %5
%7 = OpLabel
OpBranch %8
%9 = OpLabel
OpReturn
%8 = OpLabel
%10 = OpPhi %2 %3 %7 %3 %9
OpReturn
OpFunctionEnd
  )";

  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpPhi's number of incoming blocks (2) does not match "
                  "block's predecessor count (1).")));
}

TEST_P(ValidateIdWithMessage, OpPhiMismatchedTypes) {
  std::string spirv = kOpenCLMemoryModel32 + R"(
%2 = OpTypeBool
%3 = OpConstantTrue %2
%4 = OpTypeVoid
%5 = OpTypeInt 32 0
%6 = OpConstant %5 0
%7 = OpTypeFunction %4
%8 = OpFunction %4 None %7
%9 = OpLabel
OpBranchConditional %3 %10 %11
%11 = OpLabel
OpBranch %10
%10 = OpLabel
%12 = OpPhi %2 %3 %9 %6 %11
OpReturn
OpFunctionEnd
  )";

  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpPhi's result type <id> '2[%bool]' does not match "
                  "incoming value <id> '6[%uint_0]' type <id> "
                  "'5[%uint]'.")));
}

TEST_P(ValidateIdWithMessage, OpPhiPredecessorNotABlock) {
  std::string spirv = kOpenCLMemoryModel32 + R"(
%2 = OpTypeBool
%3 = OpConstantTrue %2
%4 = OpTypeVoid
%5 = OpTypeFunction %4
%6 = OpFunction %4 None %5
%7 = OpLabel
OpBranchConditional %3 %8 %9
%9 = OpLabel
OpBranch %11
%11 = OpLabel
OpBranch %8
%8 = OpLabel
%10 = OpPhi %2 %3 %7 %3 %3
OpReturn
OpFunctionEnd
  )";

  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpPhi's incoming basic block <id> '3[%true]' is not an "
                  "OpLabel.")));
}

TEST_P(ValidateIdWithMessage, OpPhiNotAPredecessor) {
  std::string spirv = kOpenCLMemoryModel32 + R"(
%2 = OpTypeBool
%3 = OpConstantTrue %2
%4 = OpTypeVoid
%5 = OpTypeFunction %4
%6 = OpFunction %4 None %5
%7 = OpLabel
OpBranchConditional %3 %8 %9
%9 = OpLabel
OpBranch %11
%11 = OpLabel
OpBranch %8
%8 = OpLabel
%10 = OpPhi %2 %3 %7 %3 %9
OpReturn
OpFunctionEnd
  )";

  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpPhi's incoming basic block <id> '9[%9]' is not a "
                  "predecessor of <id> '8[%8]'.")));
}

TEST_P(ValidateIdWithMessage, OpBranchConditionalGood) {
  std::string spirv = BranchConditionalSetup + R"(
    %branch_cond = OpINotEqual %bool %i0 %i1
                   OpSelectionMerge %end None
                   OpBranchConditional %branch_cond %target_t %target_f
  )" + BranchConditionalTail;

  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
}

TEST_P(ValidateIdWithMessage, OpBranchConditionalWithWeightsGood) {
  std::string spirv = BranchConditionalSetup + R"(
    %branch_cond = OpINotEqual %bool %i0 %i1
                   OpSelectionMerge %end None
                   OpBranchConditional %branch_cond %target_t %target_f 1 1
  )" + BranchConditionalTail;

  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
}

TEST_P(ValidateIdWithMessage, OpBranchConditional_CondIsScalarInt) {
  std::string spirv = BranchConditionalSetup + R"(
                   OpSelectionMerge %end None
                   OpBranchConditional %i0 %target_t %target_f
  )" + BranchConditionalTail;

  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("Condition operand for OpBranchConditional must "
                             "be of boolean type")));
}

TEST_P(ValidateIdWithMessage, OpBranchConditional_TrueTargetIsNotLabel) {
  std::string spirv = BranchConditionalSetup + R"(
                   OpSelectionMerge %end None
                   OpBranchConditional %true %i0 %target_f
  )" + BranchConditionalTail;

  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "The 'True Label' operand for OpBranchConditional must "
                  "be the ID of an OpLabel instruction")));
}

TEST_P(ValidateIdWithMessage, OpBranchConditional_FalseTargetIsNotLabel) {
  std::string spirv = BranchConditionalSetup + R"(
                   OpSelectionMerge %end None
                   OpBranchConditional %true %target_t %i0
  )" + BranchConditionalTail;

  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "The 'False Label' operand for OpBranchConditional "
                  "must be the ID of an OpLabel instruction")));
}

TEST_P(ValidateIdWithMessage, OpBranchConditional_NotEnoughWeights) {
  std::string spirv = BranchConditionalSetup + R"(
    %branch_cond = OpINotEqual %bool %i0 %i1
                   OpSelectionMerge %end None
                   OpBranchConditional %branch_cond %target_t %target_f 1
  )" + BranchConditionalTail;

  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpBranchConditional requires either 3 or 5 parameters")));
}

TEST_P(ValidateIdWithMessage, OpBranchConditional_TooManyWeights) {
  std::string spirv = BranchConditionalSetup + R"(
    %branch_cond = OpINotEqual %bool %i0 %i1
                   OpSelectionMerge %end None
                   OpBranchConditional %branch_cond %target_t %target_f 1 2 3
  )" + BranchConditionalTail;

  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpBranchConditional requires either 3 or 5 parameters")));
}

TEST_P(ValidateIdWithMessage, OpBranchConditional_ConditionIsAType) {
  std::string spirv = BranchConditionalSetup + R"(
OpBranchConditional %bool %target_t %target_f
)" + BranchConditionalTail;

  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message("Operand '3[%bool]' cannot be a "
                                     "type")));
}

// TODO: OpSwitch

TEST_P(ValidateIdWithMessage, OpReturnValueConstantGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypeFunction %2
%4 = OpConstant %2 42
%5 = OpFunction %2 None %3
%6 = OpLabel
     OpReturnValue %4
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpReturnValueVariableGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0 ;10
%3 = OpTypeFunction %2
%8 = OpTypePointer Function %2 ;18
%4 = OpConstant %2 42 ;22
%5 = OpFunction %2 None %3 ;27
%6 = OpLabel ;29
%7 = OpVariable %8 Function %4 ;34
%9 = OpLoad %2 %7
     OpReturnValue %9 ;36
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpReturnValueExpressionGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypeFunction %2
%4 = OpConstant %2 42
%5 = OpFunction %2 None %3
%6 = OpLabel
%7 = OpIAdd %2 %4 %4
     OpReturnValue %7
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpReturnValueIsType) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypeFunction %2
%5 = OpFunction %2 None %3
%6 = OpLabel
     OpReturnValue %1
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message("Operand '1[%void]' cannot be a "
                                     "type")));
}

TEST_P(ValidateIdWithMessage, OpReturnValueIsLabel) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypeFunction %2
%5 = OpFunction %2 None %3
%6 = OpLabel
     OpReturnValue %6
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message("Operand '5[%5]' requires a type")));
}

TEST_P(ValidateIdWithMessage, OpReturnValueIsVoid) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypeFunction %1
%5 = OpFunction %1 None %3
%6 = OpLabel
%7 = OpFunctionCall %1 %5
     OpReturnValue %7
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpReturnValue value's type <id> '1[%void]' is missing or "
                  "void.")));
}

TEST_P(ValidateIdWithMessage, OpReturnValueIsVariableInPhysical) {
  // It's valid to return a pointer in a physical addressing model.
  std::string spirv = kOpCapabilitySetup + R"(
     OpMemoryModel Physical32 OpenCL
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Function %2
%4 = OpTypeFunction %3
%5 = OpFunction %3 None %4
%6 = OpLabel
%7 = OpVariable %3 Function
     OpReturnValue %7
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpReturnValueIsVariableInLogical) {
  // It's invalid to return a pointer in a physical addressing model.
  std::string spirv = kOpCapabilitySetup + R"(
     OpMemoryModel Logical GLSL450
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Function %2
%4 = OpTypeFunction %3
%5 = OpFunction %3 None %4
%6 = OpLabel
%7 = OpVariable %3 Function
     OpReturnValue %7
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("OpReturnValue value's type <id> "
                             "'3[%_ptr_Function_uint]' is a pointer, which is "
                             "invalid in the Logical addressing model.")));
}

// With the VariablePointer Capability, the return value of a function is
// allowed to be a pointer.
TEST_P(ValidateIdWithMessage, OpReturnValueVarPtrGood) {
  std::ostringstream spirv;
  createVariablePointerSpirvProgram(&spirv,
                                    "" /* Instructions to add to "main" */,
                                    true /* Add VariablePointers Capability?*/,
                                    true /* Use Helper Function? */);
  CompileSuccessfully(spirv.str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

// Without the VariablePointer Capability, the return value of a function is
// *not* allowed to be a pointer.
// Disabled since using OpSelect with pointers without VariablePointers will
// fail LogicalsPass.
TEST_P(ValidateIdWithMessage, DISABLED_OpReturnValueVarPtrBad) {
  std::ostringstream spirv;
  createVariablePointerSpirvProgram(&spirv,
                                    "" /* Instructions to add to "main" */,
                                    false /* Add VariablePointers Capability?*/,
                                    true /* Use Helper Function? */);
  CompileSuccessfully(spirv.str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpReturnValue value's type <id> '7' is a pointer, "
                  "which is invalid in the Logical addressing model.")));
}

// TODO: enable when this bug is fixed:
// https://cvs.khronos.org/bugzilla/show_bug.cgi?id=15404
TEST_P(ValidateIdWithMessage, DISABLED_OpReturnValueIsFunction) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypeFunction %2
%5 = OpFunction %2 None %3
%6 = OpLabel
     OpReturnValue %5
     OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, UndefinedTypeId) {
  std::string spirv = kGLSL450MemoryModel + R"(
%s = OpTypeStruct %i32
)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "Operand '2[%2]' requires a previous definition")));
}

TEST_P(ValidateIdWithMessage, UndefinedIdScope) {
  std::string spirv = kGLSL450MemoryModel + R"(
%u32    = OpTypeInt 32 0
%memsem = OpConstant %u32 0
%void   = OpTypeVoid
%void_f = OpTypeFunction %void
%f      = OpFunction %void None %void_f
%l      = OpLabel
          OpMemoryBarrier %undef %memsem
          OpReturn
          OpFunctionEnd
)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message("ID '7[%7]' has not been "
                                     "defined")));
}

TEST_P(ValidateIdWithMessage, UndefinedIdMemSem) {
  std::string spirv = kGLSL450MemoryModel + R"(
%u32    = OpTypeInt 32 0
%scope  = OpConstant %u32 0
%void   = OpTypeVoid
%void_f = OpTypeFunction %void
%f      = OpFunction %void None %void_f
%l      = OpLabel
          OpMemoryBarrier %scope %undef
          OpReturn
          OpFunctionEnd
)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message("ID '7[%7]' has not been "
                                     "defined")));
}

TEST_P(ValidateIdWithMessage,
       KernelOpEntryPointAndOpInBoundsPtrAccessChainGood) {
  std::string spirv = kOpenCLMemoryModel32 + R"(
      OpEntryPoint Kernel %2 "simple_kernel"
      OpSource OpenCL_C 200000
      OpDecorate %3 BuiltIn GlobalInvocationId
      OpDecorate %3 Constant
      OpDecorate %4 FuncParamAttr NoCapture
      OpDecorate %3 LinkageAttributes "__spirv_GlobalInvocationId" Import
 %5 = OpTypeInt 32 0
 %6 = OpTypeVector %5 3
 %7 = OpTypePointer UniformConstant %6
 %3 = OpVariable %7 UniformConstant
 %8 = OpTypeVoid
 %9 = OpTypeStruct %5
%10 = OpTypePointer CrossWorkgroup %9
%11 = OpTypeFunction %8 %10
%12 = OpConstant %5 0
%13 = OpTypePointer CrossWorkgroup %5
%14 = OpConstant %5 42
 %2 = OpFunction %8 None %11
 %4 = OpFunctionParameter %10
%15 = OpLabel
%16 = OpLoad %6 %3 Aligned 0
%17 = OpCompositeExtract %5 %16 0
%18 = OpInBoundsPtrAccessChain %13 %4 %17 %12
      OpStore %18 %14 Aligned 4
      OpReturn
      OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpPtrAccessChainGood) {
  std::string spirv = kOpenCLMemoryModel64 + R"(
      OpEntryPoint Kernel %2 "another_kernel"
      OpSource OpenCL_C 200000
      OpDecorate %3 BuiltIn GlobalInvocationId
      OpDecorate %3 Constant
      OpDecorate %4 FuncParamAttr NoCapture
      OpDecorate %3 LinkageAttributes "__spirv_GlobalInvocationId" Import
 %5 = OpTypeInt 64 0
 %6 = OpTypeVector %5 3
 %7 = OpTypePointer UniformConstant %6
 %3 = OpVariable %7 UniformConstant
 %8 = OpTypeVoid
 %9 = OpTypeInt 32 0
%10 = OpTypeStruct %9
%11 = OpTypePointer CrossWorkgroup %10
%12 = OpTypeFunction %8 %11
%13 = OpConstant %5 4294967295
%14 = OpConstant %9 0
%15 = OpTypePointer CrossWorkgroup %9
%16 = OpConstant %9 42
 %2 = OpFunction %8 None %12
 %4 = OpFunctionParameter %11
%17 = OpLabel
%18 = OpLoad %6 %3 Aligned 0
%19 = OpCompositeExtract %5 %18 0
%20 = OpBitwiseAnd %5 %19 %13
%21 = OpPtrAccessChain %15 %4 %20 %14
      OpStore %21 %16 Aligned 4
      OpReturn
      OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, StgBufOpPtrAccessChainGood) {
  std::string spirv = R"(
     OpCapability Shader
     OpCapability Linkage
     OpCapability VariablePointersStorageBuffer
     OpExtension "SPV_KHR_variable_pointers"
     OpMemoryModel Logical GLSL450
     OpEntryPoint GLCompute %3 ""
     OpDecorate %ptr ArrayStride 8
%int = OpTypeInt 32 0
%int_2 = OpConstant %int 2
%int_4 = OpConstant %int 4
%struct = OpTypeStruct %int
%array = OpTypeArray %struct %int_4
%ptr = OpTypePointer StorageBuffer %array
%var = OpVariable %ptr StorageBuffer
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%3 = OpFunction %1 None %2
%4 = OpLabel
%5 = OpPtrAccessChain %ptr %var %int_2
     OpReturn
     OpFunctionEnd
)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}

TEST_P(ValidateIdWithMessage, OpLoadBitcastPointerGood) {
  std::string spirv = kOpenCLMemoryModel64 + R"(
%2  = OpTypeVoid
%3  = OpTypeInt 32 0
%4  = OpTypeFloat 32
%5  = OpTypePointer UniformConstant %3
%6  = OpTypePointer UniformConstant %4
%7  = OpVariable %5 UniformConstant
%8  = OpTypeFunction %2
%9  = OpFunction %2 None %8
%10 = OpLabel
%11 = OpBitcast %6 %7
%12 = OpLoad %4 %11
      OpReturn
      OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpLoadBitcastNonPointerBad) {
  std::string spirv = kOpenCLMemoryModel64 + R"(
%2  = OpTypeVoid
%3  = OpTypeInt 32 0
%4  = OpTypeFloat 32
%5  = OpTypePointer UniformConstant %3
%6  = OpTypeFunction %2
%7  = OpVariable %5 UniformConstant
%8  = OpFunction %2 None %6
%9  = OpLabel
%10 = OpLoad %3 %7
%11 = OpBitcast %4 %10
%12 = OpLoad %3 %11
      OpReturn
      OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpLoad type for pointer <id> '11[%11]' is not a pointer "
                  "type.")));
}
TEST_P(ValidateIdWithMessage, OpStoreBitcastPointerGood) {
  std::string spirv = kOpenCLMemoryModel64 + R"(
%2  = OpTypeVoid
%3  = OpTypeInt 32 0
%4  = OpTypeFloat 32
%5  = OpTypePointer Function %3
%6  = OpTypePointer Function %4
%7  = OpTypeFunction %2
%8  = OpConstant %3 42
%9  = OpFunction %2 None %7
%10 = OpLabel
%11 = OpVariable %6 Function
%12 = OpBitcast %5 %11
      OpStore %12 %8
      OpReturn
      OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_P(ValidateIdWithMessage, OpStoreBitcastNonPointerBad) {
  std::string spirv = kOpenCLMemoryModel64 + R"(
%2  = OpTypeVoid
%3  = OpTypeInt 32 0
%4  = OpTypeFloat 32
%5  = OpTypePointer Function %4
%6  = OpTypeFunction %2
%7  = OpConstant %4 42
%8  = OpFunction %2 None %6
%9  = OpLabel
%10 = OpVariable %5 Function
%11 = OpBitcast %3 %7
      OpStore %11 %7
      OpReturn
      OpFunctionEnd)";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "OpStore type for pointer <id> '11[%11]' is not a pointer "
                  "type.")));
}

// Result <id> resulting from an instruction within a function may not be used
// outside that function.
TEST_P(ValidateIdWithMessage, ResultIdUsedOutsideOfFunctionBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%3 = OpTypeInt 32 0
%4 = OpTypePointer Function %3
%5 = OpFunction %1 None %2
%6 = OpLabel
%7 = OpVariable %4 Function
OpReturn
OpFunctionEnd
%8 = OpFunction %1 None %2
%9 = OpLabel
%10 = OpLoad %3 %7
OpReturn
OpFunctionEnd
  )";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message("ID '7[%7]' defined in block '6[%6]' does "
                                     "not dominate its use in block "
                                     "'9[%9]'")));
}

TEST_P(ValidateIdWithMessage, SpecIdTargetNotSpecializationConstant) {
  std::string spirv = kGLSL450MemoryModel + R"(
OpDecorate %1 SpecId 200
%void = OpTypeVoid
%2 = OpTypeFunction %void
%int = OpTypeInt 32 0
%1 = OpConstant %int 3
%main = OpFunction %void None %2
%4 = OpLabel
OpReturnValue %1
OpFunctionEnd
  )";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("SpecId decoration on target <id> "
                             "'1[%uint_3]' must be a scalar specialization "
                             "constant")));
}

TEST_P(ValidateIdWithMessage, SpecIdTargetOpSpecConstantOpBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
OpDecorate %1 SpecId 200
%void = OpTypeVoid
%2 = OpTypeFunction %void
%int = OpTypeInt 32 0
%3 = OpConstant %int 1
%4 = OpConstant %int 2
%1 = OpSpecConstantOp %int IAdd %3 %4
%main = OpFunction %void None %2
%6 = OpLabel
OpReturnValue %3
OpFunctionEnd
  )";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("SpecId decoration on target <id> '1[%1]' "
                             "must be a scalar specialization constant")));
}

TEST_P(ValidateIdWithMessage, SpecIdTargetOpSpecConstantCompositeBad) {
  std::string spirv = kGLSL450MemoryModel + R"(
OpDecorate %1 SpecId 200
%void = OpTypeVoid
%2 = OpTypeFunction %void
%int = OpTypeInt 32 0
%3 = OpConstant %int 1
%1 = OpSpecConstantComposite %int
%main = OpFunction %void None %2
%4 = OpLabel
OpReturnValue %3
OpFunctionEnd
  )";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("SpecId decoration on target <id> '1[%1]' "
                             "must be a scalar specialization constant")));
}

TEST_P(ValidateIdWithMessage, SpecIdTargetGood) {
  std::string spirv = kGLSL450MemoryModel + R"(
OpDecorate %3 SpecId 200
OpDecorate %4 SpecId 201
OpDecorate %5 SpecId 202
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%int = OpTypeInt 32 0
%bool = OpTypeBool
%3 = OpSpecConstant %int 3
%4 = OpSpecConstantTrue %bool
%5 = OpSpecConstantFalse %bool
%main = OpFunction %1 None %2
%6 = OpLabel
OpReturn
OpFunctionEnd
  )";
  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
}

TEST_P(ValidateIdWithMessage, CorrectErrorForShuffle) {
  std::string spirv = kGLSL450MemoryModel + R"(
   %uint = OpTypeInt 32 0
  %float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%v2float = OpTypeVector %float 2
   %void = OpTypeVoid
    %548 = OpTypeFunction %void
     %CS = OpFunction %void None %548
    %550 = OpLabel
   %6275 = OpUndef %v2float
   %6280 = OpUndef %v2float
   %6282 = OpVectorShuffle %v4float %6275 %6280 0 1 4 5
           OpReturn
           OpFunctionEnd
  )";

  CompileSuccessfully(spirv.c_str());
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message(
          "Component index 4 is out of bounds for combined (Vector1 + Vector2) "
          "size of 4.")));
  EXPECT_EQ(25, getErrorPosition().index);
}

TEST_P(ValidateIdWithMessage, VoidStructMember) {
  const std::string spirv = kGLSL450MemoryModel + R"(
%void = OpTypeVoid
%struct = OpTypeStruct %void
)";

  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("Structures cannot contain a void type.")));
}

TEST_P(ValidateIdWithMessage, TypeFunctionBadUse) {
  std::string spirv = kGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%3 = OpTypePointer Function %2
%4 = OpFunction %1 None %2
%5 = OpLabel
     OpReturn
     OpFunctionEnd)";

  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "Invalid use of function type result id '2[%2]'.")));
}

TEST_P(ValidateIdWithMessage, BadTypeId) {
  std::string spirv = kGLSL450MemoryModel + R"(
          %1 = OpTypeVoid
          %2 = OpTypeFunction %1
          %3 = OpTypeFloat 32
          %4 = OpConstant %3 0
          %5 = OpFunction %1 None %2
          %6 = OpLabel
          %7 = OpUndef %4
               OpReturn
               OpFunctionEnd
)";

  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message("ID '4[%float_0]' is not a type "
                                     "id")));
}

TEST_P(ValidateIdWithMessage, VulkanMemoryModelLoadMakePointerVisibleGood) {
  std::string spirv = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
OpCapability Linkage
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Workgroup %2
%4 = OpVariable %3 Workgroup
%5 = OpTypeFunction %1
%6 = OpConstant %2 2
%7 = OpFunction %1 None %5
%8 = OpLabel
%9 = OpLoad %2 %4 NonPrivatePointerKHR|MakePointerVisibleKHR %6
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
}

TEST_P(ValidateIdWithMessage,
       VulkanMemoryModelLoadMakePointerVisibleMissingNonPrivatePointer) {
  std::string spirv = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
OpCapability Linkage
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Workgroup %2
%4 = OpVariable %3 Workgroup
%5 = OpTypeFunction %1
%6 = OpConstant %2 2
%7 = OpFunction %1 None %5
%8 = OpLabel
%9 = OpLoad %2 %4 MakePointerVisibleKHR %6
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("NonPrivatePointerKHR must be specified if "
                             "MakePointerVisibleKHR is specified.")));
}

TEST_P(ValidateIdWithMessage,
       VulkanMemoryModelLoadNonPrivatePointerBadStorageClass) {
  std::string spirv = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
OpCapability Linkage
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Private %2
%4 = OpVariable %3 Private
%5 = OpTypeFunction %1
%6 = OpConstant %2 2
%7 = OpFunction %1 None %5
%8 = OpLabel
%9 = OpLoad %2 %4 NonPrivatePointerKHR
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "NonPrivatePointerKHR requires a pointer in Uniform, "
                  "Workgroup, CrossWorkgroup, Generic, Image or "
                  "StorageBuffer storage classes.")));
}

TEST_P(ValidateIdWithMessage,
       VulkanMemoryModelLoadMakePointerAvailableCannotBeUsed) {
  std::string spirv = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
OpCapability Linkage
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Workgroup %2
%4 = OpVariable %3 Workgroup
%5 = OpTypeFunction %1
%6 = OpConstant %2 2
%7 = OpFunction %1 None %5
%8 = OpLabel
%9 = OpLoad %2 %4 NonPrivatePointerKHR|MakePointerAvailableKHR %6
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "MakePointerAvailableKHR cannot be used with OpLoad")));
}

TEST_P(ValidateIdWithMessage, VulkanMemoryModelStoreMakePointerAvailableGood) {
  std::string spirv = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
OpCapability Linkage
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Uniform %2
%4 = OpVariable %3 Uniform
%5 = OpTypeFunction %1
%6 = OpConstant %2 5
%7 = OpFunction %1 None %5
%8 = OpLabel
OpStore %4 %6 NonPrivatePointerKHR|MakePointerAvailableKHR %6
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
}

TEST_P(ValidateIdWithMessage,
       VulkanMemoryModelStoreMakePointerAvailableMissingNonPrivatePointer) {
  std::string spirv = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
OpCapability Linkage
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Uniform %2
%4 = OpVariable %3 Uniform
%5 = OpTypeFunction %1
%6 = OpConstant %2 5
%7 = OpFunction %1 None %5
%8 = OpLabel
OpStore %4 %6 MakePointerAvailableKHR %6
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("NonPrivatePointerKHR must be specified if "
                             "MakePointerAvailableKHR is specified.")));
}

TEST_P(ValidateIdWithMessage,
       VulkanMemoryModelStoreNonPrivatePointerBadStorageClass) {
  std::string spirv = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
OpCapability Linkage
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Output %2
%4 = OpVariable %3 Output
%5 = OpTypeFunction %1
%6 = OpConstant %2 5
%7 = OpFunction %1 None %5
%8 = OpLabel
OpStore %4 %6 NonPrivatePointerKHR
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "NonPrivatePointerKHR requires a pointer in Uniform, "
                  "Workgroup, CrossWorkgroup, Generic, Image or "
                  "StorageBuffer storage classes.")));
}

TEST_P(ValidateIdWithMessage,
       VulkanMemoryModelStoreMakePointerVisibleCannotBeUsed) {
  std::string spirv = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
OpCapability Linkage
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Uniform %2
%4 = OpVariable %3 Uniform
%5 = OpTypeFunction %1
%6 = OpConstant %2 5
%7 = OpFunction %1 None %5
%8 = OpLabel
OpStore %4 %6 NonPrivatePointerKHR|MakePointerVisibleKHR %6
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "MakePointerVisibleKHR cannot be used with OpStore.")));
}

TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemoryAvailable) {
  std::string spirv = R"(
OpCapability Shader
OpCapability Linkage
OpCapability VulkanMemoryModelKHR
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Workgroup %2
%4 = OpVariable %3 Workgroup
%5 = OpTypePointer Uniform %2
%6 = OpVariable %5 Uniform
%7 = OpConstant %2 2
%8 = OpConstant %2 5
%9 = OpTypeFunction %1
%10 = OpFunction %1 None %9
%11 = OpLabel
OpCopyMemory %4 %6 NonPrivatePointerKHR|MakePointerAvailableKHR %7
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
}

TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemoryVisible) {
  std::string spirv = R"(
OpCapability Shader
OpCapability Linkage
OpCapability VulkanMemoryModelKHR
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Workgroup %2
%4 = OpVariable %3 Workgroup
%5 = OpTypePointer Uniform %2
%6 = OpVariable %5 Uniform
%7 = OpConstant %2 2
%8 = OpConstant %2 5
%9 = OpTypeFunction %1
%10 = OpFunction %1 None %9
%11 = OpLabel
OpCopyMemory %4 %6 NonPrivatePointerKHR|MakePointerVisibleKHR %8
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
}

TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemoryAvailableAndVisible) {
  std::string spirv = R"(
OpCapability Shader
OpCapability Linkage
OpCapability VulkanMemoryModelKHR
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Workgroup %2
%4 = OpVariable %3 Workgroup
%5 = OpTypePointer Uniform %2
%6 = OpVariable %5 Uniform
%7 = OpConstant %2 2
%8 = OpConstant %2 5
%9 = OpTypeFunction %1
%10 = OpFunction %1 None %9
%11 = OpLabel
OpCopyMemory %4 %6 NonPrivatePointerKHR|MakePointerAvailableKHR|MakePointerVisibleKHR %7 %8
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
}

TEST_P(ValidateIdWithMessage,
       VulkanMemoryModelCopyMemoryAvailableMissingNonPrivatePointer) {
  std::string spirv = R"(
OpCapability Shader
OpCapability Linkage
OpCapability VulkanMemoryModelKHR
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Workgroup %2
%4 = OpVariable %3 Workgroup
%5 = OpTypePointer Uniform %2
%6 = OpVariable %5 Uniform
%7 = OpConstant %2 2
%8 = OpConstant %2 5
%9 = OpTypeFunction %1
%10 = OpFunction %1 None %9
%11 = OpLabel
OpCopyMemory %4 %6 MakePointerAvailableKHR %7
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("NonPrivatePointerKHR must be specified if "
                             "MakePointerAvailableKHR is specified.")));
}

TEST_P(ValidateIdWithMessage,
       VulkanMemoryModelCopyMemoryVisibleMissingNonPrivatePointer) {
  std::string spirv = R"(
OpCapability Shader
OpCapability Linkage
OpCapability VulkanMemoryModelKHR
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Workgroup %2
%4 = OpVariable %3 Workgroup
%5 = OpTypePointer Uniform %2
%6 = OpVariable %5 Uniform
%7 = OpConstant %2 2
%8 = OpConstant %2 5
%9 = OpTypeFunction %1
%10 = OpFunction %1 None %9
%11 = OpLabel
OpCopyMemory %4 %6 MakePointerVisibleKHR %8
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("NonPrivatePointerKHR must be specified if "
                             "MakePointerVisibleKHR is specified.")));
}

TEST_P(ValidateIdWithMessage,
       VulkanMemoryModelCopyMemoryAvailableBadStorageClass) {
  std::string spirv = R"(
OpCapability Shader
OpCapability Linkage
OpCapability VulkanMemoryModelKHR
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Output %2
%4 = OpVariable %3 Output
%5 = OpTypePointer Uniform %2
%6 = OpVariable %5 Uniform
%7 = OpConstant %2 2
%8 = OpConstant %2 5
%9 = OpTypeFunction %1
%10 = OpFunction %1 None %9
%11 = OpLabel
OpCopyMemory %4 %6 NonPrivatePointerKHR
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "NonPrivatePointerKHR requires a pointer in Uniform, "
                  "Workgroup, CrossWorkgroup, Generic, Image or "
                  "StorageBuffer storage classes.")));
}

TEST_P(ValidateIdWithMessage,
       VulkanMemoryModelCopyMemoryVisibleBadStorageClass) {
  std::string spirv = R"(
OpCapability Shader
OpCapability Linkage
OpCapability VulkanMemoryModelKHR
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Workgroup %2
%4 = OpVariable %3 Workgroup
%5 = OpTypePointer Input %2
%6 = OpVariable %5 Input
%7 = OpConstant %2 2
%8 = OpConstant %2 5
%9 = OpTypeFunction %1
%10 = OpFunction %1 None %9
%11 = OpLabel
OpCopyMemory %4 %6 NonPrivatePointerKHR
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "NonPrivatePointerKHR requires a pointer in Uniform, "
                  "Workgroup, CrossWorkgroup, Generic, Image or "
                  "StorageBuffer storage classes.")));
}

TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemorySizedAvailable) {
  std::string spirv = R"(
OpCapability Shader
OpCapability Linkage
OpCapability Addresses
OpCapability VulkanMemoryModelKHR
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Workgroup %2
%4 = OpVariable %3 Workgroup
%5 = OpTypePointer Uniform %2
%6 = OpVariable %5 Uniform
%7 = OpConstant %2 2
%8 = OpConstant %2 5
%9 = OpTypeFunction %1
%10 = OpFunction %1 None %9
%11 = OpLabel
OpCopyMemorySized %4 %6 %7 NonPrivatePointerKHR|MakePointerAvailableKHR %7
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
}

TEST_P(ValidateIdWithMessage, VulkanMemoryModelCopyMemorySizedVisible) {
  std::string spirv = R"(
OpCapability Shader
OpCapability Linkage
OpCapability Addresses
OpCapability VulkanMemoryModelKHR
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Workgroup %2
%4 = OpVariable %3 Workgroup
%5 = OpTypePointer Uniform %2
%6 = OpVariable %5 Uniform
%7 = OpConstant %2 2
%8 = OpConstant %2 5
%9 = OpTypeFunction %1
%10 = OpFunction %1 None %9
%11 = OpLabel
OpCopyMemorySized %4 %6 %7 NonPrivatePointerKHR|MakePointerVisibleKHR %8
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
}

TEST_P(ValidateIdWithMessage,
       VulkanMemoryModelCopyMemorySizedAvailableAndVisible) {
  std::string spirv = R"(
OpCapability Shader
OpCapability Linkage
OpCapability Addresses
OpCapability VulkanMemoryModelKHR
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Workgroup %2
%4 = OpVariable %3 Workgroup
%5 = OpTypePointer Uniform %2
%6 = OpVariable %5 Uniform
%7 = OpConstant %2 2
%8 = OpConstant %2 5
%9 = OpTypeFunction %1
%10 = OpFunction %1 None %9
%11 = OpLabel
OpCopyMemorySized %4 %6 %7 NonPrivatePointerKHR|MakePointerAvailableKHR|MakePointerVisibleKHR %7 %8
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
}

TEST_P(ValidateIdWithMessage,
       VulkanMemoryModelCopyMemorySizedAvailableMissingNonPrivatePointer) {
  std::string spirv = R"(
OpCapability Shader
OpCapability Linkage
OpCapability Addresses
OpCapability VulkanMemoryModelKHR
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Workgroup %2
%4 = OpVariable %3 Workgroup
%5 = OpTypePointer Uniform %2
%6 = OpVariable %5 Uniform
%7 = OpConstant %2 2
%8 = OpConstant %2 5
%9 = OpTypeFunction %1
%10 = OpFunction %1 None %9
%11 = OpLabel
OpCopyMemorySized %4 %6 %7 MakePointerAvailableKHR %7
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("NonPrivatePointerKHR must be specified if "
                             "MakePointerAvailableKHR is specified.")));
}

TEST_P(ValidateIdWithMessage,
       VulkanMemoryModelCopyMemorySizedVisibleMissingNonPrivatePointer) {
  std::string spirv = R"(
OpCapability Shader
OpCapability Linkage
OpCapability Addresses
OpCapability VulkanMemoryModelKHR
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Workgroup %2
%4 = OpVariable %3 Workgroup
%5 = OpTypePointer Uniform %2
%6 = OpVariable %5 Uniform
%7 = OpConstant %2 2
%8 = OpConstant %2 5
%9 = OpTypeFunction %1
%10 = OpFunction %1 None %9
%11 = OpLabel
OpCopyMemorySized %4 %6 %7 MakePointerVisibleKHR %8
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message("NonPrivatePointerKHR must be specified if "
                             "MakePointerVisibleKHR is specified.")));
}

TEST_P(ValidateIdWithMessage,
       VulkanMemoryModelCopyMemorySizedAvailableBadStorageClass) {
  std::string spirv = R"(
OpCapability Shader
OpCapability Linkage
OpCapability Addresses
OpCapability VulkanMemoryModelKHR
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Output %2
%4 = OpVariable %3 Output
%5 = OpTypePointer Uniform %2
%6 = OpVariable %5 Uniform
%7 = OpConstant %2 2
%8 = OpConstant %2 5
%9 = OpTypeFunction %1
%10 = OpFunction %1 None %9
%11 = OpLabel
OpCopyMemorySized %4 %6 %7 NonPrivatePointerKHR
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "NonPrivatePointerKHR requires a pointer in Uniform, "
                  "Workgroup, CrossWorkgroup, Generic, Image or "
                  "StorageBuffer storage classes.")));
}

TEST_P(ValidateIdWithMessage,
       VulkanMemoryModelCopyMemorySizedVisibleBadStorageClass) {
  std::string spirv = R"(
OpCapability Shader
OpCapability Linkage
OpCapability Addresses
OpCapability VulkanMemoryModelKHR
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpTypePointer Workgroup %2
%4 = OpVariable %3 Workgroup
%5 = OpTypePointer Input %2
%6 = OpVariable %5 Input
%7 = OpConstant %2 2
%8 = OpConstant %2 5
%9 = OpTypeFunction %1
%10 = OpFunction %1 None %9
%11 = OpLabel
OpCopyMemorySized %4 %6 %7 NonPrivatePointerKHR
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "NonPrivatePointerKHR requires a pointer in Uniform, "
                  "Workgroup, CrossWorkgroup, Generic, Image or "
                  "StorageBuffer storage classes.")));
}

TEST_P(ValidateIdWithMessage, IdDefInUnreachableBlock1) {
  const std::string spirv = kNoKernelGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%3 = OpTypeFloat 32
%4 = OpTypeFunction %3
%5 = OpFunction %1 None %2
%6 = OpLabel
OpReturn
%7 = OpLabel
%8 = OpFunctionCall %3 %9
OpUnreachable
OpFunctionEnd
%9 = OpFunction %3 None %4
%10 = OpLabel
OpReturnValue %8
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "ID '8[%8]' defined in block '7[%7]' does not dominate its "
                  "use in block '10[%10]'\n  %10 = OpLabel")));
}

TEST_P(ValidateIdWithMessage, IdDefInUnreachableBlock2) {
  const std::string spirv = kNoKernelGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%3 = OpTypeFloat 32
%4 = OpTypeFunction %3
%5 = OpFunction %1 None %2
%6 = OpLabel
OpReturn
%7 = OpLabel
%8 = OpFunctionCall %3 %9
OpUnreachable
OpFunctionEnd
%9 = OpFunction %3 None %4
%10 = OpLabel
OpReturnValue %8
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "ID '8[%8]' defined in block '7[%7]' does not dominate its "
                  "use in block '10[%10]'\n  %10 = OpLabel")));
}

TEST_P(ValidateIdWithMessage, IdDefInUnreachableBlock3) {
  const std::string spirv = kNoKernelGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%3 = OpTypeFloat 32
%4 = OpTypeFunction %3
%5 = OpFunction %1 None %2
%6 = OpLabel
OpReturn
%7 = OpLabel
%8 = OpFunctionCall %3 %9
OpReturn
OpFunctionEnd
%9 = OpFunction %3 None %4
%10 = OpLabel
OpReturnValue %8
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "ID '8[%8]' defined in block '7[%7]' does not dominate its "
                  "use in block '10[%10]'\n  %10 = OpLabel")));
}

TEST_P(ValidateIdWithMessage, IdDefInUnreachableBlock4) {
  const std::string spirv = kNoKernelGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%3 = OpTypeFloat 32
%4 = OpTypeFunction %3
%5 = OpFunction %1 None %2
%6 = OpLabel
OpReturn
%7 = OpLabel
%8 = OpUndef %3
%9 = OpCopyObject %3 %8
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
}

TEST_P(ValidateIdWithMessage, IdDefInUnreachableBlock5) {
  const std::string spirv = kNoKernelGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%3 = OpTypeFloat 32
%4 = OpTypeFunction %3
%5 = OpFunction %1 None %2
%6 = OpLabel
OpReturn
%7 = OpLabel
%8 = OpUndef %3
OpBranch %9
%9 = OpLabel
%10 = OpCopyObject %3 %8
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
}

TEST_P(ValidateIdWithMessage, IdDefInUnreachableBlock6) {
  const std::string spirv = kNoKernelGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%3 = OpTypeFloat 32
%4 = OpTypeFunction %3
%5 = OpFunction %1 None %2
%6 = OpLabel
OpBranch %7
%8 = OpLabel
%9 = OpUndef %3
OpBranch %7
%7 = OpLabel
%10 = OpCopyObject %3 %9
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "ID '9[%9]' defined in block '8[%8]' does not dominate its "
                  "use in block '7[%7]'\n  %7 = OpLabel")));
}

TEST_P(ValidateIdWithMessage, ReachableDefUnreachableUse) {
  const std::string spirv = kNoKernelGLSL450MemoryModel + R"(
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%3 = OpTypeFloat 32
%4 = OpTypeFunction %3
%5 = OpFunction %1 None %2
%6 = OpLabel
%7 = OpUndef %3
OpReturn
%8 = OpLabel
%9 = OpCopyObject %3 %7
OpReturn
OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
}

TEST_P(ValidateIdWithMessage, UnreachableDefUsedInPhi) {
  const std::string spirv = kNoKernelGLSL450MemoryModel + R"(
       %void = OpTypeVoid
          %3 = OpTypeFunction %void
      %float = OpTypeFloat 32
       %bool = OpTypeBool
          %6 = OpTypeFunction %float
          %1 = OpFunction %void None %3
          %7 = OpLabel
          %8 = OpUndef %bool
               OpSelectionMerge %9 None
               OpBranchConditional %8 %10 %9
         %10 = OpLabel
         %11 = OpUndef %float
               OpBranch %9
         %12 = OpLabel
         %13 = OpUndef %float
               OpUnreachable
          %9 = OpLabel
         %14 = OpPhi %float %11 %10 %13 %7
               OpReturn
               OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
  EXPECT_THAT(
      getDiagnosticString(),
      HasSubstr(make_message(
          "In OpPhi instruction '14[%14]', ID '13[%13]' definition does not "
          "dominate its parent '7[%7]'\n  %14 = OpPhi %float %11 %10 %13 "
          "%7")));
}

TEST_P(ValidateIdWithMessage, OpTypeForwardPointerNotAPointerType) {
  std::string spirv = R"(
     OpCapability GenericPointer
     OpCapability VariablePointersStorageBuffer
     OpMemoryModel Logical GLSL450
     OpEntryPoint Fragment %1 "main"
     OpExecutionMode %1 OriginLowerLeft
     OpTypeForwardPointer %2 CrossWorkgroup
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%1 = OpFunction %2 DontInline %3
%4 = OpLabel
     OpReturn
     OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "Pointer type in OpTypeForwardPointer is not a pointer "
                  "type.\n  OpTypeForwardPointer %void CrossWorkgroup")));
}

TEST_P(ValidateIdWithMessage, OpTypeForwardPointerWrongStorageClass) {
  std::string spirv = R"(
     OpCapability GenericPointer
     OpCapability VariablePointersStorageBuffer
     OpMemoryModel Logical GLSL450
     OpEntryPoint Fragment %1 "main"
     OpExecutionMode %1 OriginLowerLeft
     OpTypeForwardPointer %2 CrossWorkgroup
%int = OpTypeInt 32 1
%2 = OpTypePointer Function %int
%void = OpTypeVoid
%3 = OpTypeFunction %void
%1 = OpFunction %void None %3
%4 = OpLabel
     OpReturn
     OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message(
                  "Storage class in OpTypeForwardPointer does not match the "
                  "pointer definition.\n  OpTypeForwardPointer "
                  "%_ptr_Function_int CrossWorkgroup")));
}

TEST_P(ValidateIdWithMessage, MissingForwardPointer) {
  const std::string spirv = R"(
               OpCapability Linkage
               OpCapability Shader
               OpMemoryModel Logical Simple
      %float = OpTypeFloat 32
  %_struct_9 = OpTypeStruct %float %_ptr_Uniform__struct_9
%_ptr_Uniform__struct_9 = OpTypePointer Uniform %_struct_9
       %1278 = OpVariable %_ptr_Uniform__struct_9 Uniform
)";

  CompileSuccessfully(spirv);
  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
  EXPECT_THAT(getDiagnosticString(),
              HasSubstr(make_message("Operand '3[%_ptr_Uniform__struct_2]' "
                                     "requires a previous definition")));
}

TEST_P(ValidateIdWithMessage, NVBindlessSamplerInStruct) {
  std::string spirv = R"(
            OpCapability Shader
            OpCapability BindlessTextureNV
            OpExtension "SPV_NV_bindless_texture"
            OpMemoryModel Logical GLSL450
            OpSamplerImageAddressingModeNV 64
            OpEntryPoint Fragment %main "main"
            OpExecutionMode %main OriginUpperLeft
    %void = OpTypeVoid
       %3 = OpTypeFunction %void
   %float = OpTypeFloat 32
       %7 = OpTypeImage %float 2D 0 0 0 1 Unknown
       %8 = OpTypeSampledImage %7
       %9 = OpTypeImage %float 2D 0 0 0 2 Rgba32f
      %10 = OpTypeSampler
     %UBO = OpTypeStruct %8 %9 %10
%_ptr_Uniform_UBO = OpTypePointer Uniform %UBO
       %_ = OpVariable %_ptr_Uniform_UBO Uniform
    %main = OpFunction %void None %3
       %5 = OpLabel
            OpReturn
            OpFunctionEnd
)";

  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
}

INSTANTIATE_TEST_SUITE_P(, ValidateIdWithMessage, ::testing::Bool());

}  // namespace
}  // namespace val
}  // namespace spvtools
