[spirv-reader] Support null vector, matrix, arr, struct

Bug: tint:3
Change-Id: I2fa25e5d28b965de4a419719e37bc999be8489ea
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/21581
Reviewed-by: dan sinclair <dsinclair@google.com>
diff --git a/src/reader/spirv/parser_impl.cc b/src/reader/spirv/parser_impl.cc
index 7016e5e..a09401c 100644
--- a/src/reader/spirv/parser_impl.cc
+++ b/src/reader/spirv/parser_impl.cc
@@ -914,11 +914,87 @@
     return {ast_type, std::make_unique<ast::TypeConstructorExpression>(
                           ast_type, std::move(ast_components))};
   }
+  auto* spirv_null_const = spirv_const->AsNullConstant();
+  if (spirv_null_const != nullptr) {
+    return {ast_type, MakeNullValue(ast_type)};
+  }
   Fail() << "Unhandled constant type " << inst->type_id() << " for value ID "
          << id;
   return {};
 }
 
+std::unique_ptr<ast::Expression> ParserImpl::MakeNullValue(
+    ast::type::Type* type) {
+  // TODO(dneto): Use the no-operands constructor syntax when it becomes
+  // available in Tint.
+  // https://github.com/gpuweb/gpuweb/issues/685
+  // https://bugs.chromium.org/p/tint/issues/detail?id=34
+
+  if (!type) {
+    Fail() << "trying to create null value for a null type";
+    return nullptr;
+  }
+
+  if (type->IsBool()) {
+    return std::make_unique<ast::ScalarConstructorExpression>(
+        std::make_unique<ast::BoolLiteral>(type, false));
+  }
+  if (type->IsU32()) {
+    return std::make_unique<ast::ScalarConstructorExpression>(
+        std::make_unique<ast::UintLiteral>(type, 0u));
+  }
+  if (type->IsI32()) {
+    return std::make_unique<ast::ScalarConstructorExpression>(
+        std::make_unique<ast::IntLiteral>(type, 0));
+  }
+  if (type->IsF32()) {
+    return std::make_unique<ast::ScalarConstructorExpression>(
+        std::make_unique<ast::FloatLiteral>(type, 0.0f));
+  }
+  if (type->IsVector()) {
+    const auto* vec_ty = type->AsVector();
+    ast::ExpressionList ast_components;
+    for (size_t i = 0; i < vec_ty->size(); ++i) {
+      ast_components.emplace_back(MakeNullValue(vec_ty->type()));
+    }
+    return std::make_unique<ast::TypeConstructorExpression>(
+        type, std::move(ast_components));
+  }
+  if (type->IsMatrix()) {
+    const auto* mat_ty = type->AsMatrix();
+    // Matrix components are columns
+    auto* column_ty =
+        ctx_.type_mgr().Get(std::make_unique<ast::type::VectorType>(
+            mat_ty->type(), mat_ty->rows()));
+    ast::ExpressionList ast_components;
+    for (size_t i = 0; i < mat_ty->columns(); ++i) {
+      ast_components.emplace_back(MakeNullValue(column_ty));
+    }
+    return std::make_unique<ast::TypeConstructorExpression>(
+        type, std::move(ast_components));
+  }
+  if (type->IsArray()) {
+    auto* arr_ty = type->AsArray();
+    ast::ExpressionList ast_components;
+    for (size_t i = 0; i < arr_ty->size(); ++i) {
+      ast_components.emplace_back(MakeNullValue(arr_ty->type()));
+    }
+    return std::make_unique<ast::TypeConstructorExpression>(
+        type, std::move(ast_components));
+  }
+  if (type->IsStruct()) {
+    auto* struct_ty = type->AsStruct();
+    ast::ExpressionList ast_components;
+    for (auto& member : struct_ty->impl()->members()) {
+      ast_components.emplace_back(MakeNullValue(member->type()));
+    }
+    return std::make_unique<ast::TypeConstructorExpression>(
+        type, std::move(ast_components));
+  }
+  Fail() << "can't make null value for type: " << type->type_name();
+  return nullptr;
+}
+
 TypedExpression ParserImpl::RectifyOperandSignedness(SpvOp op,
                                                      TypedExpression&& expr) {
   const bool requires_signed = AssumesSignedOperands(op);
diff --git a/src/reader/spirv/parser_impl.h b/src/reader/spirv/parser_impl.h
index 743a5b2..554fe27 100644
--- a/src/reader/spirv/parser_impl.h
+++ b/src/reader/spirv/parser_impl.h
@@ -244,9 +244,14 @@
 
   /// Creates an AST expression node for a SPIR-V constant.
   /// @param id the SPIR-V ID of the constant
-  /// @returns a new Literal node
+  /// @returns a new expression
   TypedExpression MakeConstantExpression(uint32_t id);
 
+  /// Creates an AST expression node for the null value for the given type.
+  /// @param type the AST type
+  /// @returns a new expression
+  std::unique_ptr<ast::Expression> MakeNullValue(ast::type::Type* type);
+
   /// Converts a given expression to the signedness demanded for an operand
   /// of the given SPIR-V opcode, if required.  If the operation assumes
   /// signed integer operands, and |expr| is unsigned, then return an
diff --git a/src/reader/spirv/parser_impl_module_var_test.cc b/src/reader/spirv/parser_impl_module_var_test.cc
index eb60d25..d46ab0a 100644
--- a/src/reader/spirv/parser_impl_module_var_test.cc
+++ b/src/reader/spirv/parser_impl_module_var_test.cc
@@ -50,6 +50,9 @@
     %int_m1 = OpConstant %int -1
     %uint_2 = OpConstant %uint 2
 
+    %v2bool = OpTypeVector %bool 2
+    %v2uint = OpTypeVector %uint 2
+    %v2int = OpTypeVector %int 2
     %v2float = OpTypeVector %float 2
     %m3v2float = OpTypeMatrix %v2float 3
 
@@ -300,6 +303,98 @@
   })"));
 }
 
+TEST_F(SpvParserTest, ModuleScopeVar_VectorBoolNullInitializer) {
+  auto* p = parser(test::Assemble(CommonTypes() + R"(
+     %ptr = OpTypePointer Private %v2bool
+     %const = OpConstantNull %v2bool
+     %200 = OpVariable %ptr Private %const
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  EXPECT_TRUE(p->error().empty());
+  const auto module_str = p->module().to_str();
+  EXPECT_THAT(module_str, HasSubstr(R"(Variable{
+    x_200
+    private
+    __vec_2__bool
+    {
+      TypeConstructor{
+        __vec_2__bool
+        ScalarConstructor{false}
+        ScalarConstructor{false}
+      }
+    }
+  })"));
+}
+
+TEST_F(SpvParserTest, ModuleScopeVar_VectorUintNullInitializer) {
+  auto* p = parser(test::Assemble(CommonTypes() + R"(
+     %ptr = OpTypePointer Private %v2uint
+     %const = OpConstantNull %v2uint
+     %200 = OpVariable %ptr Private %const
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  EXPECT_TRUE(p->error().empty());
+  const auto module_str = p->module().to_str();
+  EXPECT_THAT(module_str, HasSubstr(R"(Variable{
+    x_200
+    private
+    __vec_2__u32
+    {
+      TypeConstructor{
+        __vec_2__u32
+        ScalarConstructor{0}
+        ScalarConstructor{0}
+      }
+    }
+  })"));
+}
+
+TEST_F(SpvParserTest, ModuleScopeVar_VectorIntNullInitializer) {
+  auto* p = parser(test::Assemble(CommonTypes() + R"(
+     %ptr = OpTypePointer Private %v2int
+     %const = OpConstantNull %v2int
+     %200 = OpVariable %ptr Private %const
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  EXPECT_TRUE(p->error().empty());
+  const auto module_str = p->module().to_str();
+  EXPECT_THAT(module_str, HasSubstr(R"(Variable{
+    x_200
+    private
+    __vec_2__i32
+    {
+      TypeConstructor{
+        __vec_2__i32
+        ScalarConstructor{0}
+        ScalarConstructor{0}
+      }
+    }
+  })"));
+}
+
+TEST_F(SpvParserTest, ModuleScopeVar_VectorFloatNullInitializer) {
+  auto* p = parser(test::Assemble(CommonTypes() + R"(
+     %ptr = OpTypePointer Private %v2float
+     %const = OpConstantNull %v2float
+     %200 = OpVariable %ptr Private %const
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  EXPECT_TRUE(p->error().empty());
+  const auto module_str = p->module().to_str();
+  EXPECT_THAT(module_str, HasSubstr(R"(Variable{
+    x_200
+    private
+    __vec_2__f32
+    {
+      TypeConstructor{
+        __vec_2__f32
+        ScalarConstructor{0.000000}
+        ScalarConstructor{0.000000}
+      }
+    }
+  })"));
+}
+
 TEST_F(SpvParserTest, ModuleScopeVar_MatrixInitializer) {
   auto* p = parser(test::Assemble(CommonTypes() + R"(
      %ptr = OpTypePointer Private %m3v2float
@@ -342,6 +437,42 @@
   })"));
 }
 
+TEST_F(SpvParserTest, ModuleScopeVar_MatrixNullInitializer) {
+  auto* p = parser(test::Assemble(CommonTypes() + R"(
+     %ptr = OpTypePointer Private %m3v2float
+     %const = OpConstantNull %m3v2float
+     %200 = OpVariable %ptr Private %const
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  EXPECT_TRUE(p->error().empty());
+  const auto module_str = p->module().to_str();
+  EXPECT_THAT(module_str, HasSubstr(R"(Variable{
+    x_200
+    private
+    __mat_2_3__f32
+    {
+      TypeConstructor{
+        __mat_2_3__f32
+        TypeConstructor{
+          __vec_2__f32
+          ScalarConstructor{0.000000}
+          ScalarConstructor{0.000000}
+        }
+        TypeConstructor{
+          __vec_2__f32
+          ScalarConstructor{0.000000}
+          ScalarConstructor{0.000000}
+        }
+        TypeConstructor{
+          __vec_2__f32
+          ScalarConstructor{0.000000}
+          ScalarConstructor{0.000000}
+        }
+      }
+    }
+  })"));
+}
+
 TEST_F(SpvParserTest, ModuleScopeVar_ArrayInitializer) {
   auto* p = parser(test::Assemble(CommonTypes() + R"(
      %ptr = OpTypePointer Private %arr2uint
@@ -366,6 +497,29 @@
   })"));
 }
 
+TEST_F(SpvParserTest, ModuleScopeVar_ArrayNullInitializer) {
+  auto* p = parser(test::Assemble(CommonTypes() + R"(
+     %ptr = OpTypePointer Private %arr2uint
+     %const = OpConstantNull %arr2uint
+     %200 = OpVariable %ptr Private %const
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  EXPECT_TRUE(p->error().empty());
+  const auto module_str = p->module().to_str();
+  EXPECT_THAT(module_str, HasSubstr(R"(Variable{
+    x_200
+    private
+    __array__u32_2
+    {
+      TypeConstructor{
+        __array__u32_2
+        ScalarConstructor{0}
+        ScalarConstructor{0}
+      }
+    }
+  })"));
+}
+
 TEST_F(SpvParserTest, ModuleScopeVar_StructInitializer) {
   auto* p = parser(test::Assemble(CommonTypes() + R"(
      %ptr = OpTypePointer Private %strct
@@ -396,6 +550,34 @@
   })"));
 }
 
+TEST_F(SpvParserTest, ModuleScopeVar_StructNullInitializer) {
+  auto* p = parser(test::Assemble(CommonTypes() + R"(
+     %ptr = OpTypePointer Private %strct
+     %const = OpConstantNull %strct
+     %200 = OpVariable %ptr Private %const
+  )"));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+  EXPECT_TRUE(p->error().empty());
+  const auto module_str = p->module().to_str();
+  EXPECT_THAT(module_str, HasSubstr(R"(Variable{
+    x_200
+    private
+    __struct_S
+    {
+      TypeConstructor{
+        __struct_S
+        ScalarConstructor{0}
+        ScalarConstructor{0.000000}
+        TypeConstructor{
+          __array__u32_2
+          ScalarConstructor{0}
+          ScalarConstructor{0}
+        }
+      }
+    }
+  })"));
+}
+
 }  // namespace
 }  // namespace spirv
 }  // namespace reader