[ir] Validate construct for array types

Other composite types will need more special-case logic.

Change-Id: I2bcd1cbcb63918c9c1b73edfab8f52011e77c1cb
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/249235
Auto-Submit: James Price <jrprice@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Commit-Queue: James Price <jrprice@google.com>
diff --git a/src/tint/lang/core/ir/validator.cc b/src/tint/lang/core/ir/validator.cc
index 3989869..772b3d6 100644
--- a/src/tint/lang/core/ir/validator.cc
+++ b/src/tint/lang/core/ir/validator.cc
@@ -3091,7 +3091,32 @@
         return;
     }
 
-    if (auto* str = As<core::type::Struct>(construct->Result()->Type())) {
+    auto* result_type = construct->Result()->Type();
+
+    auto check_args_match_elements = [&] {
+        // Check that type type of each argument matches the expected element type of the composite.
+        for (size_t i = 0; i < args.Length(); i++) {
+            if (args[i]->Is<ir::Unused>()) {
+                continue;
+            }
+            auto* expected_type = result_type->Element(static_cast<uint32_t>(i));
+            if (args[i]->Type() != expected_type) {
+                AddError(construct, Construct::kArgsOperandOffset + i)
+                    << "type " << NameOf(args[i]->Type()) << " of argument " << i
+                    << " does not match expected type " << NameOf(expected_type);
+            }
+        }
+    };
+
+    if (result_type->Is<core::type::Scalar>()) {
+        // TODO(crbug.com/427964608): This needs special handling as Element() produces nullptr.
+    } else if (result_type->Is<core::type::Vector>()) {
+        // TODO(crbug.com/427964205): This needs special handling as there are many cases.
+    } else if (result_type->Is<core::type::Matrix>()) {
+        // TODO(crbug.com/427965903): This needs special handling as there are many cases.
+    } else if (result_type->Is<core::type::Array>()) {
+        check_args_match_elements();
+    } else if (auto* str = As<core::type::Struct>(result_type)) {
         auto members = str->Members();
         if (args.Length() != str->Members().Length()) {
             AddError(construct) << "structure has " << members.Length()
@@ -3099,16 +3124,7 @@
                                 << " arguments";
             return;
         }
-        for (size_t i = 0; i < args.Length(); i++) {
-            if (args[i]->Is<ir::Unused>()) {
-                continue;
-            }
-            if (args[i]->Type() != members[i]->Type()) {
-                AddError(construct, Construct::kArgsOperandOffset + i)
-                    << "type " << NameOf(args[i]->Type()) << " of argument " << i
-                    << " does not match type " << NameOf(members[i]->Type()) << " of struct member";
-            }
-        }
+        check_args_match_elements();
     }
 }
 
diff --git a/src/tint/lang/core/ir/validator_test.cc b/src/tint/lang/core/ir/validator_test.cc
index 20ce4db..90e708b 100644
--- a/src/tint/lang/core/ir/validator_test.cc
+++ b/src/tint/lang/core/ir/validator_test.cc
@@ -191,6 +191,24 @@
 )")) << res.Failure();
 }
 
+TEST_F(IR_ValidatorTest, Construct_Array_WrongArgType) {
+    auto* f = b.Function("f", ty.void_());
+    b.Append(f->Block(), [&] {
+        b.Construct<array<u32, 4>>(1_u, 2_u, 3_i, 4_u);
+        b.Return(f);
+    });
+
+    auto res = ir::Validate(mod);
+    ASSERT_NE(res, Success);
+    EXPECT_THAT(
+        res.Failure().reason,
+        testing::HasSubstr(
+            R"(:3:42 error: construct: type 'i32' of argument 2 does not match expected type 'u32'
+    %2:array<u32, 4> = construct 1u, 2u, 3i, 4u
+                                         ^^
+)")) << res.Failure();
+}
+
 TEST_F(IR_ValidatorTest, Construct_Struct_ZeroValue) {
     auto* str_ty = ty.Struct(mod.symbols.New("MyStruct"), {
                                                               {mod.symbols.New("a"), ty.i32()},
@@ -302,7 +320,7 @@
     EXPECT_THAT(
         res.Failure().reason,
         testing::HasSubstr(
-            R"(:8:33 error: construct: type 'i32' of argument 1 does not match type 'u32' of struct member
+            R"(:8:33 error: construct: type 'i32' of argument 1 does not match expected type 'u32'
     %2:MyStruct = construct 1i, 2i
                                 ^^
 )")) << res.Failure();