validator: add IsStorable helper function

Bug: tint:419
Change-Id: Ib15aa819a032d8412fb3bc162b29a02c331f56b8
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/37960
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: David Neto <dneto@google.com>
Auto-Submit: David Neto <dneto@google.com>
diff --git a/src/validator/validator_impl.cc b/src/validator/validator_impl.cc
index 5aa6f0a..3461e51 100644
--- a/src/validator/validator_impl.cc
+++ b/src/validator/validator_impl.cc
@@ -29,8 +29,10 @@
 #include "src/ast/switch_statement.h"
 #include "src/ast/type/array_type.h"
 #include "src/ast/type/i32_type.h"
+#include "src/ast/type/matrix_type.h"
 #include "src/ast/type/struct_type.h"
 #include "src/ast/type/u32_type.h"
+#include "src/ast/type/vector_type.h"
 #include "src/ast/type/void_type.h"
 #include "src/ast/uint_literal.h"
 #include "src/ast/variable_decl_statement.h"
@@ -506,4 +508,29 @@
   return true;
 }
 
+bool ValidatorImpl::IsStorable(ast::type::Type* type) {
+  if (type == nullptr) {
+    return false;
+  }
+  if (type->is_scalar() || type->Is<ast::type::Vector>() ||
+      type->Is<ast::type::Matrix>()) {
+    return true;
+  }
+  if (ast::type::Array* array_type = type->As<ast::type::Array>()) {
+    return IsStorable(array_type->type());
+  }
+  if (ast::type::Struct* struct_type = type->As<ast::type::Struct>()) {
+    for (const auto* member : struct_type->impl()->members()) {
+      if (!IsStorable(member->type())) {
+        return false;
+      }
+    }
+    return true;
+  }
+  if (ast::type::Alias* alias_type = type->As<ast::type::Alias>()) {
+    return IsStorable(alias_type->type());
+  }
+  return false;
+}
+
 }  // namespace tint
diff --git a/src/validator/validator_impl.h b/src/validator/validator_impl.h
index 0a77654..d619b82 100644
--- a/src/validator/validator_impl.h
+++ b/src/validator/validator_impl.h
@@ -17,6 +17,7 @@
 
 #include <string>
 #include <unordered_map>
+#include <unordered_set>
 #include <vector>
 
 #include "src/ast/assignment_statement.h"
@@ -147,6 +148,12 @@
   bool ValidateConstructedTypes(
       const std::vector<ast::type::Type*>& constructed_types);
 
+  /// Returns true if the given type is storable. This uses and
+  /// updates `storable_` and `not_storable_`.
+  /// @param type the given type
+  /// @returns true if the given type is storable.
+  bool IsStorable(ast::type::Type* type);
+
  private:
   const ast::Module& module_;
   diag::List diags_;
diff --git a/src/validator/validator_test.cc b/src/validator/validator_test.cc
index eacc3e3..1aa10f5 100644
--- a/src/validator/validator_test.cc
+++ b/src/validator/validator_test.cc
@@ -579,5 +579,86 @@
   EXPECT_TRUE(v()->ValidateStatements(body)) << v()->error();
 }
 
+TEST_F(ValidatorTest, IsStorable_Void) {
+  EXPECT_FALSE(v()->IsStorable(ty.void_));
+}
+
+TEST_F(ValidatorTest, IsStorable_Scalar) {
+  EXPECT_TRUE(v()->IsStorable(ty.bool_));
+  EXPECT_TRUE(v()->IsStorable(ty.i32));
+  EXPECT_TRUE(v()->IsStorable(ty.u32));
+  EXPECT_TRUE(v()->IsStorable(ty.f32));
+}
+
+TEST_F(ValidatorTest, IsStorable_Vector) {
+  EXPECT_TRUE(v()->IsStorable(ty.vec2<int>()));
+  EXPECT_TRUE(v()->IsStorable(ty.vec3<int>()));
+  EXPECT_TRUE(v()->IsStorable(ty.vec4<int>()));
+  EXPECT_TRUE(v()->IsStorable(ty.vec2<unsigned>()));
+  EXPECT_TRUE(v()->IsStorable(ty.vec3<unsigned>()));
+  EXPECT_TRUE(v()->IsStorable(ty.vec4<unsigned>()));
+  EXPECT_TRUE(v()->IsStorable(ty.vec2<float>()));
+  EXPECT_TRUE(v()->IsStorable(ty.vec3<float>()));
+  EXPECT_TRUE(v()->IsStorable(ty.vec4<float>()));
+}
+
+TEST_F(ValidatorTest, IsStorable_Matrix) {
+  EXPECT_TRUE(v()->IsStorable(ty.mat2x2<float>()));
+  EXPECT_TRUE(v()->IsStorable(ty.mat2x3<float>()));
+  EXPECT_TRUE(v()->IsStorable(ty.mat2x4<float>()));
+  EXPECT_TRUE(v()->IsStorable(ty.mat3x2<float>()));
+  EXPECT_TRUE(v()->IsStorable(ty.mat3x3<float>()));
+  EXPECT_TRUE(v()->IsStorable(ty.mat3x4<float>()));
+  EXPECT_TRUE(v()->IsStorable(ty.mat4x2<float>()));
+  EXPECT_TRUE(v()->IsStorable(ty.mat4x3<float>()));
+  EXPECT_TRUE(v()->IsStorable(ty.mat4x4<float>()));
+}
+
+TEST_F(ValidatorTest, IsStorable_Pointer) {
+  auto* ptr_ty = ty.pointer<int>(ast::StorageClass::kPrivate);
+  EXPECT_FALSE(v()->IsStorable(ptr_ty));
+}
+
+TEST_F(ValidatorTest, IsStorable_AliasVoid) {
+  auto* alias = ty.alias("myalias", ty.void_);
+  EXPECT_FALSE(v()->IsStorable(alias));
+}
+
+TEST_F(ValidatorTest, IsStorable_AliasI32) {
+  auto* alias = ty.alias("myalias", ty.i32);
+  EXPECT_TRUE(v()->IsStorable(alias));
+}
+
+TEST_F(ValidatorTest, IsStorable_ArraySizedOfStorable) {
+  EXPECT_TRUE(v()->IsStorable(ty.array(ty.i32, 5)));
+}
+
+TEST_F(ValidatorTest, IsStorable_ArraySizedOfNonStorable) {
+  EXPECT_FALSE(v()->IsStorable(ty.array(ty.void_, 5)));
+}
+
+TEST_F(ValidatorTest, IsStorable_ArrayUnsizedOfStorable) {
+  EXPECT_TRUE(v()->IsStorable(ty.array<int>()));
+}
+
+TEST_F(ValidatorTest, IsStorable_ArrayUnsizedOfNonStorable) {
+  EXPECT_FALSE(v()->IsStorable(ty.array<void>()));
+}
+
+TEST_F(ValidatorTest, IsStorable_Struct_AllMembersStorable) {
+  ast::StructMemberList members{Member("a", ty.i32), Member("b", ty.f32)};
+  auto* s = create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
+  auto* s_ty = ty.struct_("mystruct", s);
+  EXPECT_TRUE(v()->IsStorable(s_ty));
+}
+
+TEST_F(ValidatorTest, IsStorable_Struct_SomeMembersNonStorable) {
+  auto* ptr_ty = ty.pointer<int>(ast::StorageClass::kPrivate);
+  ast::StructMemberList members{Member("a", ty.i32), Member("b", ptr_ty)};
+  auto* s = create<ast::Struct>(Source{}, members, ast::StructDecorationList{});
+  auto* s_ty = ty.struct_("mystruct", s);
+  EXPECT_FALSE(v()->IsStorable(s_ty));
+}
+
 }  // namespace
 }  // namespace tint