[tint] Add sem::Array, derives from core::type::Array

This will hold additional resolver-only data.

Change-Id: I24727df206998ec4a0645b1114b15bb0f1c0a68f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/155302
Reviewed-by: James Price <jrprice@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/lang/core/type/array.h b/src/tint/lang/core/type/array.h
index 0f97911..ea29674 100644
--- a/src/tint/lang/core/type/array.h
+++ b/src/tint/lang/core/type/array.h
@@ -28,7 +28,7 @@
 namespace tint::core::type {
 
 /// Array holds the type information for Array nodes.
-class Array final : public Castable<Array, Type> {
+class Array : public Castable<Array, Type> {
   public:
     /// An error message string stating that the array count was expected to be a constant
     /// expression. Used by multiple writers and transforms.
diff --git a/src/tint/lang/wgsl/resolver/inferred_type_test.cc b/src/tint/lang/wgsl/resolver/inferred_type_test.cc
index 89212fe..ec9e59a 100644
--- a/src/tint/lang/wgsl/resolver/inferred_type_test.cc
+++ b/src/tint/lang/wgsl/resolver/inferred_type_test.cc
@@ -14,6 +14,7 @@
 
 #include "src/tint/lang/wgsl/resolver/resolver.h"
 #include "src/tint/lang/wgsl/resolver/resolver_helper_test.h"
+#include "src/tint/lang/wgsl/sem/array.h"
 
 #include "gmock/gmock.h"
 
@@ -124,9 +125,9 @@
 
 TEST_F(ResolverInferredTypeTest, InferArray_Pass) {
     auto type = ty.array<u32, 10>();
-    auto* expected_type = create<core::type::Array>(create<core::type::U32>(),
-                                                    create<core::type::ConstantArrayCount>(10u), 4u,
-                                                    4u * 10u, 4u, 4u);
+    auto* expected_type =
+        create<sem::Array>(create<core::type::U32>(), create<core::type::ConstantArrayCount>(10u),
+                           4u, 4u * 10u, 4u, 4u);
 
     auto* ctor_expr = Call(type);
     auto* var = Var("a", core::AddressSpace::kFunction, ctor_expr);
diff --git a/src/tint/lang/wgsl/resolver/is_host_shareable_test.cc b/src/tint/lang/wgsl/resolver/is_host_shareable_test.cc
index e23a9b5..b7b0aa6 100644
--- a/src/tint/lang/wgsl/resolver/is_host_shareable_test.cc
+++ b/src/tint/lang/wgsl/resolver/is_host_shareable_test.cc
@@ -17,6 +17,7 @@
 #include "gmock/gmock.h"
 #include "src/tint/lang/core/type/atomic.h"
 #include "src/tint/lang/wgsl/resolver/resolver_helper_test.h"
+#include "src/tint/lang/wgsl/sem/array.h"
 
 namespace tint::resolver {
 namespace {
@@ -106,14 +107,14 @@
 }
 
 TEST_F(ResolverIsHostShareable, ArraySizedOfHostShareable) {
-    auto* arr = create<core::type::Array>(
-        create<core::type::I32>(), create<core::type::ConstantArrayCount>(5u), 4u, 20u, 4u, 4u);
+    auto* arr = create<sem::Array>(create<core::type::I32>(),
+                                   create<core::type::ConstantArrayCount>(5u), 4u, 20u, 4u, 4u);
     EXPECT_TRUE(r()->IsHostShareable(arr));
 }
 
 TEST_F(ResolverIsHostShareable, ArrayUnsizedOfHostShareable) {
-    auto* arr = create<core::type::Array>(create<core::type::I32>(),
-                                          create<core::type::RuntimeArrayCount>(), 4u, 4u, 4u, 4u);
+    auto* arr = create<sem::Array>(create<core::type::I32>(),
+                                   create<core::type::RuntimeArrayCount>(), 4u, 4u, 4u, 4u);
     EXPECT_TRUE(r()->IsHostShareable(arr));
 }
 
diff --git a/src/tint/lang/wgsl/resolver/is_storeable_test.cc b/src/tint/lang/wgsl/resolver/is_storeable_test.cc
index b040dce..e549151 100644
--- a/src/tint/lang/wgsl/resolver/is_storeable_test.cc
+++ b/src/tint/lang/wgsl/resolver/is_storeable_test.cc
@@ -17,6 +17,7 @@
 #include "gmock/gmock.h"
 #include "src/tint/lang/core/type/atomic.h"
 #include "src/tint/lang/wgsl/resolver/resolver_helper_test.h"
+#include "src/tint/lang/wgsl/sem/array.h"
 
 using namespace tint::core::fluent_types;  // NOLINT
 
@@ -91,14 +92,14 @@
 }
 
 TEST_F(ResolverIsStorableTest, ArraySizedOfStorable) {
-    auto* arr = create<core::type::Array>(
-        create<core::type::I32>(), create<core::type::ConstantArrayCount>(5u), 4u, 20u, 4u, 4u);
+    auto* arr = create<sem::Array>(create<core::type::I32>(),
+                                   create<core::type::ConstantArrayCount>(5u), 4u, 20u, 4u, 4u);
     EXPECT_TRUE(r()->IsStorable(arr));
 }
 
 TEST_F(ResolverIsStorableTest, ArrayUnsizedOfStorable) {
-    auto* arr = create<core::type::Array>(create<core::type::I32>(),
-                                          create<core::type::RuntimeArrayCount>(), 4u, 4u, 4u, 4u);
+    auto* arr = create<sem::Array>(create<core::type::I32>(),
+                                   create<core::type::RuntimeArrayCount>(), 4u, 4u, 4u, 4u);
     EXPECT_TRUE(r()->IsStorable(arr));
 }
 
diff --git a/src/tint/lang/wgsl/resolver/materialize_test.cc b/src/tint/lang/wgsl/resolver/materialize_test.cc
index f4b0ab5..53a6bc1 100644
--- a/src/tint/lang/wgsl/resolver/materialize_test.cc
+++ b/src/tint/lang/wgsl/resolver/materialize_test.cc
@@ -17,6 +17,7 @@
 #include "src/tint/lang/core/type/helper_test.h"
 #include "src/tint/lang/wgsl/resolver/resolver.h"
 #include "src/tint/lang/wgsl/resolver/resolver_helper_test.h"
+#include "src/tint/lang/wgsl/sem/array.h"
 #include "src/tint/utils/rtti/switch.h"
 
 #include "gmock/gmock.h"
@@ -117,7 +118,7 @@
                     }
                 }
             },
-            [&](const core::type::Array* a) {
+            [&](const sem::Array* a) {
                 auto count = a->ConstantCount();
                 ASSERT_NE(count, 0u);
                 for (uint32_t i = 0; i < count; i++) {
diff --git a/src/tint/lang/wgsl/resolver/resolver.cc b/src/tint/lang/wgsl/resolver/resolver.cc
index 6708a0c..ff8f66b 100644
--- a/src/tint/lang/wgsl/resolver/resolver.cc
+++ b/src/tint/lang/wgsl/resolver/resolver.cc
@@ -64,6 +64,7 @@
 #include "src/tint/lang/wgsl/resolver/incomplete_type.h"
 #include "src/tint/lang/wgsl/resolver/uniformity.h"
 #include "src/tint/lang/wgsl/resolver/unresolved_identifier.h"
+#include "src/tint/lang/wgsl/sem/array.h"
 #include "src/tint/lang/wgsl/sem/break_if_statement.h"
 #include "src/tint/lang/wgsl/sem/builtin_enum_expression.h"
 #include "src/tint/lang/wgsl/sem/call.h"
@@ -901,7 +902,7 @@
     for (auto* var : transitively_referenced_overrides) {
         b.Sem().AddTransitivelyReferencedOverride(sem, var);
     }
-    if (auto* arr = sem->Type()->UnwrapRef()->As<core::type::Array>()) {
+    if (auto* arr = sem->Type()->UnwrapRef()->As<sem::Array>()) {
         auto* refs = b.Sem().TransitivelyReferencedOverrides(arr);
         if (refs) {
             for (auto* var : *refs) {
@@ -1775,9 +1776,9 @@
                               return target_ty ? target_ty : f32m(m->columns(), m->rows());
                           });
         },
-        [&](const core::type::Array* a) -> const core::type::Type* {
+        [&](const sem::Array* a) -> const core::type::Type* {
             const core::type::Type* target_el_ty = nullptr;
-            if (auto* target_arr_ty = As<core::type::Array>(target_ty)) {
+            if (auto* target_arr_ty = As<sem::Array>(target_ty)) {
                 target_el_ty = target_arr_ty->ElemType();
             }
             if (auto* el_ty = ConcreteType(a->ElemType(), target_el_ty, source)) {
@@ -1944,7 +1945,7 @@
     auto* obj_ty = obj_raw_ty->UnwrapRef();
     auto* ty = Switch(
         obj_ty,  //
-        [&](const core::type::Array* arr) { return arr->ElemType(); },
+        [&](const sem::Array* arr) { return arr->ElemType(); },
         [&](const core::type::Vector* vec) { return vec->type(); },
         [&](const core::type::Matrix* mat) {
             return b.create<core::type::Vector>(mat->type(), mat->rows());
@@ -2174,7 +2175,7 @@
                 return ctor_or_conv(wgsl::intrinsic::MatrixCtorConv(m->columns(), m->rows()),
                                     m->type());
             },
-            [&](const core::type::Array* arr) -> sem::Call* {
+            [&](const sem::Array* arr) -> sem::Call* {
                 auto* call_target = array_ctors_.GetOrCreate(
                     ArrayConstructorSig{{arr, args.Length(), args_stage}},
                     [&]() -> sem::ValueConstructor* {
@@ -4045,12 +4046,12 @@
     return true;
 }
 
-core::type::Array* Resolver::Array(const Source& array_source,
-                                   const Source& el_source,
-                                   const Source& count_source,
-                                   const core::type::Type* el_ty,
-                                   const core::type::ArrayCount* el_count,
-                                   uint32_t explicit_stride) {
+sem::Array* Resolver::Array(const Source& array_source,
+                            const Source& el_source,
+                            const Source& count_source,
+                            const core::type::Type* el_ty,
+                            const core::type::ArrayCount* el_count,
+                            uint32_t explicit_stride) {
     uint32_t el_align = el_ty->Align();
     uint32_t el_size = el_ty->Size();
     uint64_t implicit_stride = el_size ? tint::RoundUp<uint64_t>(el_align, el_size) : 0;
@@ -4069,9 +4070,9 @@
     } else if (el_count->Is<core::type::RuntimeArrayCount>()) {
         size = stride;
     }
-    auto* out = b.create<core::type::Array>(el_ty, el_count, el_align, static_cast<uint32_t>(size),
-                                            static_cast<uint32_t>(stride),
-                                            static_cast<uint32_t>(implicit_stride));
+    auto* out =
+        b.create<sem::Array>(el_ty, el_count, el_align, static_cast<uint32_t>(size),
+                             static_cast<uint32_t>(stride), static_cast<uint32_t>(implicit_stride));
 
     // Maximum nesting depth of composite types
     //  https://gpuweb.github.io/gpuweb/wgsl/#limits
@@ -4735,7 +4736,7 @@
         return true;
     }
 
-    if (auto* arr = ty->As<core::type::Array>()) {
+    if (auto* arr = ty->As<sem::Array>()) {
         if (address_space != core::AddressSpace::kStorage) {
             if (arr->Count()->Is<core::type::RuntimeArrayCount>()) {
                 AddError("runtime-sized arrays can only be used in the <storage> address space",
diff --git a/src/tint/lang/wgsl/resolver/resolver.h b/src/tint/lang/wgsl/resolver/resolver.h
index 458546e..45e487f 100644
--- a/src/tint/lang/wgsl/resolver/resolver.h
+++ b/src/tint/lang/wgsl/resolver/resolver.h
@@ -484,12 +484,12 @@
     /// @param el_ty the Array element type
     /// @param el_count the number of elements in the array.
     /// @param explicit_stride the explicit byte stride of the array. Zero means implicit stride.
-    core::type::Array* Array(const Source& array_source,
-                             const Source& el_source,
-                             const Source& count_source,
-                             const core::type::Type* el_ty,
-                             const core::type::ArrayCount* el_count,
-                             uint32_t explicit_stride);
+    sem::Array* Array(const Source& array_source,
+                      const Source& el_source,
+                      const Source& count_source,
+                      const core::type::Type* el_ty,
+                      const core::type::ArrayCount* el_count,
+                      uint32_t explicit_stride);
 
     /// Builds and returns the semantic information for the alias `alias`.
     /// This method does not mark the ast::Alias node, nor attach the generated
@@ -634,8 +634,8 @@
 
     // ArrayConstructorSig represents a unique array constructor signature.
     // It is a tuple of the array type, number of arguments provided and earliest evaluation stage.
-    using ArrayConstructorSig = tint::UnorderedKeyWrapper<
-        std::tuple<const core::type::Array*, size_t, core::EvaluationStage>>;
+    using ArrayConstructorSig =
+        tint::UnorderedKeyWrapper<std::tuple<const sem::Array*, size_t, core::EvaluationStage>>;
 
     // StructConstructorSig represents a unique structure constructor signature.
     // It is a tuple of the structure type, number of arguments provided and earliest evaluation
diff --git a/src/tint/lang/wgsl/resolver/resolver_helper_test.h b/src/tint/lang/wgsl/resolver/resolver_helper_test.h
index f7bda4a..f15027c 100644
--- a/src/tint/lang/wgsl/resolver/resolver_helper_test.h
+++ b/src/tint/lang/wgsl/resolver/resolver_helper_test.h
@@ -28,6 +28,7 @@
 #include "src/tint/lang/core/type/abstract_int.h"
 #include "src/tint/lang/wgsl/program/program_builder.h"
 #include "src/tint/lang/wgsl/resolver/resolver.h"
+#include "src/tint/lang/wgsl/sem/array.h"
 #include "src/tint/lang/wgsl/sem/statement.h"
 #include "src/tint/lang/wgsl/sem/value_expression.h"
 #include "src/tint/lang/wgsl/sem/variable.h"
@@ -682,7 +683,7 @@
         } else {
             count = b.create<core::type::ConstantArrayCount>(N);
         }
-        return b.create<core::type::Array>(
+        return b.create<sem::Array>(
             /* element */ el,
             /* count */ count,
             /* align */ el->Align(),
diff --git a/src/tint/lang/wgsl/resolver/resolver_test.cc b/src/tint/lang/wgsl/resolver/resolver_test.cc
index 17a039d..3fc4a87 100644
--- a/src/tint/lang/wgsl/resolver/resolver_test.cc
+++ b/src/tint/lang/wgsl/resolver/resolver_test.cc
@@ -39,6 +39,7 @@
 #include "src/tint/lang/wgsl/ast/variable_decl_statement.h"
 #include "src/tint/lang/wgsl/ast/workgroup_attribute.h"
 #include "src/tint/lang/wgsl/resolver/resolver_helper_test.h"
+#include "src/tint/lang/wgsl/sem/array.h"
 #include "src/tint/lang/wgsl/sem/call.h"
 #include "src/tint/lang/wgsl/sem/function.h"
 #include "src/tint/lang/wgsl/sem/member_accessor_expression.h"
@@ -423,7 +424,7 @@
     ASSERT_NE(TypeOf(a), nullptr);
     auto* ref = TypeOf(a)->As<core::type::Reference>();
     ASSERT_NE(ref, nullptr);
-    auto* ary = ref->StoreType()->As<core::type::Array>();
+    auto* ary = ref->StoreType()->As<sem::Array>();
     EXPECT_EQ(ary->Count(), create<core::type::ConstantArrayCount>(10u));
 }
 
@@ -436,7 +437,7 @@
     ASSERT_NE(TypeOf(a), nullptr);
     auto* ref = TypeOf(a)->As<core::type::Reference>();
     ASSERT_NE(ref, nullptr);
-    auto* ary = ref->StoreType()->As<core::type::Array>();
+    auto* ary = ref->StoreType()->As<sem::Array>();
     EXPECT_EQ(ary->Count(), create<core::type::ConstantArrayCount>(10u));
 }
 
@@ -451,7 +452,7 @@
     ASSERT_NE(TypeOf(a), nullptr);
     auto* ref = TypeOf(a)->As<core::type::Reference>();
     ASSERT_NE(ref, nullptr);
-    auto* ary = ref->StoreType()->As<core::type::Array>();
+    auto* ary = ref->StoreType()->As<sem::Array>();
     EXPECT_EQ(ary->Count(), create<core::type::ConstantArrayCount>(10u));
 }
 
@@ -466,7 +467,7 @@
     ASSERT_NE(TypeOf(a), nullptr);
     auto* ref = TypeOf(a)->As<core::type::Reference>();
     ASSERT_NE(ref, nullptr);
-    auto* ary = ref->StoreType()->As<core::type::Array>();
+    auto* ary = ref->StoreType()->As<sem::Array>();
     EXPECT_EQ(ary->Count(), create<core::type::ConstantArrayCount>(10u));
 }
 
@@ -481,7 +482,7 @@
     ASSERT_NE(TypeOf(a), nullptr);
     auto* ref = TypeOf(a)->As<core::type::Reference>();
     ASSERT_NE(ref, nullptr);
-    auto* ary = ref->StoreType()->As<core::type::Array>();
+    auto* ary = ref->StoreType()->As<sem::Array>();
     auto* sem_override = Sem().Get(override);
     ASSERT_NE(sem_override, nullptr);
     EXPECT_EQ(ary->Count(), create<sem::NamedOverrideArrayCount>(sem_override));
@@ -500,12 +501,12 @@
     ASSERT_NE(TypeOf(a), nullptr);
     auto* ref_a = TypeOf(a)->As<core::type::Reference>();
     ASSERT_NE(ref_a, nullptr);
-    auto* ary_a = ref_a->StoreType()->As<core::type::Array>();
+    auto* ary_a = ref_a->StoreType()->As<sem::Array>();
 
     ASSERT_NE(TypeOf(b), nullptr);
     auto* ref_b = TypeOf(b)->As<core::type::Reference>();
     ASSERT_NE(ref_b, nullptr);
-    auto* ary_b = ref_b->StoreType()->As<core::type::Array>();
+    auto* ary_b = ref_b->StoreType()->As<sem::Array>();
 
     auto* sem_override = Sem().Get(override);
     ASSERT_NE(sem_override, nullptr);
@@ -526,7 +527,7 @@
     ASSERT_NE(TypeOf(a), nullptr);
     auto* ref = TypeOf(a)->As<core::type::Reference>();
     ASSERT_NE(ref, nullptr);
-    auto* ary = ref->StoreType()->As<core::type::Array>();
+    auto* ary = ref->StoreType()->As<sem::Array>();
     auto* sem_override = Sem().Get(override);
     ASSERT_NE(sem_override, nullptr);
     EXPECT_EQ(ary->Count(), create<sem::UnnamedOverrideArrayCount>(Sem().Get(cnt)));
@@ -547,12 +548,12 @@
     ASSERT_NE(TypeOf(a), nullptr);
     auto* ref_a = TypeOf(a)->As<core::type::Reference>();
     ASSERT_NE(ref_a, nullptr);
-    auto* ary_a = ref_a->StoreType()->As<core::type::Array>();
+    auto* ary_a = ref_a->StoreType()->As<sem::Array>();
 
     ASSERT_NE(TypeOf(b), nullptr);
     auto* ref_b = TypeOf(b)->As<core::type::Reference>();
     ASSERT_NE(ref_b, nullptr);
-    auto* ary_b = ref_b->StoreType()->As<core::type::Array>();
+    auto* ary_b = ref_b->StoreType()->As<sem::Array>();
 
     auto* sem_override = Sem().Get(override);
     ASSERT_NE(sem_override, nullptr);
diff --git a/src/tint/lang/wgsl/resolver/validator.cc b/src/tint/lang/wgsl/resolver/validator.cc
index ba87a8e..762db30 100644
--- a/src/tint/lang/wgsl/resolver/validator.cc
+++ b/src/tint/lang/wgsl/resolver/validator.cc
@@ -21,7 +21,6 @@
 
 #include "src/tint/lang/core/fluent_types.h"
 #include "src/tint/lang/core/type/abstract_numeric.h"
-#include "src/tint/lang/core/type/array.h"
 #include "src/tint/lang/core/type/atomic.h"
 #include "src/tint/lang/core/type/depth_multisampled_texture.h"
 #include "src/tint/lang/core/type/depth_texture.h"
@@ -53,6 +52,7 @@
 #include "src/tint/lang/wgsl/ast/unary_op_expression.h"
 #include "src/tint/lang/wgsl/ast/variable_decl_statement.h"
 #include "src/tint/lang/wgsl/ast/workgroup_attribute.h"
+#include "src/tint/lang/wgsl/sem/array.h"
 #include "src/tint/lang/wgsl/sem/break_if_statement.h"
 #include "src/tint/lang/wgsl/sem/call.h"
 #include "src/tint/lang/wgsl/sem/for_loop_statement.h"
@@ -202,7 +202,7 @@
 // https://gpuweb.github.io/gpuweb/wgsl/#plain-types-section
 bool Validator::IsPlain(const core::type::Type* type) const {
     return type->IsAnyOf<core::type::Scalar, core::type::Atomic, core::type::Vector,
-                         core::type::Matrix, core::type::Array, core::type::Struct>();
+                         core::type::Matrix, sem::Array, core::type::Struct>();
 }
 
 // https://gpuweb.github.io/gpuweb/wgsl/#fixed-footprint-types
@@ -212,7 +212,7 @@
         [&](const core::type::Vector*) { return true; },  //
         [&](const core::type::Matrix*) { return true; },  //
         [&](const core::type::Atomic*) { return true; },
-        [&](const core::type::Array* arr) {
+        [&](const sem::Array* arr) {
             return !arr->Count()->Is<core::type::RuntimeArrayCount>() &&
                    IsFixedFootprint(arr->ElemType());
         },
@@ -236,7 +236,7 @@
         type,  //
         [&](const core::type::Vector* vec) { return IsHostShareable(vec->type()); },
         [&](const core::type::Matrix* mat) { return IsHostShareable(mat->type()); },
-        [&](const core::type::Array* arr) { return IsHostShareable(arr->ElemType()); },
+        [&](const sem::Array* arr) { return IsHostShareable(arr->ElemType()); },
         [&](const core::type::Struct* str) {
             for (auto* member : str->Members()) {
                 if (!IsHostShareable(member->Type())) {
@@ -416,7 +416,7 @@
 
     auto is_uniform_struct_or_array = [address_space](const core::type::Type* ty) {
         return address_space == core::AddressSpace::kUniform &&
-               ty->IsAnyOf<core::type::Array, core::type::Struct>();
+               ty->IsAnyOf<sem::Array, core::type::Struct>();
     };
 
     auto is_uniform_struct = [address_space](const core::type::Type* ty) {
@@ -523,7 +523,7 @@
     }
 
     // For uniform buffer array members, validate that array elements are aligned to 16 bytes
-    if (auto* arr = store_ty->As<core::type::Array>()) {
+    if (auto* arr = store_ty->As<sem::Array>()) {
         // Recurse into the element type.
         // TODO(crbug.com/tint/1388): Ideally we'd pass the source for nested element type here, but
         // we can't easily get that from the semantic node. We should consider recursing through the
@@ -1889,7 +1889,7 @@
 }
 
 bool Validator::ArrayConstructor(const ast::CallExpression* ctor,
-                                 const core::type::Array* array_type) const {
+                                 const sem::Array* array_type) const {
     auto& values = ctor->args;
     auto* elem_ty = array_type->ElemType();
     for (auto* value : values) {
@@ -2069,7 +2069,7 @@
     return true;
 }
 
-bool Validator::Array(const core::type::Array* arr, const Source& el_source) const {
+bool Validator::Array(const sem::Array* arr, const Source& el_source) const {
     auto* el_ty = arr->ElemType();
 
     if (!IsPlain(el_ty)) {
@@ -2123,7 +2123,7 @@
     auto has_index = false;
     Hashset<std::pair<uint32_t, uint32_t>, 8> locationsAndIndexes;
     for (auto* member : str->Members()) {
-        if (auto* r = member->Type()->As<core::type::Array>()) {
+        if (auto* r = member->Type()->As<sem::Array>()) {
             if (r->Count()->Is<core::type::RuntimeArrayCount>()) {
                 if (member != str->Members().Back()) {
                     AddError("runtime arrays may only appear as the last member of a struct",
@@ -2607,7 +2607,7 @@
 }
 
 bool Validator::IsArrayWithOverrideCount(const core::type::Type* ty) const {
-    if (auto* arr = ty->UnwrapRef()->As<core::type::Array>()) {
+    if (auto* arr = ty->UnwrapRef()->As<sem::Array>()) {
         if (arr->Count()->IsAnyOf<sem::NamedOverrideArrayCount, sem::UnnamedOverrideArrayCount>()) {
             return true;
         }
@@ -2715,7 +2715,7 @@
             return true;
         },
         [&](const core::type::Struct*) { return check_sub_atomics(); },  //
-        [&](const core::type::Array*) { return check_sub_atomics(); },   //
+        [&](const sem::Array*) { return check_sub_atomics(); },          //
         [&](Default) { return true; });
 }
 
diff --git a/src/tint/lang/wgsl/resolver/validator.h b/src/tint/lang/wgsl/resolver/validator.h
index ecce5ba..e5df0f1 100644
--- a/src/tint/lang/wgsl/resolver/validator.h
+++ b/src/tint/lang/wgsl/resolver/validator.h
@@ -173,7 +173,7 @@
     /// @param el_source the source of the array element, or the array if the array does not have a
     ///        locally-declared element AST node.
     /// @returns true on success, false otherwise.
-    bool Array(const core::type::Array* arr, const Source& el_source) const;
+    bool Array(const sem::Array* arr, const Source& el_source) const;
 
     /// Validates an array stride attribute
     /// @param attr the stride attribute to validate
@@ -452,7 +452,7 @@
     /// @param ctor the call expresion to validate
     /// @param arr_type the type of the array
     /// @returns true on success, false otherwise
-    bool ArrayConstructor(const ast::CallExpression* ctor, const core::type::Array* arr_type) const;
+    bool ArrayConstructor(const ast::CallExpression* ctor, const sem::Array* arr_type) const;
 
     /// Validates a texture builtin function
     /// @param call the builtin call to validate
diff --git a/src/tint/lang/wgsl/resolver/validator_is_storeable_test.cc b/src/tint/lang/wgsl/resolver/validator_is_storeable_test.cc
index 8674a75..0290231 100644
--- a/src/tint/lang/wgsl/resolver/validator_is_storeable_test.cc
+++ b/src/tint/lang/wgsl/resolver/validator_is_storeable_test.cc
@@ -17,6 +17,7 @@
 #include "gmock/gmock.h"
 #include "src/tint/lang/core/type/atomic.h"
 #include "src/tint/lang/wgsl/resolver/resolver_helper_test.h"
+#include "src/tint/lang/wgsl/sem/array.h"
 
 namespace tint::resolver {
 namespace {
@@ -89,14 +90,14 @@
 }
 
 TEST_F(ValidatorIsStorableTest, ArraySizedOfStorable) {
-    auto* arr = create<core::type::Array>(
-        create<core::type::I32>(), create<core::type::ConstantArrayCount>(5u), 4u, 20u, 4u, 4u);
+    auto* arr = create<sem::Array>(create<core::type::I32>(),
+                                   create<core::type::ConstantArrayCount>(5u), 4u, 20u, 4u, 4u);
     EXPECT_TRUE(v()->IsStorable(arr));
 }
 
 TEST_F(ValidatorIsStorableTest, ArrayUnsizedOfStorable) {
-    auto* arr = create<core::type::Array>(create<core::type::I32>(),
-                                          create<core::type::RuntimeArrayCount>(), 4u, 4u, 4u, 4u);
+    auto* arr = create<sem::Array>(create<core::type::I32>(),
+                                   create<core::type::RuntimeArrayCount>(), 4u, 4u, 4u, 4u);
     EXPECT_TRUE(v()->IsStorable(arr));
 }
 
diff --git a/src/tint/lang/wgsl/resolver/value_constructor_validation_test.cc b/src/tint/lang/wgsl/resolver/value_constructor_validation_test.cc
index 0dcd3a1..c962e18 100644
--- a/src/tint/lang/wgsl/resolver/value_constructor_validation_test.cc
+++ b/src/tint/lang/wgsl/resolver/value_constructor_validation_test.cc
@@ -15,6 +15,7 @@
 #include "gmock/gmock.h"
 #include "src/tint/lang/core/type/reference.h"
 #include "src/tint/lang/wgsl/resolver/resolver_helper_test.h"
+#include "src/tint/lang/wgsl/sem/array.h"
 #include "src/tint/lang/wgsl/sem/value_constructor.h"
 #include "src/tint/lang/wgsl/sem/value_conversion.h"
 #include "src/tint/utils/text/string_stream.h"
@@ -489,7 +490,7 @@
 
     auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
-    EXPECT_TRUE(call->Type()->Is<core::type::Array>());
+    EXPECT_TRUE(call->Type()->Is<sem::Array>());
     auto* ctor = call->Target()->As<sem::ValueConstructor>();
     ASSERT_NE(ctor, nullptr);
     EXPECT_EQ(call->Type(), ctor->ReturnType());
@@ -505,7 +506,7 @@
 
     auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
-    EXPECT_TRUE(call->Type()->Is<core::type::Array>());
+    EXPECT_TRUE(call->Type()->Is<sem::Array>());
     auto* ctor = call->Target()->As<sem::ValueConstructor>();
     ASSERT_NE(ctor, nullptr);
     EXPECT_EQ(call->Type(), ctor->ReturnType());
@@ -524,7 +525,7 @@
 
     auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
-    EXPECT_TRUE(call->Type()->Is<core::type::Array>());
+    EXPECT_TRUE(call->Type()->Is<sem::Array>());
     auto* ctor = call->Target()->As<sem::ValueConstructor>();
     ASSERT_NE(ctor, nullptr);
     EXPECT_EQ(call->Type(), ctor->ReturnType());
@@ -543,7 +544,7 @@
 
     auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
-    EXPECT_TRUE(call->Type()->Is<core::type::Array>());
+    EXPECT_TRUE(call->Type()->Is<sem::Array>());
     auto* ctor = call->Target()->As<sem::ValueConstructor>();
     ASSERT_NE(ctor, nullptr);
     EXPECT_EQ(call->Type(), ctor->ReturnType());
@@ -562,7 +563,7 @@
 
     auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
-    EXPECT_TRUE(call->Type()->Is<core::type::Array>());
+    EXPECT_TRUE(call->Type()->Is<sem::Array>());
     auto* ctor = call->Target()->As<sem::ValueConstructor>();
     ASSERT_NE(ctor, nullptr);
     EXPECT_EQ(call->Type(), ctor->ReturnType());
@@ -581,7 +582,7 @@
 
     auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
-    EXPECT_TRUE(call->Type()->Is<core::type::Array>());
+    EXPECT_TRUE(call->Type()->Is<sem::Array>());
     auto* ctor = call->Target()->As<sem::ValueConstructor>();
     ASSERT_NE(ctor, nullptr);
     EXPECT_EQ(call->Type(), ctor->ReturnType());
@@ -600,7 +601,7 @@
 
     auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
-    EXPECT_TRUE(call->Type()->Is<core::type::Array>());
+    EXPECT_TRUE(call->Type()->Is<sem::Array>());
     auto* ctor = call->Target()->As<sem::ValueConstructor>();
     ASSERT_NE(ctor, nullptr);
     EXPECT_EQ(call->Type(), ctor->ReturnType());
@@ -621,7 +622,7 @@
 
     auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
-    EXPECT_TRUE(call->Type()->Is<core::type::Array>());
+    EXPECT_TRUE(call->Type()->Is<sem::Array>());
     auto* ctor = call->Target()->As<sem::ValueConstructor>();
     ASSERT_NE(ctor, nullptr);
     EXPECT_EQ(call->Type(), ctor->ReturnType());
@@ -645,7 +646,7 @@
 
     auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
-    EXPECT_TRUE(call->Type()->Is<core::type::Array>());
+    EXPECT_TRUE(call->Type()->Is<sem::Array>());
     auto* ctor = call->Target()->As<sem::ValueConstructor>();
     ASSERT_NE(ctor, nullptr);
     EXPECT_EQ(call->Type(), ctor->ReturnType());
diff --git a/src/tint/lang/wgsl/sem/BUILD.bazel b/src/tint/lang/wgsl/sem/BUILD.bazel
index a014edf..a7abdd6 100644
--- a/src/tint/lang/wgsl/sem/BUILD.bazel
+++ b/src/tint/lang/wgsl/sem/BUILD.bazel
@@ -27,6 +27,7 @@
   name = "sem",
   srcs = [
     "accessor_expression.cc",
+    "array.cc",
     "array_count.cc",
     "behavior.cc",
     "block_statement.cc",
@@ -60,6 +61,7 @@
   ],
   hdrs = [
     "accessor_expression.h",
+    "array.h",
     "array_count.h",
     "behavior.h",
     "block_statement.h",
diff --git a/src/tint/lang/wgsl/sem/BUILD.cmake b/src/tint/lang/wgsl/sem/BUILD.cmake
index 6e545fa..29749a5 100644
--- a/src/tint/lang/wgsl/sem/BUILD.cmake
+++ b/src/tint/lang/wgsl/sem/BUILD.cmake
@@ -28,6 +28,8 @@
 tint_add_target(tint_lang_wgsl_sem lib
   lang/wgsl/sem/accessor_expression.cc
   lang/wgsl/sem/accessor_expression.h
+  lang/wgsl/sem/array.cc
+  lang/wgsl/sem/array.h
   lang/wgsl/sem/array_count.cc
   lang/wgsl/sem/array_count.h
   lang/wgsl/sem/behavior.cc
diff --git a/src/tint/lang/wgsl/sem/BUILD.gn b/src/tint/lang/wgsl/sem/BUILD.gn
index bdce811..f0465ba 100644
--- a/src/tint/lang/wgsl/sem/BUILD.gn
+++ b/src/tint/lang/wgsl/sem/BUILD.gn
@@ -33,6 +33,8 @@
   sources = [
     "accessor_expression.cc",
     "accessor_expression.h",
+    "array.cc",
+    "array.h",
     "array_count.cc",
     "array_count.h",
     "behavior.cc",
diff --git a/src/tint/lang/wgsl/sem/array.cc b/src/tint/lang/wgsl/sem/array.cc
new file mode 100644
index 0000000..3793bb6
--- /dev/null
+++ b/src/tint/lang/wgsl/sem/array.cc
@@ -0,0 +1,31 @@
+// Copyright 2021 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 "src/tint/lang/wgsl/sem/array.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::sem::Array);
+
+namespace tint::sem {
+
+Array::Array(core::type::Type const* element,
+             const core::type::ArrayCount* count,
+             uint32_t align,
+             uint32_t size,
+             uint32_t stride,
+             uint32_t implicit_stride)
+    : Base(element, count, align, size, stride, implicit_stride) {}
+
+Array::~Array() = default;
+
+}  // namespace tint::sem
diff --git a/src/tint/lang/wgsl/sem/array.h b/src/tint/lang/wgsl/sem/array.h
new file mode 100644
index 0000000..82c635a
--- /dev/null
+++ b/src/tint/lang/wgsl/sem/array.h
@@ -0,0 +1,55 @@
+// Copyright 2023 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.
+
+#ifndef SRC_TINT_LANG_WGSL_SEM_ARRAY_H_
+#define SRC_TINT_LANG_WGSL_SEM_ARRAY_H_
+
+#include "src/tint/lang/core/type/array.h"
+#include "src/tint/utils/containers/unique_vector.h"
+
+/// Forward declarations
+namespace tint::sem {
+class GlobalVariable;
+}
+
+namespace tint::sem {
+
+/// Array holds the semantic information for Arrays.
+class Array final : public Castable<Array, core::type::Array> {
+  public:
+    /// Constructor
+    /// @param element the array element type
+    /// @param count the number of elements in the array.
+    /// @param align the byte alignment of the array
+    /// @param size the byte size of the array. The size will be 0 if the array element count is
+    ///        pipeline overridable.
+    /// @param stride the number of bytes from the start of one element of the
+    ///        array to the start of the next element
+    /// @param implicit_stride the number of bytes from the start of one element
+    /// of the array to the start of the next element, if there was no `@stride`
+    /// attribute applied.
+    Array(core::type::Type const* element,
+          const core::type::ArrayCount* count,
+          uint32_t align,
+          uint32_t size,
+          uint32_t stride,
+          uint32_t implicit_stride);
+
+    /// Destructor
+    ~Array() override;
+};
+
+}  // namespace tint::sem
+
+#endif  // SRC_TINT_LANG_WGSL_SEM_ARRAY_H_
diff --git a/src/tint/lang/wgsl/sem/helper_test.h b/src/tint/lang/wgsl/sem/helper_test.h
index ece8694..fb45117 100644
--- a/src/tint/lang/wgsl/sem/helper_test.h
+++ b/src/tint/lang/wgsl/sem/helper_test.h
@@ -36,8 +36,11 @@
         return resolver::Resolve(*this);
     }
 };
+
+/// An alias to TestHelper templated without a test parameter
 using TestHelper = TestHelperBase<testing::Test>;
 
+/// An alias to TestHelper templated with the parameter type T
 template <typename T>
 using TestParamHelper = TestHelperBase<testing::TestWithParam<T>>;
 
diff --git a/src/tint/lang/wgsl/sem/pipeline_stage_set.h b/src/tint/lang/wgsl/sem/pipeline_stage_set.h
index c21796f..417235b 100644
--- a/src/tint/lang/wgsl/sem/pipeline_stage_set.h
+++ b/src/tint/lang/wgsl/sem/pipeline_stage_set.h
@@ -20,6 +20,7 @@
 
 namespace tint::sem {
 
+/// A set of PipelineStage
 using PipelineStageSet = tint::EnumSet<ast::PipelineStage>;
 
 }  // namespace tint::sem