text: // Copyright 2020 The Tint Authors.
  //
  // 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 "gmock/gmock.h"
  #include "src/reader/spirv/function.h"
  #include "src/reader/spirv/parser_impl_test_helper.h"
  #include "src/reader/spirv/spirv_tools_helpers_test.h"
  
  namespace tint {
  namespace reader {
  namespace spirv {
  namespace {
  
  using ::testing::Eq;
  using ::testing::HasSubstr;
  
  std::string Caps() {
    return R"(
    OpCapability Shader
    OpMemoryModel Logical Simple
    OpEntryPoint GLCompute %100 "main"
    OpExecutionMode %100 LocalSize 1 1 1
  )";
  }
  
  std::string CommonTypes() {
    return R"(
    %void = OpTypeVoid
    %voidfn = OpTypeFunction %void
  
    %uint = OpTypeInt 32 0
    %int = OpTypeInt 32 1
    %float = OpTypeFloat 32
  
    %uint_10 = OpConstant %uint 10
    %uint_20 = OpConstant %uint 20
    %uint_3 = OpConstant %uint 3
    %uint_4 = OpConstant %uint 4
    %uint_5 = OpConstant %uint 5
    %int_1 = OpConstant %int 1
    %int_30 = OpConstant %int 30
    %int_40 = OpConstant %int 40
    %float_50 = OpConstant %float 50
    %float_60 = OpConstant %float 60
    %float_70 = OpConstant %float 70
  
    %v2uint = OpTypeVector %uint 2
    %v3uint = OpTypeVector %uint 3
    %v4uint = OpTypeVector %uint 4
    %v2int = OpTypeVector %int 2
    %v2float = OpTypeVector %float 2
  
    %m3v2float = OpTypeMatrix %v2float 3
    %m3v2float_0 = OpConstantNull %m3v2float
  
    %s_v2f_u_i = OpTypeStruct %v2float %uint %int
    %a_u_5 = OpTypeArray %uint %uint_5
  
    %v2uint_3_4 = OpConstantComposite %v2uint %uint_3 %uint_4
    %v2uint_4_3 = OpConstantComposite %v2uint %uint_4 %uint_3
    %v2float_50_60 = OpConstantComposite %v2float %float_50 %float_60
    %v2float_60_50 = OpConstantComposite %v2float %float_60 %float_50
    %v2float_70_70 = OpConstantComposite %v2float %float_70 %float_70
  )";
  }
  
  std::string Preamble() {
    return Caps() + CommonTypes();
  }
  
  using SpvParserTest_Composite_Construct = SpvParserTest;
  
  TEST_F(SpvParserTest_Composite_Construct, Vector) {
    const auto assembly = Preamble() + R"(
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %1 = OpCompositeConstruct %v2uint %uint_10 %uint_20
       %2 = OpCompositeConstruct %v2int %int_30 %int_40
       %3 = OpCompositeConstruct %v2float %float_50 %float_60
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto ast_body = fe.ast_body();
    EXPECT_THAT(test::ToString(p->program(), ast_body),
                HasSubstr(R"(let x_1 : vec2<u32> = vec2<u32>(10u, 20u);
  let x_2 : vec2<i32> = vec2<i32>(30, 40);
  let x_3 : vec2<f32> = vec2<f32>(50.0, 60.0);
  )"));
  }
  
  TEST_F(SpvParserTest_Composite_Construct, Matrix) {
    const auto assembly = Preamble() + R"(
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %1 = OpCompositeConstruct %m3v2float %v2float_50_60 %v2float_60_50 %v2float_70_70
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto ast_body = fe.ast_body();
    EXPECT_THAT(test::ToString(p->program(), ast_body),
                HasSubstr("let x_1 : mat3x2<f32> = mat3x2<f32>("
                          "vec2<f32>(50.0, 60.0), "
                          "vec2<f32>(60.0, 50.0), "
                          "vec2<f32>(70.0, 70.0));"));
  }
  
  TEST_F(SpvParserTest_Composite_Construct, Array) {
    const auto assembly = Preamble() + R"(
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %1 = OpCompositeConstruct %a_u_5 %uint_10 %uint_20 %uint_3 %uint_4 %uint_5
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto ast_body = fe.ast_body();
    EXPECT_THAT(
        test::ToString(p->program(), ast_body),
        HasSubstr(
            "let x_1 : array<u32, 5u> = array<u32, 5u>(10u, 20u, 3u, 4u, 5u);"));
  }
  
  TEST_F(SpvParserTest_Composite_Construct, Struct) {
    const auto assembly = Preamble() + R"(
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %1 = OpCompositeConstruct %s_v2f_u_i %v2float_50_60 %uint_5 %int_30
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto ast_body = fe.ast_body();
    EXPECT_THAT(test::ToString(p->program(), ast_body),
                HasSubstr("let x_1 : S = S(vec2<f32>(50.0, 60.0), 5u, 30);"));
  }
  
  TEST_F(SpvParserTest_Composite_Construct,
         ConstantComposite_Struct_NoDeduplication) {
    const auto assembly = Preamble() + R"(
       %200 = OpTypeStruct %uint
       %300 = OpTypeStruct %uint ; isomorphic structures
  
       %201 = OpConstantComposite %200 %uint_10
       %301 = OpConstantComposite %300 %uint_10  ; isomorphic constants
  
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %2 = OpCopyObject %200 %201
       %3 = OpCopyObject %300 %301
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto ast_body = fe.ast_body();
    const auto got = test::ToString(p->program(), ast_body);
    const auto expected = std::string(
        R"(let x_2 : S_1 = S_1(10u);
  let x_3 : S_2 = S_2(10u);
  return;
  )");
    EXPECT_EQ(got, expected) << got;
  }
  
  using SpvParserTest_CompositeExtract = SpvParserTest;
  
  TEST_F(SpvParserTest_CompositeExtract, Vector) {
    const auto assembly = Preamble() + R"(
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %1 = OpCompositeExtract %float %v2float_50_60 1
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto ast_body = fe.ast_body();
    EXPECT_THAT(test::ToString(p->program(), ast_body),
                HasSubstr("let x_1 : f32 = vec2<f32>(50.0, 60.0).y;"));
  }
  
  TEST_F(SpvParserTest_CompositeExtract, Vector_IndexTooBigError) {
    const auto assembly = Preamble() + R"(
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %1 = OpCompositeExtract %float %v2float_50_60 900
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_FALSE(fe.EmitBody());
    EXPECT_THAT(p->error(), Eq("OpCompositeExtract %1 index value 900 is out of "
                               "bounds for vector of 2 elements"));
  }
  
  TEST_F(SpvParserTest_CompositeExtract, Matrix) {
    const auto assembly = Preamble() + R"(
       %ptr = OpTypePointer Function %m3v2float
  
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %var = OpVariable %ptr Function
       %1 = OpLoad %m3v2float %var
       %2 = OpCompositeExtract %v2float %1 2
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto ast_body = fe.ast_body();
    EXPECT_THAT(test::ToString(p->program(), ast_body),
                HasSubstr("let x_2 : vec2<f32> = x_1[2u];"));
  }
  
  TEST_F(SpvParserTest_CompositeExtract, Matrix_IndexTooBigError) {
    const auto assembly = Preamble() + R"(
       %ptr = OpTypePointer Function %m3v2float
  
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %var = OpVariable %ptr Function
       %1 = OpLoad %m3v2float %var
       %2 = OpCompositeExtract %v2float %1 3
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_FALSE(fe.EmitBody()) << p->error();
    EXPECT_THAT(p->error(), Eq("OpCompositeExtract %2 index value 3 is out of "
                               "bounds for matrix of 3 elements"));
  }
  
  TEST_F(SpvParserTest_CompositeExtract, Matrix_Vector) {
    const auto assembly = Preamble() + R"(
       %ptr = OpTypePointer Function %m3v2float
  
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %var = OpVariable %ptr Function
       %1 = OpLoad %m3v2float %var
       %2 = OpCompositeExtract %float %1 2 1
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto ast_body = fe.ast_body();
    EXPECT_THAT(test::ToString(p->program(), ast_body),
                HasSubstr("let x_2 : f32 = x_1[2u].y;"));
  }
  
  TEST_F(SpvParserTest_CompositeExtract, Array) {
    const auto assembly = Preamble() + R"(
       %ptr = OpTypePointer Function %a_u_5
  
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %var = OpVariable %ptr Function
       %1 = OpLoad %a_u_5 %var
       %2 = OpCompositeExtract %uint %1 3
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto ast_body = fe.ast_body();
    EXPECT_THAT(test::ToString(p->program(), ast_body),
                HasSubstr("let x_2 : u32 = x_1[3u];"));
  }
  
  TEST_F(SpvParserTest_CompositeExtract, RuntimeArray_IsError) {
    const auto assembly = Preamble() + R"(
       %rtarr = OpTypeRuntimeArray %uint
       %ptr = OpTypePointer Function %rtarr
  
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %var = OpVariable %ptr Function
       %1 = OpLoad %rtarr %var
       %2 = OpCompositeExtract %uint %1 3
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_FALSE(fe.EmitBody()) << p->error();
    EXPECT_THAT(p->error(),
                HasSubstr("can't do OpCompositeExtract on a runtime array: "));
  }
  
  TEST_F(SpvParserTest_CompositeExtract, Struct) {
    const auto assembly = Preamble() + R"(
       %ptr = OpTypePointer Function %s_v2f_u_i
  
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %var = OpVariable %ptr Function
       %1 = OpLoad %s_v2f_u_i %var
       %2 = OpCompositeExtract %int %1 2
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto ast_body = fe.ast_body();
    EXPECT_THAT(test::ToString(p->program(), ast_body),
                HasSubstr("let x_2 : i32 = x_1.field2;"));
  }
  
  TEST_F(SpvParserTest_CompositeExtract, Struct_DifferOnlyInMemberName) {
    const std::string assembly = R"(
       OpCapability Shader
       OpMemoryModel Logical Simple
       OpEntryPoint Fragment %100 "main"
       OpExecutionMode %100 OriginUpperLeft
  
       OpMemberName %s0 0 "algo"
       OpMemberName %s1 0 "rithm"
  
       %void = OpTypeVoid
       %voidfn = OpTypeFunction %void
  
       %uint = OpTypeInt 32 0
  
       %s0 = OpTypeStruct %uint
       %s1 = OpTypeStruct %uint
       %ptr0 = OpTypePointer Function %s0
       %ptr1 = OpTypePointer Function %s1
  
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %var0 = OpVariable %ptr0 Function
       %var1 = OpVariable %ptr1 Function
       %1 = OpLoad %s0 %var0
       %2 = OpCompositeExtract %uint %1 0
       %3 = OpLoad %s1 %var1
       %4 = OpCompositeExtract %uint %3 0
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto got = fe.ast_body();
    auto program = p->program();
    EXPECT_THAT(test::ToString(program, got),
                HasSubstr("let x_2 : u32 = x_1.algo;"))
        << test::ToString(program, got);
    EXPECT_THAT(test::ToString(program, got),
                HasSubstr("let x_4 : u32 = x_3.rithm;"))
        << test::ToString(program, got);
    p->SkipDumpingPending("crbug.com/tint/863");
  }
  
  TEST_F(SpvParserTest_CompositeExtract, Struct_IndexTooBigError) {
    const auto assembly = Preamble() + R"(
       %ptr = OpTypePointer Function %s_v2f_u_i
  
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %var = OpVariable %ptr Function
       %1 = OpLoad %s_v2f_u_i %var
       %2 = OpCompositeExtract %int %1 40
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_FALSE(fe.EmitBody());
    EXPECT_THAT(p->error(), Eq("OpCompositeExtract %2 index value 40 is out of "
                               "bounds for structure %26 having 3 members"));
  }
  
  TEST_F(SpvParserTest_CompositeExtract, Struct_Array_Matrix_Vector) {
    const auto assembly = Preamble() + R"(
       %a_mat = OpTypeArray %m3v2float %uint_3
       %s = OpTypeStruct %uint %a_mat
       %ptr = OpTypePointer Function %s
  
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %var = OpVariable %ptr Function
       %1 = OpLoad %s %var
       %2 = OpCompositeExtract %float %1 1 2 0 1
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto ast_body = fe.ast_body();
    EXPECT_THAT(test::ToString(p->program(), ast_body),
                HasSubstr("let x_2 : f32 = x_1.field1[2u][0u].y;"));
  }
  
  using SpvParserTest_CompositeInsert = SpvParserTest;
  
  TEST_F(SpvParserTest_CompositeInsert, Vector) {
    const auto assembly = Preamble() + R"(
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %1 = OpCompositeInsert %v2float %float_70 %v2float_50_60 1
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto ast_body = fe.ast_body();
    auto got = test::ToString(p->program(), ast_body);
    const auto* expected =
        R"(var x_1_1 : vec2<f32> = vec2<f32>(50.0, 60.0);
  x_1_1.y = 70.0;
  let x_1 : vec2<f32> = x_1_1;
  return;
  )";
    EXPECT_EQ(got, expected);
  }
  
  TEST_F(SpvParserTest_CompositeInsert, Vector_IndexTooBigError) {
    const auto assembly = Preamble() + R"(
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %1 = OpCompositeInsert %v2float %float_70 %v2float_50_60 900
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_FALSE(fe.EmitBody());
    EXPECT_THAT(p->error(), Eq("OpCompositeInsert %1 index value 900 is out of "
                               "bounds for vector of 2 elements"));
  }
  
  TEST_F(SpvParserTest_CompositeInsert, Matrix) {
    const auto assembly = Preamble() + R"(
       %ptr = OpTypePointer Function %m3v2float
  
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %var = OpVariable %ptr Function
       %1 = OpLoad %m3v2float %var
       %2 = OpCompositeInsert %m3v2float %v2float_50_60 %1 2
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto ast_body = fe.ast_body();
    auto body_str = test::ToString(p->program(), ast_body);
    EXPECT_THAT(body_str, HasSubstr(R"(var x_2_1 : mat3x2<f32> = x_1;
  x_2_1[2u] = vec2<f32>(50.0, 60.0);
  let x_2 : mat3x2<f32> = x_2_1;
  )")) << body_str;
  }
  
  TEST_F(SpvParserTest_CompositeInsert, Matrix_IndexTooBigError) {
    const auto assembly = Preamble() + R"(
       %ptr = OpTypePointer Function %m3v2float
  
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %var = OpVariable %ptr Function
       %1 = OpLoad %m3v2float %var
       %2 = OpCompositeInsert %m3v2float %v2float_50_60 %1 3
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_FALSE(fe.EmitBody()) << p->error();
    EXPECT_THAT(p->error(), Eq("OpCompositeInsert %2 index value 3 is out of "
                               "bounds for matrix of 3 elements"));
  }
  
  TEST_F(SpvParserTest_CompositeInsert, Matrix_Vector) {
    const auto assembly = Preamble() + R"(
       %ptr = OpTypePointer Function %m3v2float
  
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %var = OpVariable %ptr Function
       %1 = OpLoad %m3v2float %var
       %2 = OpCompositeInsert %m3v2float %v2float_50_60 %1 2
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto ast_body = fe.ast_body();
    auto body_str = test::ToString(p->program(), ast_body);
    EXPECT_THAT(body_str, HasSubstr(R"(var x_2_1 : mat3x2<f32> = x_1;
  x_2_1[2u] = vec2<f32>(50.0, 60.0);
  let x_2 : mat3x2<f32> = x_2_1;
  return;
  )")) << body_str;
  }
  
  TEST_F(SpvParserTest_CompositeInsert, Array) {
    const auto assembly = Preamble() + R"(
       %ptr = OpTypePointer Function %a_u_5
  
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %var = OpVariable %ptr Function
       %1 = OpLoad %a_u_5 %var
       %2 = OpCompositeInsert %a_u_5 %uint_20 %1 3
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto ast_body = fe.ast_body();
    auto body_str = test::ToString(p->program(), ast_body);
    EXPECT_THAT(body_str, HasSubstr(R"(var x_2_1 : array<u32, 5u> = x_1;
  x_2_1[3u] = 20u;
  let x_2 : array<u32, 5u> = x_2_1;
  )")) << body_str;
  }
  
  TEST_F(SpvParserTest_CompositeInsert, RuntimeArray_IsError) {
    const auto assembly = Preamble() + R"(
       %rtarr = OpTypeRuntimeArray %uint
       %ptr = OpTypePointer Function %rtarr
  
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %var = OpVariable %ptr Function
       %1 = OpLoad %rtarr %var
       %2 = OpCompositeInsert %rtarr %uint_20 %1 3
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_FALSE(fe.EmitBody()) << p->error();
    EXPECT_THAT(p->error(),
                HasSubstr("can't do OpCompositeInsert on a runtime array: "));
  }
  
  TEST_F(SpvParserTest_CompositeInsert, Struct) {
    const auto assembly = Preamble() + R"(
       %ptr = OpTypePointer Function %s_v2f_u_i
  
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %var = OpVariable %ptr Function
       %1 = OpLoad %s_v2f_u_i %var
       %2 = OpCompositeInsert %s_v2f_u_i %int_30 %1 2
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto ast_body = fe.ast_body();
    auto body_str = test::ToString(p->program(), ast_body);
    EXPECT_THAT(body_str, HasSubstr(R"(var x_35 : S;
  let x_1 : S = x_35;
  var x_2_1 : S = x_1;
  x_2_1.field2 = 30;
  let x_2 : S = x_2_1;
  )")) << body_str;
  }
  
  TEST_F(SpvParserTest_CompositeInsert, Struct_DifferOnlyInMemberName) {
    const std::string assembly = R"(
       OpCapability Shader
       OpMemoryModel Logical Simple
       OpEntryPoint Fragment %100 "main"
       OpExecutionMode %100 OriginUpperLeft
  
       OpName %var0 "var0"
       OpName %var1 "var1"
       OpMemberName %s0 0 "algo"
       OpMemberName %s1 0 "rithm"
  
       %void = OpTypeVoid
       %voidfn = OpTypeFunction %void
  
       %uint = OpTypeInt 32 0
       %uint_10 = OpConstant %uint 10
       %uint_11 = OpConstant %uint 11
  
       %s0 = OpTypeStruct %uint
       %s1 = OpTypeStruct %uint
       %ptr0 = OpTypePointer Function %s0
       %ptr1 = OpTypePointer Function %s1
  
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %var0 = OpVariable %ptr0 Function
       %var1 = OpVariable %ptr1 Function
       %1 = OpLoad %s0 %var0
       %2 = OpCompositeInsert %s0 %uint_10 %1 0
       %3 = OpLoad %s1 %var1
       %4 = OpCompositeInsert %s1 %uint_11 %3 0
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto ast_body = fe.ast_body();
    const auto got = test::ToString(p->program(), ast_body);
    const std::string expected = R"(var var0 : S;
  var var1 : S_1;
  let x_1 : S = var0;
  var x_2_1 : S = x_1;
  x_2_1.algo = 10u;
  let x_2 : S = x_2_1;
  let x_3 : S_1 = var1;
  var x_4_1 : S_1 = x_3;
  x_4_1.rithm = 11u;
  let x_4 : S_1 = x_4_1;
  return;
  )";
    EXPECT_EQ(got, expected) << got;
  }
  
  TEST_F(SpvParserTest_CompositeInsert, Struct_IndexTooBigError) {
    const auto assembly = Preamble() + R"(
       %ptr = OpTypePointer Function %s_v2f_u_i
  
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %var = OpVariable %ptr Function
       %1 = OpLoad %s_v2f_u_i %var
       %2 = OpCompositeInsert %s_v2f_u_i %uint_10 %1 40
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_FALSE(fe.EmitBody());
    EXPECT_THAT(p->error(), Eq("OpCompositeInsert %2 index value 40 is out of "
                               "bounds for structure %26 having 3 members"));
  }
  
  TEST_F(SpvParserTest_CompositeInsert, Struct_Array_Matrix_Vector) {
    const auto assembly = Preamble() + R"(
       %a_mat = OpTypeArray %m3v2float %uint_3
       %s = OpTypeStruct %uint %a_mat
       %ptr = OpTypePointer Function %s
  
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %var = OpVariable %ptr Function
       %1 = OpLoad %s %var
       %2 = OpCompositeInsert %s %float_70 %1 1 2 0 1
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto ast_body = fe.ast_body();
    auto body_str = test::ToString(p->program(), ast_body);
    EXPECT_THAT(body_str, HasSubstr(R"(var x_37 : S_1;
  let x_1 : S_1 = x_37;
  var x_2_1 : S_1 = x_1;
  x_2_1.field1[2u][0u].y = 70.0;
  let x_2 : S_1 = x_2_1;
  )")) << body_str;
  }
  
  using SpvParserTest_CopyObject = SpvParserTest;
  
  TEST_F(SpvParserTest_CopyObject, Scalar) {
    const auto assembly = Preamble() + R"(
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %1 = OpCopyObject %uint %uint_3
       %2 = OpCopyObject %uint %1
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto ast_body = fe.ast_body();
    EXPECT_THAT(test::ToString(p->program(), ast_body),
                HasSubstr(R"(let x_1 : u32 = 3u;
  let x_2 : u32 = x_1;
  )"));
  }
  
  TEST_F(SpvParserTest_CopyObject, Pointer) {
    const auto assembly = Preamble() + R"(
       %ptr = OpTypePointer Function %uint
  
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %10 = OpVariable %ptr Function
       %1 = OpCopyObject %ptr %10
       %2 = OpCopyObject %ptr %1
       OpReturn
       OpFunctionEnd
    )";
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto ast_body = fe.ast_body();
    EXPECT_THAT(test::ToString(p->program(), ast_body),
                HasSubstr(R"(let x_1 : ptr<function, u32> = &(x_10);
  let x_2 : ptr<function, u32> = x_1;
  )"));
  }
  
  using SpvParserTest_VectorShuffle = SpvParserTest;
  
  TEST_F(SpvParserTest_VectorShuffle, FunctionScopeOperands_UseBoth) {
    // Note that variables are generated for the vector operands.
    const auto assembly = Preamble() + R"(
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %1 = OpCopyObject %v2uint %v2uint_3_4
       %2 = OpIAdd %v2uint %v2uint_4_3 %v2uint_3_4
       %10 = OpVectorShuffle %v4uint %1 %2 3 2 1 0
       OpReturn
       OpFunctionEnd
  )";
  
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto ast_body = fe.ast_body();
    EXPECT_THAT(
        test::ToString(p->program(), ast_body),
        HasSubstr(
            "let x_10 : vec4<u32> = vec4<u32>(x_2.y, x_2.x, x_1.y, x_1.x);"));
  }
  
  TEST_F(SpvParserTest_VectorShuffle, ConstantOperands_UseBoth) {
    const auto assembly = Preamble() + R"(
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %10 = OpVectorShuffle %v4uint %v2uint_3_4 %v2uint_4_3 3 2 1 0
       OpReturn
       OpFunctionEnd
  )";
  
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto ast_body = fe.ast_body();
    EXPECT_THAT(test::ToString(p->program(), ast_body),
                HasSubstr("let x_10 : vec4<u32> = vec4<u32>("
                          "vec2<u32>(4u, 3u).y, "
                          "vec2<u32>(4u, 3u).x, "
                          "vec2<u32>(3u, 4u).y, "
                          "vec2<u32>(3u, 4u).x);"));
  }
  
  TEST_F(SpvParserTest_VectorShuffle, ConstantOperands_AllOnesMapToNull) {
    const auto assembly = Preamble() + R"(
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %1 = OpCopyObject %v2uint %v2uint_4_3
       %10 = OpVectorShuffle %v2uint %1 %1 0xFFFFFFFF 1
       OpReturn
       OpFunctionEnd
  )";
  
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto ast_body = fe.ast_body();
    EXPECT_THAT(test::ToString(p->program(), ast_body),
                HasSubstr("let x_10 : vec2<u32> = vec2<u32>(0u, x_1.y);"));
  }
  
  TEST_F(SpvParserTest_VectorShuffle,
         FunctionScopeOperands_MixedInputOperandSizes) {
    // Note that variables are generated for the vector operands.
    const auto assembly = Preamble() + R"(
       %v3uint_3_4_5 = OpConstantComposite %v3uint %uint_3 %uint_4 %uint_5
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %1 = OpCopyObject %v2uint %v2uint_3_4
       %3 = OpCopyObject %v3uint %v3uint_3_4_5
       %10 = OpVectorShuffle %v2uint %1 %3 1 4
       OpReturn
       OpFunctionEnd
  )";
  
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto ast_body = fe.ast_body();
    EXPECT_THAT(test::ToString(p->program(), ast_body),
                HasSubstr("let x_10 : vec2<u32> = vec2<u32>(x_1.y, x_3.z);"));
  }
  
  TEST_F(SpvParserTest_VectorShuffle, IndexTooBig_IsError) {
    const auto assembly = Preamble() + R"(
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %10 = OpVectorShuffle %v4uint %v2uint_3_4 %v2uint_4_3 9 2 1 0
       OpReturn
       OpFunctionEnd
  )";
  
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_FALSE(fe.EmitBody()) << p->error();
    EXPECT_THAT(p->error(),
                Eq("invalid vectorshuffle ID %10: index too large: 9"));
  }
  
  using SpvParserTest_VectorExtractDynamic = SpvParserTest;
  
  TEST_F(SpvParserTest_VectorExtractDynamic, SignedIndex) {
    const auto assembly = Preamble() + R"(
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %1 = OpCopyObject %v2uint %v2uint_3_4
       %2 = OpCopyObject %int %int_1
       %10 = OpVectorExtractDynamic %uint %1 %2
       OpReturn
       OpFunctionEnd
  )";
  
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto ast_body = fe.ast_body();
    const auto got = test::ToString(p->program(), ast_body);
    EXPECT_THAT(got, HasSubstr("let x_10 : u32 = x_1[x_2];")) << got;
  }
  
  TEST_F(SpvParserTest_VectorExtractDynamic, UnsignedIndex) {
    const auto assembly = Preamble() + R"(
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %1 = OpCopyObject %v2uint %v2uint_3_4
       %2 = OpCopyObject %uint %uint_3
       %10 = OpVectorExtractDynamic %uint %1 %2
       OpReturn
       OpFunctionEnd
  )";
  
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto ast_body = fe.ast_body();
    const auto got = test::ToString(p->program(), ast_body);
    EXPECT_THAT(got, HasSubstr("let x_10 : u32 = x_1[x_2];")) << got;
  }
  
  using SpvParserTest_VectorInsertDynamic = SpvParserTest;
  
  TEST_F(SpvParserTest_VectorInsertDynamic, Sample) {
    const auto assembly = Preamble() + R"(
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %1 = OpCopyObject %v2uint %v2uint_3_4
       %2 = OpCopyObject %uint %uint_3
       %3 = OpCopyObject %int %int_1
       %10 = OpVectorInsertDynamic %v2uint %1 %2 %3
       OpReturn
       OpFunctionEnd
  )";
  
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.EmitBody()) << p->error();
    auto ast_body = fe.ast_body();
    const auto got = test::ToString(p->program(), ast_body);
    EXPECT_THAT(got, HasSubstr(R"(var x_10_1 : vec2<u32> = x_1;
  x_10_1[x_3] = x_2;
  let x_10 : vec2<u32> = x_10_1;
  )")) << got
       << assembly;
  }
  
  TEST_F(SpvParserTest, DISABLED_WorkgroupSize_Overridable) {
    // TODO(dneto): Support specializable workgroup size. crbug.com/tint/504
    const auto* assembly = R"(
    OpCapability Shader
    OpMemoryModel Logical Simple
    OpEntryPoint GLCompute %100 "main"
    OpDecorate %1 BuiltIn WorkgroupSize
    OpDecorate %uint_2 SpecId 0
    OpDecorate %uint_4 SpecId 1
    OpDecorate %uint_8 SpecId 2
  
    %uint = OpTypeInt 32 0
    %uint_2 = OpSpecConstant %uint 2
    %uint_4 = OpSpecConstant %uint 4
    %uint_8 = OpSpecConstant %uint 8
    %v3uint = OpTypeVector %uint 3
    %1 = OpSpecConstantComposite %v3uint %uint_2 %uint_4 %uint_8
    %void = OpTypeVoid
    %voidfn = OpTypeFunction %void
  
       %100 = OpFunction %void None %voidfn
       %entry = OpLabel
       %10 = OpCopyObject %v3uint %1
       %11 = OpCopyObject %uint %uint_2
       %12 = OpCopyObject %uint %uint_4
       %13 = OpCopyObject %uint %uint_8
       OpReturn
       OpFunctionEnd
  )";
  
    auto p = parser(test::Assemble(assembly));
    ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
    auto fe = p->function_emitter(100);
    EXPECT_TRUE(fe.Emit()) << p->error();
    const auto got = test::ToString(p->program());
    EXPECT_THAT(got, HasSubstr(R"(
    VariableConst{
      Decorations{
        OverrideDecoration{0}
      }
      x_2
      none
      __u32
      {
        ScalarConstructor[not set]{2}
      }
    }
    VariableConst{
      Decorations{
        OverrideDecoration{1}
      }
      x_3
      none
      __u32
      {
        ScalarConstructor[not set]{4}
      }
    }
    VariableConst{
      Decorations{
        OverrideDecoration{2}
      }
      x_4
      none
      __u32
      {
        ScalarConstructor[not set]{8}
      }
    }
  )")) << got;
    EXPECT_THAT(got, HasSubstr(R"(
      VariableDeclStatement{
        VariableConst{
          x_10
          none
          __vec_3__u32
          {
            TypeConstructor[not set]{
              __vec_3__u32
              ScalarConstructor[not set]{2}
              ScalarConstructor[not set]{4}
              ScalarConstructor[not set]{8}
            }
          }
        }
      }
      VariableDeclStatement{
        VariableConst{
          x_11
          none
          __u32
          {
            Identifier[not set]{x_2}
          }
        }
      }
      VariableDeclStatement{
        VariableConst{
          x_12
          none
          __u32
          {
            Identifier[not set]{x_3}
          }
        }
      }
      VariableDeclStatement{
        VariableConst{
          x_13
          none
          __u32
          {
            Identifier[not set]{x_4}
          }
        }
      })"))
        << got << assembly;
  }
  
  }  // namespace
  }  // namespace spirv
  }  // namespace reader
  }  // namespace tint
  