Resolver: make IsConstructible non-recursive for arrays and struct members

Also fix cases of implicit conversions of bool to int when creating
sem::Array.

Bug: tint:917
Change-Id: I5392fb737efc410f039b4dbd96cffc5daa4fd3a2
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/58783
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/intrinsic_table_test.cc b/src/intrinsic_table_test.cc
index 5ff0654..2635312 100644
--- a/src/intrinsic_table_test.cc
+++ b/src/intrinsic_table_test.cc
@@ -215,7 +215,7 @@
 }
 
 TEST_F(IntrinsicTableTest, MatchArray) {
-  auto* arr = create<sem::Array>(create<sem::U32>(), 0, 4, 4, 4, true);
+  auto* arr = create<sem::Array>(create<sem::U32>(), 0, 4, 4, 4, 4);
   auto* arr_ptr = create<sem::Pointer>(arr, ast::StorageClass::kStorage,
                                        ast::Access::kReadWrite);
   auto* result =
diff --git a/src/resolver/inferred_type_test.cc b/src/resolver/inferred_type_test.cc
index 789964b..5bd957b 100644
--- a/src/resolver/inferred_type_test.cc
+++ b/src/resolver/inferred_type_test.cc
@@ -142,7 +142,7 @@
 TEST_F(ResolverInferredTypeTest, InferArray_Pass) {
   auto* type = ty.array(ty.u32(), 10);
   auto* expected_type =
-      create<sem::Array>(create<sem::U32>(), 10, 4, 4 * 10, 4, true);
+      create<sem::Array>(create<sem::U32>(), 10, 4, 4 * 10, 4, 4);
 
   auto* ctor_expr = Construct(type);
   auto* var = Var("a", nullptr, ast::StorageClass::kFunction, ctor_expr);
diff --git a/src/resolver/is_host_shareable_test.cc b/src/resolver/is_host_shareable_test.cc
index 5da1626..804d8ee 100644
--- a/src/resolver/is_host_shareable_test.cc
+++ b/src/resolver/is_host_shareable_test.cc
@@ -99,12 +99,12 @@
 }
 
 TEST_F(ResolverIsHostShareable, ArraySizedOfHostShareable) {
-  auto* arr = create<sem::Array>(create<sem::I32>(), 5, 4, 20, 4, true);
+  auto* arr = create<sem::Array>(create<sem::I32>(), 5, 4, 20, 4, 4);
   EXPECT_TRUE(r()->IsHostShareable(arr));
 }
 
 TEST_F(ResolverIsHostShareable, ArrayUnsizedOfHostShareable) {
-  auto* arr = create<sem::Array>(create<sem::I32>(), 0, 4, 4, 4, true);
+  auto* arr = create<sem::Array>(create<sem::I32>(), 0, 4, 4, 4, 4);
   EXPECT_TRUE(r()->IsHostShareable(arr));
 }
 
diff --git a/src/resolver/is_storeable_test.cc b/src/resolver/is_storeable_test.cc
index e1495c1..f084d27 100644
--- a/src/resolver/is_storeable_test.cc
+++ b/src/resolver/is_storeable_test.cc
@@ -74,12 +74,12 @@
 }
 
 TEST_F(ResolverIsStorableTest, ArraySizedOfStorable) {
-  auto* arr = create<sem::Array>(create<sem::I32>(), 5, 4, 20, 4, true);
+  auto* arr = create<sem::Array>(create<sem::I32>(), 5, 4, 20, 4, 4);
   EXPECT_TRUE(r()->IsStorable(arr));
 }
 
 TEST_F(ResolverIsStorableTest, ArrayUnsizedOfStorable) {
-  auto* arr = create<sem::Array>(create<sem::I32>(), 0, 4, 4, 4, true);
+  auto* arr = create<sem::Array>(create<sem::I32>(), 0, 4, 4, 4, 4);
   EXPECT_TRUE(r()->IsStorable(arr));
 }
 
diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc
index e485c05..35812c4 100644
--- a/src/resolver/resolver.cc
+++ b/src/resolver/resolver.cc
@@ -200,36 +200,6 @@
          type->Is<sem::Array>() || type->Is<sem::Struct>();
 }
 
-// https://gpuweb.github.io/gpuweb/wgsl/#constructible-types
-bool Resolver::IsConstructible(const sem::Type* type) const {
-  if (type->Is<sem::Atomic>()) {
-    return false;
-  }
-
-  if (type->is_scalar() || type->Is<sem::Vector>() || type->Is<sem::Matrix>()) {
-    return true;
-  }
-
-  if (auto* arr = type->As<sem::Array>()) {
-    if (arr->IsRuntimeSized()) {
-      return false;
-    }
-
-    return IsConstructible(arr->ElemType());
-  }
-
-  if (auto* str = type->As<sem::Struct>()) {
-    for (auto* m : str->Members()) {
-      if (!IsConstructible(m->Type())) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  return false;
-}
-
 // https://gpuweb.github.io/gpuweb/wgsl.html#storable-types
 bool Resolver::IsStorable(const sem::Type* type) const {
   return IsPlain(type) || type->Is<sem::Texture>() || type->Is<sem::Sampler>();
@@ -1208,7 +1178,7 @@
   }
 
   if (IsPlain(info->type)) {
-    if (!IsConstructible(info->type) &&
+    if (!info->type->IsConstructible() &&
         IsValidationEnabled(
             info->declaration->decorations(),
             ast::DisabledValidation::kIgnoreConstructibleFunctionParameter)) {
@@ -1411,7 +1381,7 @@
   }
 
   if (!info->return_type->Is<sem::Void>()) {
-    if (!IsConstructible(info->return_type)) {
+    if (!info->return_type->IsConstructible()) {
       AddError("function return type must be a constructible type",
                func->return_type()->source());
       return false;
diff --git a/src/resolver/resolver.h b/src/resolver/resolver.h
index 0cbcc6f..6afb4c7 100644
--- a/src/resolver/resolver.h
+++ b/src/resolver/resolver.h
@@ -84,10 +84,6 @@
   bool IsPlain(const sem::Type* type) const;
 
   /// @param type the given type
-  /// @returns true if the given type is a constructible type
-  bool IsConstructible(const sem::Type* type) const;
-
-  /// @param type the given type
   /// @returns true if the given type is storable
   bool IsStorable(const sem::Type* type) const;
 
diff --git a/src/sem/array.cc b/src/sem/array.cc
index 9dd6ea8..8e3a734 100644
--- a/src/sem/array.cc
+++ b/src/sem/array.cc
@@ -34,10 +34,16 @@
       align_(align),
       size_(size),
       stride_(stride),
-      implicit_stride_(implicit_stride) {
+      implicit_stride_(implicit_stride),
+      constructible_(count > 0  // Runtime-sized arrays are not constructible
+                     && element->IsConstructible()) {
   TINT_ASSERT(Semantic, element_);
 }
 
+bool Array::IsConstructible() const {
+  return constructible_;
+}
+
 std::string Array::type_name() const {
   std::string type_name = "__array" + element_->type_name();
   type_name += "_count_" + std::to_string(count_);
diff --git a/src/sem/array.h b/src/sem/array.h
index 634dcfd..592c538 100644
--- a/src/sem/array.h
+++ b/src/sem/array.h
@@ -85,6 +85,10 @@
   /// @returns true if this array is runtime sized
   bool IsRuntimeSized() const { return count_ == 0; }
 
+  /// @returns true if constructible as per
+  /// https://gpuweb.github.io/gpuweb/wgsl/#constructible-types
+  bool IsConstructible() const override;
+
   /// @returns the name for the type
   std::string type_name() const override;
 
@@ -100,6 +104,7 @@
   uint32_t const size_;
   uint32_t const stride_;
   uint32_t const implicit_stride_;
+  bool const constructible_;
 };
 
 }  // namespace sem
diff --git a/src/sem/bool_type.cc b/src/sem/bool_type.cc
index 817693f..aec4f6b 100644
--- a/src/sem/bool_type.cc
+++ b/src/sem/bool_type.cc
@@ -35,5 +35,9 @@
   return "bool";
 }
 
+bool Bool::IsConstructible() const {
+  return true;
+}
+
 }  // namespace sem
 }  // namespace tint
diff --git a/src/sem/bool_type.h b/src/sem/bool_type.h
index af2a21c..b9fa24d 100644
--- a/src/sem/bool_type.h
+++ b/src/sem/bool_type.h
@@ -44,6 +44,10 @@
   /// @returns the name for this type that closely resembles how it would be
   /// declared in WGSL.
   std::string FriendlyName(const SymbolTable& symbols) const override;
+
+  /// @returns true if constructible as per
+  /// https://gpuweb.github.io/gpuweb/wgsl/#constructible-types
+  bool IsConstructible() const override;
 };
 
 }  // namespace sem
diff --git a/src/sem/f32_type.cc b/src/sem/f32_type.cc
index 12f5c1e..49d8666 100644
--- a/src/sem/f32_type.cc
+++ b/src/sem/f32_type.cc
@@ -35,5 +35,9 @@
   return "f32";
 }
 
+bool F32::IsConstructible() const {
+  return true;
+}
+
 }  // namespace sem
 }  // namespace tint
diff --git a/src/sem/f32_type.h b/src/sem/f32_type.h
index 54dc1cd..2d35982 100644
--- a/src/sem/f32_type.h
+++ b/src/sem/f32_type.h
@@ -38,6 +38,10 @@
   /// @returns the name for this type that closely resembles how it would be
   /// declared in WGSL.
   std::string FriendlyName(const SymbolTable& symbols) const override;
+
+  /// @returns true if constructible as per
+  /// https://gpuweb.github.io/gpuweb/wgsl/#constructible-types
+  bool IsConstructible() const override;
 };
 
 }  // namespace sem
diff --git a/src/sem/i32_type.cc b/src/sem/i32_type.cc
index dcc6a78..0b24e87 100644
--- a/src/sem/i32_type.cc
+++ b/src/sem/i32_type.cc
@@ -35,5 +35,9 @@
   return "i32";
 }
 
+bool I32::IsConstructible() const {
+  return true;
+}
+
 }  // namespace sem
 }  // namespace tint
diff --git a/src/sem/i32_type.h b/src/sem/i32_type.h
index 27bd3bb..4621306 100644
--- a/src/sem/i32_type.h
+++ b/src/sem/i32_type.h
@@ -38,6 +38,10 @@
   /// @returns the name for this type that closely resembles how it would be
   /// declared in WGSL.
   std::string FriendlyName(const SymbolTable& symbols) const override;
+
+  /// @returns true if constructible as per
+  /// https://gpuweb.github.io/gpuweb/wgsl/#constructible-types
+  bool IsConstructible() const override;
 };
 
 }  // namespace sem
diff --git a/src/sem/matrix_type.cc b/src/sem/matrix_type.cc
index 2de6a01..c095f7c 100644
--- a/src/sem/matrix_type.cc
+++ b/src/sem/matrix_type.cc
@@ -49,5 +49,9 @@
   return out.str();
 }
 
+bool Matrix::IsConstructible() const {
+  return true;
+}
+
 }  // namespace sem
 }  // namespace tint
diff --git a/src/sem/matrix_type.h b/src/sem/matrix_type.h
index d91ded0..d3724fa 100644
--- a/src/sem/matrix_type.h
+++ b/src/sem/matrix_type.h
@@ -54,6 +54,10 @@
   /// declared in WGSL.
   std::string FriendlyName(const SymbolTable& symbols) const override;
 
+  /// @returns true if constructible as per
+  /// https://gpuweb.github.io/gpuweb/wgsl/#constructible-types
+  bool IsConstructible() const override;
+
  private:
   Type* const subtype_;
   Vector* const column_type_;
diff --git a/src/sem/struct.cc b/src/sem/struct.cc
index 5365997..317402e 100644
--- a/src/sem/struct.cc
+++ b/src/sem/struct.cc
@@ -35,7 +35,15 @@
       members_(std::move(members)),
       align_(align),
       size_(size),
-      size_no_padding_(size_no_padding) {}
+      size_no_padding_(size_no_padding) {
+  constructible_ = true;
+  for (auto* member : members_) {
+    if (!member->Type()->IsConstructible()) {
+      constructible_ = false;
+      break;
+    }
+  }
+}
 
 Struct::~Struct() = default;
 
@@ -56,6 +64,10 @@
   return symbols.NameFor(declaration_->name());
 }
 
+bool Struct::IsConstructible() const {
+  return constructible_;
+}
+
 StructMember::StructMember(ast::StructMember* declaration,
                            sem::Type* type,
                            uint32_t index,
diff --git a/src/sem/struct.h b/src/sem/struct.h
index 6694d6d..e4595c9 100644
--- a/src/sem/struct.h
+++ b/src/sem/struct.h
@@ -148,6 +148,10 @@
   /// declared in WGSL.
   std::string FriendlyName(const SymbolTable& symbols) const override;
 
+  /// @returns true if constructible as per
+  /// https://gpuweb.github.io/gpuweb/wgsl/#constructible-types
+  bool IsConstructible() const override;
+
  private:
   uint64_t LargestMemberBaseAlignment(MemoryLayout mem_layout) const;
 
@@ -158,6 +162,7 @@
   uint32_t const size_no_padding_;
   std::unordered_set<ast::StorageClass> storage_class_usage_;
   std::unordered_set<PipelineStageUsage> pipeline_stage_uses_;
+  bool constructible_;
 };
 
 /// StructMember holds the semantic information for structure members.
diff --git a/src/sem/type.cc b/src/sem/type.cc
index bddc470..338d8fd 100644
--- a/src/sem/type.cc
+++ b/src/sem/type.cc
@@ -111,6 +111,10 @@
   TINT_ASSERT(Semantic, false);
 }
 
+bool Type::IsConstructible() const {
+  return false;
+}
+
 bool Type::is_scalar() const {
   return IsAnyOf<F32, U32, I32, Bool>();
 }
diff --git a/src/sem/type.h b/src/sem/type.h
index 3a268d4..81a11d9 100644
--- a/src/sem/type.h
+++ b/src/sem/type.h
@@ -56,6 +56,10 @@
   /// @param size the output default size in bytes for this type.
   void GetDefaultAlignAndSize(uint32_t& align, uint32_t& size) const;
 
+  /// @returns true if constructible as per
+  /// https://gpuweb.github.io/gpuweb/wgsl/#constructible-types
+  virtual bool IsConstructible() const;
+
   /// @returns true if this type is a scalar
   bool is_scalar() const;
   /// @returns true if this type is a numeric scalar
diff --git a/src/sem/u32_type.cc b/src/sem/u32_type.cc
index 8dced5f..dbc42b0 100644
--- a/src/sem/u32_type.cc
+++ b/src/sem/u32_type.cc
@@ -35,5 +35,9 @@
   return "u32";
 }
 
+bool U32::IsConstructible() const {
+  return true;
+}
+
 }  // namespace sem
 }  // namespace tint
diff --git a/src/sem/u32_type.h b/src/sem/u32_type.h
index b530b79..cf68da6 100644
--- a/src/sem/u32_type.h
+++ b/src/sem/u32_type.h
@@ -38,6 +38,10 @@
   /// @returns the name for this type that closely resembles how it would be
   /// declared in WGSL.
   std::string FriendlyName(const SymbolTable& symbols) const override;
+
+  /// @returns true if constructible as per
+  /// https://gpuweb.github.io/gpuweb/wgsl/#constructible-types
+  bool IsConstructible() const override;
 };
 
 }  // namespace sem
diff --git a/src/sem/vector_type.cc b/src/sem/vector_type.cc
index 4b6fe20..74cea14 100644
--- a/src/sem/vector_type.cc
+++ b/src/sem/vector_type.cc
@@ -41,5 +41,9 @@
   return out.str();
 }
 
+bool Vector::IsConstructible() const {
+  return true;
+}
+
 }  // namespace sem
 }  // namespace tint
diff --git a/src/sem/vector_type.h b/src/sem/vector_type.h
index 5f8d319..79d8835 100644
--- a/src/sem/vector_type.h
+++ b/src/sem/vector_type.h
@@ -46,6 +46,10 @@
   /// declared in WGSL.
   std::string FriendlyName(const SymbolTable& symbols) const override;
 
+  /// @returns true if constructible as per
+  /// https://gpuweb.github.io/gpuweb/wgsl/#constructible-types
+  bool IsConstructible() const override;
+
  private:
   Type const* const subtype_;
   uint32_t const size_;