Move array to type/

This CL moves array to the type/ folder. Namespaces are updated as
needed. A FriendlyName method was added to ArrayCount so the sem::
ArrayCount entries do not need to be referenced inside type/.

Bug: tint:1718
Change-Id: I16a8f32b3fab1131b284a6981a5c386081138b08
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/113427
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 7537902..4db8289 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -413,7 +413,6 @@
     "resolver/validator.cc",
     "resolver/validator.h",
     "scope_stack.h",
-    "sem/array.h",
     "sem/array_count.h",
     "sem/behavior.h",
     "sem/binding_point.h",
@@ -555,6 +554,7 @@
     "type/abstract_float.h",
     "type/abstract_int.h",
     "type/abstract_numeric.h",
+    "type/array.h",
     "type/array_count.h",
     "type/atomic.h",
     "type/bool.h",
@@ -633,8 +633,6 @@
 
 libtint_source_set("libtint_sem_src") {
   sources = [
-    "sem/array.cc",
-    "sem/array.h",
     "sem/array_count.cc",
     "sem/array_count.h",
     "sem/behavior.cc",
@@ -703,6 +701,8 @@
     "type/abstract_int.h",
     "type/abstract_numeric.cc",
     "type/abstract_numeric.h",
+    "type/array.cc",
+    "type/array.h",
     "type/array_count.cc",
     "type/array_count.h",
     "type/atomic.cc",
@@ -1206,7 +1206,6 @@
 
   tint_unittests_source_set("tint_unittests_sem_src") {
     sources = [
-      "sem/array_test.cc",
       "sem/builtin_test.cc",
       "sem/expression_test.cc",
       "sem/struct_test.cc",
@@ -1215,6 +1214,7 @@
 
   tint_unittests_source_set("tint_unittests_type_src") {
     sources = [
+      "type/array_test.cc",
       "type/atomic_test.cc",
       "type/bool_test.cc",
       "type/depth_multisampled_texture_test.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 375c253..708d765 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -291,8 +291,6 @@
   resolver/validator.cc
   resolver/validator.h
   scope_stack.h
-  sem/array.cc
-  sem/array.h
   sem/array_count.cc
   sem/array_count.h
   sem/behavior.cc
@@ -460,6 +458,8 @@
   type/abstract_int.h
   type/abstract_numeric.cc
   type/abstract_numeric.h
+  type/array.cc
+  type/array.h
   type/array_count.cc
   type/array_count.h
   type/atomic.cc
@@ -923,7 +923,6 @@
     resolver/variable_test.cc
     resolver/variable_validation_test.cc
     scope_stack_test.cc
-    sem/array_test.cc
     sem/builtin_test.cc
     sem/expression_test.cc
     sem/struct_test.cc
@@ -934,6 +933,7 @@
     text/unicode_test.cc
     traits_test.cc
     transform/transform_test.cc
+    type/array_test.cc
     type/atomic.cc
     type/bool_test.cc
     type/depth_multisampled_texture_test.cc
diff --git a/src/tint/inspector/inspector.cc b/src/tint/inspector/inspector.cc
index 1b7a7fb..647d14e 100644
--- a/src/tint/inspector/inspector.cc
+++ b/src/tint/inspector/inspector.cc
@@ -28,13 +28,13 @@
 #include "src/tint/ast/module.h"
 #include "src/tint/ast/override.h"
 #include "src/tint/ast/var.h"
-#include "src/tint/sem/array.h"
 #include "src/tint/sem/call.h"
 #include "src/tint/sem/function.h"
 #include "src/tint/sem/module.h"
 #include "src/tint/sem/statement.h"
 #include "src/tint/sem/struct.h"
 #include "src/tint/sem/variable.h"
+#include "src/tint/type/array.h"
 #include "src/tint/type/bool.h"
 #include "src/tint/type/depth_multisampled_texture.h"
 #include "src/tint/type/depth_texture.h"
diff --git a/src/tint/inspector/resource_binding.cc b/src/tint/inspector/resource_binding.cc
index b809e8e..4fca85e 100644
--- a/src/tint/inspector/resource_binding.cc
+++ b/src/tint/inspector/resource_binding.cc
@@ -14,7 +14,7 @@
 
 #include "src/tint/inspector/resource_binding.h"
 
-#include "src/tint/sem/array.h"
+#include "src/tint/type/array.h"
 #include "src/tint/type/f32.h"
 #include "src/tint/type/i32.h"
 #include "src/tint/type/matrix.h"
@@ -50,7 +50,7 @@
         return ResourceBinding::SampledKind::kUnknown;
     }
 
-    if (auto* at = base_type->As<sem::Array>()) {
+    if (auto* at = base_type->As<type::Array>()) {
         base_type = at->ElemType();
     } else if (auto* mt = base_type->As<type::Matrix>()) {
         base_type = mt->type();
diff --git a/src/tint/program_builder.h b/src/tint/program_builder.h
index 03dca17..feacc38 100644
--- a/src/tint/program_builder.h
+++ b/src/tint/program_builder.h
@@ -90,10 +90,10 @@
 #include "src/tint/number.h"
 #include "src/tint/program.h"
 #include "src/tint/program_id.h"
-#include "src/tint/sem/array.h"
 #include "src/tint/sem/array_count.h"
 #include "src/tint/sem/constant.h"
 #include "src/tint/sem/struct.h"
+#include "src/tint/type/array.h"
 #include "src/tint/type/bool.h"
 #include "src/tint/type/depth_texture.h"
 #include "src/tint/type/external_texture.h"
diff --git a/src/tint/resolver/const_eval.cc b/src/tint/resolver/const_eval.cc
index 29eb86e..02b36d5 100644
--- a/src/tint/resolver/const_eval.cc
+++ b/src/tint/resolver/const_eval.cc
@@ -23,12 +23,12 @@
 #include <utility>
 
 #include "src/tint/program_builder.h"
-#include "src/tint/sem/array.h"
 #include "src/tint/sem/constant.h"
 #include "src/tint/sem/member_accessor_expression.h"
 #include "src/tint/sem/type_initializer.h"
 #include "src/tint/type/abstract_float.h"
 #include "src/tint/type/abstract_int.h"
+#include "src/tint/type/array.h"
 #include "src/tint/type/bool.h"
 #include "src/tint/type/f16.h"
 #include "src/tint/type/f32.h"
@@ -486,7 +486,7 @@
             auto* zero_el = ZeroValue(builder, m->ColumnType());
             return builder.create<Splat>(type, zero_el, m->columns());
         },
-        [&](const sem::Array* a) -> const ImplConstant* {
+        [&](const type::Array* a) -> const ImplConstant* {
             if (auto n = a->ConstantCount()) {
                 if (auto* zero_el = ZeroValue(builder, a->ElemType())) {
                     return builder.create<Splat>(type, zero_el, n.value());
@@ -547,7 +547,7 @@
             }
             return true;
         },
-        [&](const sem::Array* arr) {
+        [&](const type::Array* arr) {
             if (auto count = arr->ConstantCount()) {
                 for (size_t i = 0; i < count; i++) {
                     if (!Equal(a->Index(i), b->Index(i))) {
diff --git a/src/tint/resolver/const_eval_construction_test.cc b/src/tint/resolver/const_eval_construction_test.cc
index 30aef8a..2c3bd9b 100644
--- a/src/tint/resolver/const_eval_construction_test.cc
+++ b/src/tint/resolver/const_eval_construction_test.cc
@@ -1318,7 +1318,7 @@
 
     auto* sem = Sem().Get(expr);
     ASSERT_NE(sem, nullptr);
-    auto* arr = sem->Type()->As<sem::Array>();
+    auto* arr = sem->Type()->As<type::Array>();
     ASSERT_NE(arr, nullptr);
     EXPECT_TRUE(arr->ElemType()->Is<type::I32>());
     EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
@@ -1355,7 +1355,7 @@
 
     auto* sem = Sem().Get(expr);
     ASSERT_NE(sem, nullptr);
-    auto* arr = sem->Type()->As<sem::Array>();
+    auto* arr = sem->Type()->As<type::Array>();
     ASSERT_NE(arr, nullptr);
     EXPECT_TRUE(arr->ElemType()->Is<type::F32>());
     EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
@@ -1392,7 +1392,7 @@
 
     auto* sem = Sem().Get(expr);
     ASSERT_NE(sem, nullptr);
-    auto* arr = sem->Type()->As<sem::Array>();
+    auto* arr = sem->Type()->As<type::Array>();
     ASSERT_NE(arr, nullptr);
     EXPECT_TRUE(arr->ElemType()->Is<type::Vector>());
     EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
@@ -1443,7 +1443,7 @@
 
     auto* sem = Sem().Get(expr);
     ASSERT_NE(sem, nullptr);
-    auto* arr = sem->Type()->As<sem::Array>();
+    auto* arr = sem->Type()->As<type::Array>();
     ASSERT_NE(arr, nullptr);
     EXPECT_TRUE(arr->ElemType()->Is<sem::Struct>());
     EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
@@ -1480,7 +1480,7 @@
 
     auto* sem = Sem().Get(expr);
     ASSERT_NE(sem, nullptr);
-    auto* arr = sem->Type()->As<sem::Array>();
+    auto* arr = sem->Type()->As<type::Array>();
     ASSERT_NE(arr, nullptr);
     EXPECT_TRUE(arr->ElemType()->Is<type::I32>());
     EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
@@ -1517,7 +1517,7 @@
 
     auto* sem = Sem().Get(expr);
     ASSERT_NE(sem, nullptr);
-    auto* arr = sem->Type()->As<sem::Array>();
+    auto* arr = sem->Type()->As<type::Array>();
     ASSERT_NE(arr, nullptr);
     EXPECT_TRUE(arr->ElemType()->Is<type::F32>());
     EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
@@ -1555,7 +1555,7 @@
 
     auto* sem = Sem().Get(expr);
     ASSERT_NE(sem, nullptr);
-    auto* arr = sem->Type()->As<sem::Array>();
+    auto* arr = sem->Type()->As<type::Array>();
     ASSERT_NE(arr, nullptr);
     EXPECT_TRUE(arr->ElemType()->Is<type::Vector>());
     EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
@@ -1584,7 +1584,7 @@
 
     auto* sem = Sem().Get(expr);
     ASSERT_NE(sem, nullptr);
-    auto* arr = sem->Type()->As<sem::Array>();
+    auto* arr = sem->Type()->As<type::Array>();
     ASSERT_NE(arr, nullptr);
     EXPECT_TRUE(arr->ElemType()->Is<sem::Struct>());
     EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
@@ -2087,14 +2087,14 @@
     EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllEqual());
     EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
     EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
-    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Type()->Is<sem::Array>());
+    EXPECT_TRUE(sem->ConstantValue()->Index(0)->Type()->Is<type::Array>());
     EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(0)->As<i32>(), 1_i);
     EXPECT_EQ(sem->ConstantValue()->Index(0)->Index(1)->As<u32>(), 2_i);
 
     EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllEqual());
     EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
     EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
-    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Type()->Is<sem::Array>());
+    EXPECT_TRUE(sem->ConstantValue()->Index(1)->Type()->Is<type::Array>());
     EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(0)->As<i32>(), 1_f);
     EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(1)->As<u32>(), 2_f);
     EXPECT_EQ(sem->ConstantValue()->Index(1)->Index(2)->As<f32>(), 3_f);
diff --git a/src/tint/resolver/inferred_type_test.cc b/src/tint/resolver/inferred_type_test.cc
index d7f5548..6406bea 100644
--- a/src/tint/resolver/inferred_type_test.cc
+++ b/src/tint/resolver/inferred_type_test.cc
@@ -135,7 +135,7 @@
 
 TEST_F(ResolverInferredTypeTest, InferArray_Pass) {
     auto* type = ty.array(ty.u32(), 10_u);
-    auto* expected_type = create<sem::Array>(
+    auto* expected_type = create<type::Array>(
         create<type::U32>(), create<type::ConstantArrayCount>(10u), 4u, 4u * 10u, 4u, 4u);
 
     auto* ctor_expr = Construct(type);
diff --git a/src/tint/resolver/intrinsic_table.cc b/src/tint/resolver/intrinsic_table.cc
index 99ea338..f6b87f3 100644
--- a/src/tint/resolver/intrinsic_table.cc
+++ b/src/tint/resolver/intrinsic_table.cc
@@ -522,7 +522,7 @@
         return true;
     }
 
-    if (auto* a = ty->As<sem::Array>()) {
+    if (auto* a = ty->As<type::Array>()) {
         if (a->Count()->Is<type::RuntimeArrayCount>()) {
             T = a->ElemType();
             return true;
@@ -531,8 +531,8 @@
     return false;
 }
 
-const sem::Array* build_array(MatchState& state, const type::Type* el) {
-    return state.builder.create<sem::Array>(
+const type::Array* build_array(MatchState& state, const type::Type* el) {
+    return state.builder.create<type::Array>(
         el,
         /* count */ state.builder.create<type::RuntimeArrayCount>(),
         /* align */ 0u,
diff --git a/src/tint/resolver/intrinsic_table_test.cc b/src/tint/resolver/intrinsic_table_test.cc
index 6e12ed6c..4849c4c 100644
--- a/src/tint/resolver/intrinsic_table_test.cc
+++ b/src/tint/resolver/intrinsic_table_test.cc
@@ -253,7 +253,7 @@
 
 TEST_F(IntrinsicTableTest, MatchArray) {
     auto* arr =
-        create<sem::Array>(create<type::U32>(), create<type::RuntimeArrayCount>(), 4u, 4u, 4u, 4u);
+        create<type::Array>(create<type::U32>(), create<type::RuntimeArrayCount>(), 4u, 4u, 4u, 4u);
     auto* arr_ptr =
         create<type::Pointer>(arr, ast::AddressSpace::kStorage, ast::Access::kReadWrite);
     auto result = table->Lookup(BuiltinType::kArrayLength, utils::Vector{arr_ptr},
@@ -265,7 +265,7 @@
     ASSERT_EQ(result.sem->Parameters().Length(), 1u);
     auto* param_type = result.sem->Parameters()[0]->Type();
     ASSERT_TRUE(param_type->Is<type::Pointer>());
-    EXPECT_TRUE(param_type->As<type::Pointer>()->StoreType()->Is<sem::Array>());
+    EXPECT_TRUE(param_type->As<type::Pointer>()->StoreType()->Is<type::Array>());
 }
 
 TEST_F(IntrinsicTableTest, MismatchArray) {
@@ -958,7 +958,7 @@
 
 TEST_F(IntrinsicTableTest, MismatchTypeConversion) {
     auto* arr =
-        create<sem::Array>(create<type::U32>(), create<type::RuntimeArrayCount>(), 4u, 4u, 4u, 4u);
+        create<type::Array>(create<type::U32>(), create<type::RuntimeArrayCount>(), 4u, 4u, 4u, 4u);
     auto* f32 = create<type::F32>();
     auto result = table->Lookup(InitConvIntrinsic::kVec3, f32, utils::Vector{arr},
                                 sem::EvaluationStage::kConstant, Source{{12, 34}});
diff --git a/src/tint/resolver/is_host_shareable_test.cc b/src/tint/resolver/is_host_shareable_test.cc
index 1344379..1bc38b4 100644
--- a/src/tint/resolver/is_host_shareable_test.cc
+++ b/src/tint/resolver/is_host_shareable_test.cc
@@ -106,14 +106,14 @@
 }
 
 TEST_F(ResolverIsHostShareable, ArraySizedOfHostShareable) {
-    auto* arr = create<sem::Array>(create<type::I32>(), create<type::ConstantArrayCount>(5u), 4u,
-                                   20u, 4u, 4u);
+    auto* arr = create<type::Array>(create<type::I32>(), create<type::ConstantArrayCount>(5u), 4u,
+                                    20u, 4u, 4u);
     EXPECT_TRUE(r()->IsHostShareable(arr));
 }
 
 TEST_F(ResolverIsHostShareable, ArrayUnsizedOfHostShareable) {
     auto* arr =
-        create<sem::Array>(create<type::I32>(), create<type::RuntimeArrayCount>(), 4u, 4u, 4u, 4u);
+        create<type::Array>(create<type::I32>(), create<type::RuntimeArrayCount>(), 4u, 4u, 4u, 4u);
     EXPECT_TRUE(r()->IsHostShareable(arr));
 }
 
diff --git a/src/tint/resolver/is_storeable_test.cc b/src/tint/resolver/is_storeable_test.cc
index 7ed4877..58c0871 100644
--- a/src/tint/resolver/is_storeable_test.cc
+++ b/src/tint/resolver/is_storeable_test.cc
@@ -89,14 +89,14 @@
 }
 
 TEST_F(ResolverIsStorableTest, ArraySizedOfStorable) {
-    auto* arr = create<sem::Array>(create<type::I32>(), create<type::ConstantArrayCount>(5u), 4u,
-                                   20u, 4u, 4u);
+    auto* arr = create<type::Array>(create<type::I32>(), create<type::ConstantArrayCount>(5u), 4u,
+                                    20u, 4u, 4u);
     EXPECT_TRUE(r()->IsStorable(arr));
 }
 
 TEST_F(ResolverIsStorableTest, ArrayUnsizedOfStorable) {
     auto* arr =
-        create<sem::Array>(create<type::I32>(), create<type::RuntimeArrayCount>(), 4u, 4u, 4u, 4u);
+        create<type::Array>(create<type::I32>(), create<type::RuntimeArrayCount>(), 4u, 4u, 4u, 4u);
     EXPECT_TRUE(r()->IsStorable(arr));
 }
 
diff --git a/src/tint/resolver/materialize_test.cc b/src/tint/resolver/materialize_test.cc
index 8c63c1f..82f6bdd 100644
--- a/src/tint/resolver/materialize_test.cc
+++ b/src/tint/resolver/materialize_test.cc
@@ -117,7 +117,7 @@
                     }
                 }
             },
-            [&](const sem::Array* a) {
+            [&](const type::Array* a) {
                 auto count = a->ConstantCount();
                 ASSERT_NE(count, 0u);
                 for (uint32_t i = 0; i < count; i++) {
diff --git a/src/tint/resolver/override_test.cc b/src/tint/resolver/override_test.cc
index a97c92d..8d6b8e7 100644
--- a/src/tint/resolver/override_test.cc
+++ b/src/tint/resolver/override_test.cc
@@ -259,7 +259,7 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 
     {
-        auto* r = Sem().TransitivelyReferencedOverrides(Sem().Get<sem::Array>(arr_ty->type));
+        auto* r = Sem().TransitivelyReferencedOverrides(Sem().Get<type::Array>(arr_ty->type));
         ASSERT_NE(r, nullptr);
         auto& refs = *r;
         ASSERT_EQ(refs.Length(), 2u);
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 3033786..eb1b7a1 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -52,7 +52,6 @@
 #include "src/tint/ast/while_statement.h"
 #include "src/tint/ast/workgroup_attribute.h"
 #include "src/tint/resolver/uniformity.h"
-#include "src/tint/sem/array.h"
 #include "src/tint/sem/break_if_statement.h"
 #include "src/tint/sem/call.h"
 #include "src/tint/sem/for_loop_statement.h"
@@ -72,6 +71,7 @@
 #include "src/tint/sem/while_statement.h"
 #include "src/tint/type/abstract_float.h"
 #include "src/tint/type/abstract_int.h"
+#include "src/tint/type/array.h"
 #include "src/tint/type/atomic.h"
 #include "src/tint/type/depth_multisampled_texture.h"
 #include "src/tint/type/depth_texture.h"
@@ -927,7 +927,7 @@
     for (auto* var : transitively_referenced_overrides) {
         builder_->Sem().AddTransitivelyReferencedOverride(sem, var);
     }
-    if (auto* arr = sem->Type()->UnwrapRef()->As<sem::Array>()) {
+    if (auto* arr = sem->Type()->UnwrapRef()->As<type::Array>()) {
         auto* refs = builder_->Sem().TransitivelyReferencedOverrides(arr);
         if (refs) {
             for (auto* var : *refs) {
@@ -1742,9 +1742,9 @@
                               return target_ty ? target_ty : f32m(m->columns(), m->rows());
                           });
         },
-        [&](const sem::Array* a) -> const type::Type* {
+        [&](const type::Array* a) -> const type::Type* {
             const type::Type* target_el_ty = nullptr;
-            if (auto* target_arr_ty = As<sem::Array>(target_ty)) {
+            if (auto* target_arr_ty = As<type::Array>(target_ty)) {
                 target_el_ty = target_arr_ty->ElemType();
             }
             if (auto* el_ty = ConcreteType(a->ElemType(), target_el_ty, source)) {
@@ -1869,7 +1869,7 @@
     auto* obj_ty = obj_raw_ty->UnwrapRef();
     auto* ty = Switch(
         obj_ty,  //
-        [&](const sem::Array* arr) { return arr->ElemType(); },
+        [&](const type::Array* arr) { return arr->ElemType(); },
         [&](const type::Vector* vec) { return vec->type(); },
         [&](const type::Matrix* mat) {
             return builder_->create<type::Vector>(mat->type(), mat->rows());
@@ -2045,7 +2045,7 @@
             [&](const type::F16*) { return ct_init_or_conv(InitConvIntrinsic::kF16, nullptr); },
             [&](const type::F32*) { return ct_init_or_conv(InitConvIntrinsic::kF32, nullptr); },
             [&](const type::Bool*) { return ct_init_or_conv(InitConvIntrinsic::kBool, nullptr); },
-            [&](const sem::Array* arr) -> sem::Call* {
+            [&](const type::Array* arr) -> sem::Call* {
                 auto* call_target = array_inits_.GetOrCreate(
                     ArrayInitializerSig{{arr, args.Length(), args_stage}},
                     [&]() -> sem::TypeInitializer* {
@@ -2925,7 +2925,7 @@
     return result;
 }
 
-sem::Array* Resolver::Array(const ast::Array* arr) {
+type::Array* Resolver::Array(const ast::Array* arr) {
     if (!arr->type) {
         AddError("missing array element type", arr->source.End());
         return nullptr;
@@ -3053,11 +3053,11 @@
     return true;
 }
 
-sem::Array* Resolver::Array(const Source& el_source,
-                            const Source& count_source,
-                            const type::Type* el_ty,
-                            const type::ArrayCount* el_count,
-                            uint32_t explicit_stride) {
+type::Array* Resolver::Array(const Source& el_source,
+                             const Source& count_source,
+                             const type::Type* el_ty,
+                             const 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 ? utils::RoundUp<uint64_t>(el_align, el_size) : 0;
@@ -3076,9 +3076,9 @@
     } else if (el_count->Is<type::RuntimeArrayCount>()) {
         size = stride;
     }
-    auto* out = builder_->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));
+    auto* out = builder_->create<type::Array>(
+        el_ty, el_count, el_align, static_cast<uint32_t>(size), static_cast<uint32_t>(stride),
+        static_cast<uint32_t>(implicit_stride));
 
     if (!validator_.Array(out, el_source)) {
         return nullptr;
@@ -3642,7 +3642,7 @@
         return true;
     }
 
-    if (auto* arr = ty->As<sem::Array>()) {
+    if (auto* arr = ty->As<type::Array>()) {
         if (address_space != ast::AddressSpace::kStorage) {
             if (arr->Count()->Is<type::RuntimeArrayCount>()) {
                 AddError("runtime-sized arrays can only be used in the <storage> address space",
diff --git a/src/tint/resolver/resolver.h b/src/tint/resolver/resolver.h
index fdc706c..869cb19 100644
--- a/src/tint/resolver/resolver.h
+++ b/src/tint/resolver/resolver.h
@@ -272,7 +272,7 @@
     /// to the AST node.
     /// @returns the semantic Array information, or nullptr if an error is raised.
     /// @param arr the Array to get semantic information for
-    sem::Array* Array(const ast::Array* arr);
+    type::Array* Array(const ast::Array* arr);
 
     /// Resolves and validates the expression used as the count parameter of an array.
     /// @param count_expr the expression used as the second template parameter to an array<>.
@@ -297,11 +297,11 @@
     /// @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.
-    sem::Array* Array(const Source& el_source,
-                      const Source& count_source,
-                      const type::Type* el_ty,
-                      const type::ArrayCount* el_count,
-                      uint32_t explicit_stride);
+    type::Array* Array(const Source& el_source,
+                       const Source& count_source,
+                       const type::Type* el_ty,
+                       const 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
@@ -426,7 +426,7 @@
     // ArrayInitializerSig represents a unique array initializer signature.
     // It is a tuple of the array type, number of arguments provided and earliest evaluation stage.
     using ArrayInitializerSig =
-        utils::UnorderedKeyWrapper<std::tuple<const sem::Array*, size_t, sem::EvaluationStage>>;
+        utils::UnorderedKeyWrapper<std::tuple<const type::Array*, size_t, sem::EvaluationStage>>;
 
     // StructInitializerSig represents a unique structure initializer signature.
     // It is a tuple of the structure type, number of arguments provided and earliest evaluation
diff --git a/src/tint/resolver/resolver_test.cc b/src/tint/resolver/resolver_test.cc
index bb67764..a598875 100644
--- a/src/tint/resolver/resolver_test.cc
+++ b/src/tint/resolver/resolver_test.cc
@@ -439,7 +439,7 @@
     ASSERT_NE(TypeOf(a), nullptr);
     auto* ref = TypeOf(a)->As<type::Reference>();
     ASSERT_NE(ref, nullptr);
-    auto* ary = ref->StoreType()->As<sem::Array>();
+    auto* ary = ref->StoreType()->As<type::Array>();
     EXPECT_EQ(ary->Count(), create<type::ConstantArrayCount>(10u));
 }
 
@@ -452,7 +452,7 @@
     ASSERT_NE(TypeOf(a), nullptr);
     auto* ref = TypeOf(a)->As<type::Reference>();
     ASSERT_NE(ref, nullptr);
-    auto* ary = ref->StoreType()->As<sem::Array>();
+    auto* ary = ref->StoreType()->As<type::Array>();
     EXPECT_EQ(ary->Count(), create<type::ConstantArrayCount>(10u));
 }
 
@@ -467,7 +467,7 @@
     ASSERT_NE(TypeOf(a), nullptr);
     auto* ref = TypeOf(a)->As<type::Reference>();
     ASSERT_NE(ref, nullptr);
-    auto* ary = ref->StoreType()->As<sem::Array>();
+    auto* ary = ref->StoreType()->As<type::Array>();
     EXPECT_EQ(ary->Count(), create<type::ConstantArrayCount>(10u));
 }
 
@@ -482,7 +482,7 @@
     ASSERT_NE(TypeOf(a), nullptr);
     auto* ref = TypeOf(a)->As<type::Reference>();
     ASSERT_NE(ref, nullptr);
-    auto* ary = ref->StoreType()->As<sem::Array>();
+    auto* ary = ref->StoreType()->As<type::Array>();
     EXPECT_EQ(ary->Count(), create<type::ConstantArrayCount>(10u));
 }
 
@@ -497,7 +497,7 @@
     ASSERT_NE(TypeOf(a), nullptr);
     auto* ref = TypeOf(a)->As<type::Reference>();
     ASSERT_NE(ref, nullptr);
-    auto* ary = ref->StoreType()->As<sem::Array>();
+    auto* ary = ref->StoreType()->As<type::Array>();
     auto* sem_override = Sem().Get<sem::GlobalVariable>(override);
     ASSERT_NE(sem_override, nullptr);
     EXPECT_EQ(ary->Count(), create<sem::NamedOverrideArrayCount>(sem_override));
@@ -516,12 +516,12 @@
     ASSERT_NE(TypeOf(a), nullptr);
     auto* ref_a = TypeOf(a)->As<type::Reference>();
     ASSERT_NE(ref_a, nullptr);
-    auto* ary_a = ref_a->StoreType()->As<sem::Array>();
+    auto* ary_a = ref_a->StoreType()->As<type::Array>();
 
     ASSERT_NE(TypeOf(b), nullptr);
     auto* ref_b = TypeOf(b)->As<type::Reference>();
     ASSERT_NE(ref_b, nullptr);
-    auto* ary_b = ref_b->StoreType()->As<sem::Array>();
+    auto* ary_b = ref_b->StoreType()->As<type::Array>();
 
     auto* sem_override = Sem().Get<sem::GlobalVariable>(override);
     ASSERT_NE(sem_override, nullptr);
@@ -542,7 +542,7 @@
     ASSERT_NE(TypeOf(a), nullptr);
     auto* ref = TypeOf(a)->As<type::Reference>();
     ASSERT_NE(ref, nullptr);
-    auto* ary = ref->StoreType()->As<sem::Array>();
+    auto* ary = ref->StoreType()->As<type::Array>();
     auto* sem_override = Sem().Get<sem::GlobalVariable>(override);
     ASSERT_NE(sem_override, nullptr);
     EXPECT_EQ(ary->Count(), create<sem::UnnamedOverrideArrayCount>(Sem().Get(cnt)));
@@ -563,12 +563,12 @@
     ASSERT_NE(TypeOf(a), nullptr);
     auto* ref_a = TypeOf(a)->As<type::Reference>();
     ASSERT_NE(ref_a, nullptr);
-    auto* ary_a = ref_a->StoreType()->As<sem::Array>();
+    auto* ary_a = ref_a->StoreType()->As<type::Array>();
 
     ASSERT_NE(TypeOf(b), nullptr);
     auto* ref_b = TypeOf(b)->As<type::Reference>();
     ASSERT_NE(ref_b, nullptr);
-    auto* ary_b = ref_b->StoreType()->As<sem::Array>();
+    auto* ary_b = ref_b->StoreType()->As<type::Array>();
 
     auto* sem_override = Sem().Get<sem::GlobalVariable>(override);
     ASSERT_NE(sem_override, nullptr);
diff --git a/src/tint/resolver/resolver_test_helper.h b/src/tint/resolver/resolver_test_helper.h
index 8829a53..12d8fe3 100644
--- a/src/tint/resolver/resolver_test_helper.h
+++ b/src/tint/resolver/resolver_test_helper.h
@@ -667,7 +667,7 @@
         } else {
             count = b.create<type::ConstantArrayCount>(N);
         }
-        return b.create<sem::Array>(
+        return b.create<type::Array>(
             /* element */ el,
             /* count */ count,
             /* align */ el->Align(),
diff --git a/src/tint/resolver/type_initializer_validation_test.cc b/src/tint/resolver/type_initializer_validation_test.cc
index f38823d..6f7d78b 100644
--- a/src/tint/resolver/type_initializer_validation_test.cc
+++ b/src/tint/resolver/type_initializer_validation_test.cc
@@ -494,7 +494,7 @@
 
     auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
-    EXPECT_TRUE(call->Type()->Is<sem::Array>());
+    EXPECT_TRUE(call->Type()->Is<type::Array>());
     auto* ctor = call->Target()->As<sem::TypeInitializer>();
     ASSERT_NE(ctor, nullptr);
     EXPECT_EQ(call->Type(), ctor->ReturnType());
@@ -510,7 +510,7 @@
 
     auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
-    EXPECT_TRUE(call->Type()->Is<sem::Array>());
+    EXPECT_TRUE(call->Type()->Is<type::Array>());
     auto* ctor = call->Target()->As<sem::TypeInitializer>();
     ASSERT_NE(ctor, nullptr);
     EXPECT_EQ(call->Type(), ctor->ReturnType());
@@ -529,7 +529,7 @@
 
     auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
-    EXPECT_TRUE(call->Type()->Is<sem::Array>());
+    EXPECT_TRUE(call->Type()->Is<type::Array>());
     auto* ctor = call->Target()->As<sem::TypeInitializer>();
     ASSERT_NE(ctor, nullptr);
     EXPECT_EQ(call->Type(), ctor->ReturnType());
@@ -548,7 +548,7 @@
 
     auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
-    EXPECT_TRUE(call->Type()->Is<sem::Array>());
+    EXPECT_TRUE(call->Type()->Is<type::Array>());
     auto* ctor = call->Target()->As<sem::TypeInitializer>();
     ASSERT_NE(ctor, nullptr);
     EXPECT_EQ(call->Type(), ctor->ReturnType());
@@ -567,7 +567,7 @@
 
     auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
-    EXPECT_TRUE(call->Type()->Is<sem::Array>());
+    EXPECT_TRUE(call->Type()->Is<type::Array>());
     auto* ctor = call->Target()->As<sem::TypeInitializer>();
     ASSERT_NE(ctor, nullptr);
     EXPECT_EQ(call->Type(), ctor->ReturnType());
@@ -586,7 +586,7 @@
 
     auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
-    EXPECT_TRUE(call->Type()->Is<sem::Array>());
+    EXPECT_TRUE(call->Type()->Is<type::Array>());
     auto* ctor = call->Target()->As<sem::TypeInitializer>();
     ASSERT_NE(ctor, nullptr);
     EXPECT_EQ(call->Type(), ctor->ReturnType());
@@ -605,7 +605,7 @@
 
     auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
-    EXPECT_TRUE(call->Type()->Is<sem::Array>());
+    EXPECT_TRUE(call->Type()->Is<type::Array>());
     auto* ctor = call->Target()->As<sem::TypeInitializer>();
     ASSERT_NE(ctor, nullptr);
     EXPECT_EQ(call->Type(), ctor->ReturnType());
@@ -626,7 +626,7 @@
 
     auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
-    EXPECT_TRUE(call->Type()->Is<sem::Array>());
+    EXPECT_TRUE(call->Type()->Is<type::Array>());
     auto* ctor = call->Target()->As<sem::TypeInitializer>();
     ASSERT_NE(ctor, nullptr);
     EXPECT_EQ(call->Type(), ctor->ReturnType());
@@ -648,7 +648,7 @@
 
     auto* call = Sem().Get<sem::Call>(tc);
     ASSERT_NE(call, nullptr);
-    EXPECT_TRUE(call->Type()->Is<sem::Array>());
+    EXPECT_TRUE(call->Type()->Is<type::Array>());
     auto* ctor = call->Target()->As<sem::TypeInitializer>();
     ASSERT_NE(ctor, nullptr);
     EXPECT_EQ(call->Type(), ctor->ReturnType());
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index 82586cc..9d21314 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -47,7 +47,6 @@
 #include "src/tint/ast/variable_decl_statement.h"
 #include "src/tint/ast/vector.h"
 #include "src/tint/ast/workgroup_attribute.h"
-#include "src/tint/sem/array.h"
 #include "src/tint/sem/break_if_statement.h"
 #include "src/tint/sem/call.h"
 #include "src/tint/sem/for_loop_statement.h"
@@ -64,6 +63,7 @@
 #include "src/tint/sem/variable.h"
 #include "src/tint/sem/while_statement.h"
 #include "src/tint/type/abstract_numeric.h"
+#include "src/tint/type/array.h"
 #include "src/tint/type/atomic.h"
 #include "src/tint/type/depth_multisampled_texture.h"
 #include "src/tint/type/depth_texture.h"
@@ -183,7 +183,7 @@
 // https://gpuweb.github.io/gpuweb/wgsl/#plain-types-section
 bool Validator::IsPlain(const type::Type* type) const {
     return type->is_scalar() ||
-           type->IsAnyOf<type::Atomic, type::Vector, type::Matrix, sem::Array, sem::Struct>();
+           type->IsAnyOf<type::Atomic, type::Vector, type::Matrix, type::Array, sem::Struct>();
 }
 
 // https://gpuweb.github.io/gpuweb/wgsl/#fixed-footprint-types
@@ -193,7 +193,7 @@
         [&](const type::Vector*) { return true; },  //
         [&](const type::Matrix*) { return true; },  //
         [&](const type::Atomic*) { return true; },
-        [&](const sem::Array* arr) {
+        [&](const type::Array* arr) {
             return !arr->Count()->Is<type::RuntimeArrayCount>() &&
                    IsFixedFootprint(arr->ElemType());
         },
@@ -217,7 +217,7 @@
         type,  //
         [&](const type::Vector* vec) { return IsHostShareable(vec->type()); },
         [&](const type::Matrix* mat) { return IsHostShareable(mat->type()); },
-        [&](const sem::Array* arr) { return IsHostShareable(arr->ElemType()); },
+        [&](const type::Array* arr) { return IsHostShareable(arr->ElemType()); },
         [&](const sem::Struct* str) {
             for (auto* member : str->Members()) {
                 if (!IsHostShareable(member->Type())) {
@@ -398,7 +398,7 @@
 
     auto is_uniform_struct_or_array = [address_space](const type::Type* ty) {
         return address_space == ast::AddressSpace::kUniform &&
-               ty->IsAnyOf<sem::Array, sem::Struct>();
+               ty->IsAnyOf<type::Array, sem::Struct>();
     };
 
     auto is_uniform_struct = [address_space](const type::Type* ty) {
@@ -504,7 +504,7 @@
     }
 
     // For uniform buffer array members, validate that array elements are aligned to 16 bytes
-    if (auto* arr = store_ty->As<sem::Array>()) {
+    if (auto* arr = store_ty->As<type::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
@@ -1744,7 +1744,7 @@
 }
 
 bool Validator::ArrayInitializer(const ast::CallExpression* ctor,
-                                 const sem::Array* array_type) const {
+                                 const type::Array* array_type) const {
     auto& values = ctor->args;
     auto* elem_ty = array_type->ElemType();
     for (auto* value : values) {
@@ -1968,7 +1968,7 @@
     return true;
 }
 
-bool Validator::Array(const sem::Array* arr, const Source& el_source) const {
+bool Validator::Array(const type::Array* arr, const Source& el_source) const {
     auto* el_ty = arr->ElemType();
 
     if (!IsPlain(el_ty)) {
@@ -2021,7 +2021,7 @@
 
     utils::Hashset<uint32_t, 8> locations;
     for (auto* member : str->Members()) {
-        if (auto* r = member->Type()->As<sem::Array>()) {
+        if (auto* r = member->Type()->As<type::Array>()) {
             if (r->Count()->Is<type::RuntimeArrayCount>()) {
                 if (member != str->Members().Back()) {
                     AddError("runtime arrays may only appear as the last member of a struct",
@@ -2393,7 +2393,7 @@
 }
 
 bool Validator::IsArrayWithOverrideCount(const type::Type* ty) const {
-    if (auto* arr = ty->UnwrapRef()->As<sem::Array>()) {
+    if (auto* arr = ty->UnwrapRef()->As<type::Array>()) {
         if (arr->Count()->IsAnyOf<sem::NamedOverrideArrayCount, sem::UnnamedOverrideArrayCount>()) {
             return true;
         }
@@ -2474,7 +2474,7 @@
             return true;
         },
         [&](const sem::Struct*) { return check_sub_atomics(); },  //
-        [&](const sem::Array*) { return check_sub_atomics(); },   //
+        [&](const type::Array*) { return check_sub_atomics(); },  //
         [&](Default) { return true; });
 }
 
diff --git a/src/tint/resolver/validator.h b/src/tint/resolver/validator.h
index 1ecf573..0d6d343 100644
--- a/src/tint/resolver/validator.h
+++ b/src/tint/resolver/validator.h
@@ -154,7 +154,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 sem::Array* arr, const Source& el_source) const;
+    bool Array(const type::Array* arr, const Source& el_source) const;
 
     /// Validates an array stride attribute
     /// @param attr the stride attribute to validate
@@ -432,7 +432,7 @@
     /// @param ctor the call expresion to validate
     /// @param arr_type the type of the array
     /// @returns true on success, false otherwise
-    bool ArrayInitializer(const ast::CallExpression* ctor, const sem::Array* arr_type) const;
+    bool ArrayInitializer(const ast::CallExpression* ctor, const type::Array* arr_type) const;
 
     /// Validates a texture builtin function
     /// @param call the builtin call to validate
diff --git a/src/tint/resolver/validator_is_storeable_test.cc b/src/tint/resolver/validator_is_storeable_test.cc
index dccdcea..5bca58e 100644
--- a/src/tint/resolver/validator_is_storeable_test.cc
+++ b/src/tint/resolver/validator_is_storeable_test.cc
@@ -89,14 +89,14 @@
 }
 
 TEST_F(ValidatorIsStorableTest, ArraySizedOfStorable) {
-    auto* arr = create<sem::Array>(create<type::I32>(), create<type::ConstantArrayCount>(5u), 4u,
-                                   20u, 4u, 4u);
+    auto* arr = create<type::Array>(create<type::I32>(), create<type::ConstantArrayCount>(5u), 4u,
+                                    20u, 4u, 4u);
     EXPECT_TRUE(v()->IsStorable(arr));
 }
 
 TEST_F(ValidatorIsStorableTest, ArrayUnsizedOfStorable) {
     auto* arr =
-        create<sem::Array>(create<type::I32>(), create<type::RuntimeArrayCount>(), 4u, 4u, 4u, 4u);
+        create<type::Array>(create<type::I32>(), create<type::RuntimeArrayCount>(), 4u, 4u, 4u, 4u);
     EXPECT_TRUE(v()->IsStorable(arr));
 }
 
diff --git a/src/tint/sem/array_count.cc b/src/tint/sem/array_count.cc
index 8bb09bc..08719ad 100644
--- a/src/tint/sem/array_count.cc
+++ b/src/tint/sem/array_count.cc
@@ -34,6 +34,10 @@
     return false;
 }
 
+std::string NamedOverrideArrayCount::FriendlyName(const SymbolTable& symbols) const {
+    return symbols.NameFor(variable->Declaration()->symbol);
+}
+
 UnnamedOverrideArrayCount::UnnamedOverrideArrayCount(const Expression* e) : Base(), expr(e) {}
 UnnamedOverrideArrayCount::~UnnamedOverrideArrayCount() = default;
 
@@ -48,4 +52,8 @@
     return false;
 }
 
+std::string UnnamedOverrideArrayCount::FriendlyName(const SymbolTable&) const {
+    return "[unnamed override-expression]";
+}
+
 }  // namespace tint::sem
diff --git a/src/tint/sem/array_count.h b/src/tint/sem/array_count.h
index 0a64cf3..d948106 100644
--- a/src/tint/sem/array_count.h
+++ b/src/tint/sem/array_count.h
@@ -15,6 +15,8 @@
 #ifndef SRC_TINT_SEM_ARRAY_COUNT_H_
 #define SRC_TINT_SEM_ARRAY_COUNT_H_
 
+#include <string>
+
 #include "src/tint/sem/expression.h"
 #include "src/tint/sem/variable.h"
 #include "src/tint/type/array_count.h"
@@ -41,6 +43,10 @@
     /// @returns true if this array count is equal to the given array count
     bool Equals(const type::ArrayCount& t) const override;
 
+    /// @param symbols the symbol table
+    /// @returns the friendly name for this array count
+    std::string FriendlyName(const SymbolTable& symbols) const override;
+
     /// The `override` variable.
     const GlobalVariable* variable;
 };
@@ -66,6 +72,10 @@
     /// @returns true if this array count is equal to the given array count
     bool Equals(const type::ArrayCount& t) const override;
 
+    /// @param symbols the symbol table
+    /// @returns the friendly name for this array count
+    std::string FriendlyName(const SymbolTable& symbols) const override;
+
     /// The unnamed override expression.
     /// Note: Each AST expression gets a unique semantic expression node, so two equivalent AST
     /// expressions will not result in the same `expr` pointer. This property is important to ensure
diff --git a/src/tint/sem/type_mappings.h b/src/tint/sem/type_mappings.h
index 9e0f290..6502f9a 100644
--- a/src/tint/sem/type_mappings.h
+++ b/src/tint/sem/type_mappings.h
@@ -36,7 +36,6 @@
 class WhileStatement;
 }  // namespace tint::ast
 namespace tint::sem {
-class Array;
 class Expression;
 class ForLoopStatement;
 class Function;
@@ -51,6 +50,7 @@
 class WhileStatement;
 }  // namespace tint::sem
 namespace tint::type {
+class Array;
 class Type;
 }  // namespace tint::type
 
@@ -62,7 +62,7 @@
 /// rules will be used to infer the return type based on the argument type.
 struct TypeMappings {
     //! @cond Doxygen_Suppress
-    Array* operator()(ast::Array*);
+    type::Array* operator()(ast::Array*);
     Expression* operator()(ast::Expression*);
     ForLoopStatement* operator()(ast::ForLoopStatement*);
     Function* operator()(ast::Function*);
diff --git a/src/tint/transform/array_length_from_uniform.cc b/src/tint/transform/array_length_from_uniform.cc
index e49c215..fc59021 100644
--- a/src/tint/transform/array_length_from_uniform.cc
+++ b/src/tint/transform/array_length_from_uniform.cc
@@ -141,14 +141,14 @@
             //                             array_stride
             const ast::Expression* total_size = total_storage_buffer_size;
             auto* storage_buffer_type = storage_buffer_sem->Type()->UnwrapRef();
-            const sem::Array* array_type = nullptr;
+            const type::Array* array_type = nullptr;
             if (auto* str = storage_buffer_type->As<sem::Struct>()) {
                 // The variable is a struct, so subtract the byte offset of the array
                 // member.
                 auto* array_member_sem = str->Members().Back();
-                array_type = array_member_sem->Type()->As<sem::Array>();
+                array_type = array_member_sem->Type()->As<type::Array>();
                 total_size = b.Sub(total_storage_buffer_size, u32(array_member_sem->Offset()));
-            } else if (auto* arr = storage_buffer_type->As<sem::Array>()) {
+            } else if (auto* arr = storage_buffer_type->As<type::Array>()) {
                 array_type = arr;
             } else {
                 TINT_ICE(Transform, b.Diagnostics())
diff --git a/src/tint/transform/calculate_array_length.cc b/src/tint/transform/calculate_array_length.cc
index 25cec21..65821c9 100644
--- a/src/tint/transform/calculate_array_length.cc
+++ b/src/tint/transform/calculate_array_length.cc
@@ -202,16 +202,16 @@
                             const ast::Expression* total_size =
                                 b.Expr(buffer_size_result->variable);
 
-                            const sem::Array* array_type = Switch(
+                            const type::Array* array_type = Switch(
                                 storage_buffer_type->StoreType(),
                                 [&](const sem::Struct* str) {
                                     // The variable is a struct, so subtract the byte offset of
                                     // the array member.
                                     auto* array_member_sem = str->Members().Back();
                                     total_size = b.Sub(total_size, u32(array_member_sem->Offset()));
-                                    return array_member_sem->Type()->As<sem::Array>();
+                                    return array_member_sem->Type()->As<type::Array>();
                                 },
-                                [&](const sem::Array* arr) { return arr; });
+                                [&](const type::Array* arr) { return arr; });
 
                             if (!array_type) {
                                 TINT_ICE(Transform, b.Diagnostics())
diff --git a/src/tint/transform/decompose_memory_access.cc b/src/tint/transform/decompose_memory_access.cc
index 16d6388..df43788 100644
--- a/src/tint/transform/decompose_memory_access.cc
+++ b/src/tint/transform/decompose_memory_access.cc
@@ -26,12 +26,12 @@
 #include "src/tint/ast/type_name.h"
 #include "src/tint/ast/unary_op.h"
 #include "src/tint/program_builder.h"
-#include "src/tint/sem/array.h"
 #include "src/tint/sem/call.h"
 #include "src/tint/sem/member_accessor_expression.h"
 #include "src/tint/sem/statement.h"
 #include "src/tint/sem/struct.h"
 #include "src/tint/sem/variable.h"
+#include "src/tint/type/array.h"
 #include "src/tint/type/atomic.h"
 #include "src/tint/type/reference.h"
 #include "src/tint/utils/block_allocator.h"
@@ -490,7 +490,7 @@
                         },
                         utils::Empty);
                     b.AST().AddFunction(func);
-                } else if (auto* arr_ty = el_ty->As<sem::Array>()) {
+                } else if (auto* arr_ty = el_ty->As<type::Array>()) {
                     // fn load_func(buffer : buf_ty, offset : u32) -> array<T, N> {
                     //   var arr : array<T, N>;
                     //   for (var i = 0u; i < array_count; i = i + 1) {
@@ -592,7 +592,7 @@
                 } else {
                     auto body = Switch<utils::Vector<const ast::Statement*, 8>>(
                         el_ty,  //
-                        [&](const sem::Array* arr_ty) {
+                        [&](const type::Array* arr_ty) {
                             // fn store_func(buffer : buf_ty, offset : u32, value : el_ty) {
                             //   var array = value; // No dynamic indexing on constant arrays
                             //   for (var i = 0u; i < array_count; i = i + 1) {
@@ -928,7 +928,7 @@
         if (auto* accessor = node->As<ast::IndexAccessorExpression>()) {
             if (auto access = state.TakeAccess(accessor->object)) {
                 // X[Y]
-                if (auto* arr = access.type->As<sem::Array>()) {
+                if (auto* arr = access.type->As<type::Array>()) {
                     auto* offset = state.Mul(arr->Stride(), accessor->index);
                     state.AddAccess(accessor, {
                                                   access.var,
diff --git a/src/tint/transform/decompose_strided_array.cc b/src/tint/transform/decompose_strided_array.cc
index 73a6629..4f5410f 100644
--- a/src/tint/transform/decompose_strided_array.cc
+++ b/src/tint/transform/decompose_strided_array.cc
@@ -32,7 +32,7 @@
 namespace tint::transform {
 namespace {
 
-using DecomposedArrays = std::unordered_map<const sem::Array*, Symbol>;
+using DecomposedArrays = std::unordered_map<const type::Array*, Symbol>;
 
 bool ShouldRun(const Program* program) {
     for (auto* node : program->ASTNodes().Objects()) {
@@ -66,7 +66,7 @@
 
     // Maps an array type in the source program to the name of the struct wrapper
     // type in the target program.
-    std::unordered_map<const sem::Array*, Symbol> decomposed;
+    std::unordered_map<const type::Array*, Symbol> decomposed;
 
     // Find and replace all arrays with a @stride attribute with a array that has
     // the @stride removed. If the source array stride does not match the natural
@@ -105,7 +105,7 @@
     // Example: `arr[i]` -> `arr[i].el`
     ctx.ReplaceAll([&](const ast::IndexAccessorExpression* idx) -> const ast::Expression* {
         if (auto* ty = src->TypeOf(idx->object)) {
-            if (auto* arr = ty->UnwrapRef()->As<sem::Array>()) {
+            if (auto* arr = ty->UnwrapRef()->As<type::Array>()) {
                 if (!arr->IsStrideImplicit()) {
                     auto* expr = ctx.CloneWithoutTransform(idx);
                     return b.MemberAccessor(expr, kMemberName);
@@ -127,7 +127,7 @@
         if (!expr->args.IsEmpty()) {
             if (auto* call = sem.Get(expr)->UnwrapMaterialize()->As<sem::Call>()) {
                 if (auto* ctor = call->Target()->As<sem::TypeInitializer>()) {
-                    if (auto* arr = ctor->ReturnType()->As<sem::Array>()) {
+                    if (auto* arr = ctor->ReturnType()->As<type::Array>()) {
                         // Begin by cloning the array initializer type or name
                         // If this is an unaliased array, this may add a new entry to
                         // decomposed.
diff --git a/src/tint/transform/localize_struct_array_assignment.cc b/src/tint/transform/localize_struct_array_assignment.cc
index 7096700..8aaa276 100644
--- a/src/tint/transform/localize_struct_array_assignment.cc
+++ b/src/tint/transform/localize_struct_array_assignment.cc
@@ -157,7 +157,7 @@
                     // Indexing a member access expr?
                     if (auto* ma = ia->object->As<ast::MemberAccessorExpression>()) {
                         // That accesses an array?
-                        if (src->TypeOf(ma)->UnwrapRef()->Is<sem::Array>()) {
+                        if (src->TypeOf(ma)->UnwrapRef()->Is<type::Array>()) {
                             result = true;
                             return ast::TraverseAction::Stop;
                         }
diff --git a/src/tint/transform/module_scope_var_to_entry_point_param.cc b/src/tint/transform/module_scope_var_to_entry_point_param.cc
index 288b8f1..c54d586 100644
--- a/src/tint/transform/module_scope_var_to_entry_point_param.cc
+++ b/src/tint/transform/module_scope_var_to_entry_point_param.cc
@@ -52,7 +52,7 @@
     type = type->UnwrapRef();
     if (type->Is<type::Matrix>()) {
         return true;
-    } else if (auto* ary = type->As<sem::Array>()) {
+    } else if (auto* ary = type->As<type::Array>()) {
         return ContainsMatrix(ary->ElemType());
     } else if (auto* str = type->As<sem::Struct>()) {
         for (auto* member : str->Members()) {
@@ -95,7 +95,7 @@
             auto* ast_str = str->Declaration();
             ctx.dst->AST().AddTypeDecl(ctx.Clone(ast_str));
             ctx.Remove(ctx.src->AST().GlobalDeclarations(), ast_str);
-        } else if (auto* arr = ty->As<sem::Array>()) {
+        } else if (auto* arr = ty->As<type::Array>()) {
             CloneStructTypes(arr->ElemType());
         }
     }
@@ -146,7 +146,7 @@
                 attributes.Push(ctx.dst->Disable(ast::DisabledValidation::kIgnoreAddressSpace));
 
                 auto* param_type = store_type();
-                if (auto* arr = ty->As<sem::Array>();
+                if (auto* arr = ty->As<type::Array>();
                     arr && arr->Count()->Is<type::RuntimeArrayCount>()) {
                     // Wrap runtime-sized arrays in structures, so that we can declare pointers to
                     // them. Ideally we'd just emit the array itself as a pointer, but this is not
diff --git a/src/tint/transform/pad_structs.cc b/src/tint/transform/pad_structs.cc
index 64ce97c..f34c796 100644
--- a/src/tint/transform/pad_structs.cc
+++ b/src/tint/transform/pad_structs.cc
@@ -83,7 +83,7 @@
             if (ty->Is<sem::Struct>() && str->UsedAs(ast::AddressSpace::kUniform)) {
                 // std140 structs should be padded out to 16 bytes.
                 size = utils::RoundUp(16u, size);
-            } else if (auto* array_ty = ty->As<sem::Array>()) {
+            } else if (auto* array_ty = ty->As<type::Array>()) {
                 if (array_ty->Count()->Is<type::RuntimeArrayCount>()) {
                     has_runtime_sized_array = true;
                 }
diff --git a/src/tint/transform/preserve_padding.cc b/src/tint/transform/preserve_padding.cc
index 0dd3ea2..e2f7e00 100644
--- a/src/tint/transform/preserve_padding.cc
+++ b/src/tint/transform/preserve_padding.cc
@@ -132,7 +132,7 @@
 
         return Switch(
             ty,  //
-            [&](const sem::Array* arr) {
+            [&](const type::Array* arr) {
                 // Call a helper function that uses a loop to assigns each element separately.
                 return call_helper([&]() {
                     utils::Vector<const ast::Statement*, 8> body;
@@ -171,7 +171,7 @@
     bool HasPadding(const type::Type* ty) {
         return Switch(
             ty,  //
-            [&](const sem::Array* arr) {
+            [&](const type::Array* arr) {
                 auto* elem_ty = arr->ElemType();
                 if (elem_ty->Size() % elem_ty->Align() > 0) {
                     return true;
diff --git a/src/tint/transform/promote_initializers_to_let.cc b/src/tint/transform/promote_initializers_to_let.cc
index 7d81989..a6a5cf6 100644
--- a/src/tint/transform/promote_initializers_to_let.cc
+++ b/src/tint/transform/promote_initializers_to_let.cc
@@ -64,7 +64,7 @@
         }
 
         auto* src_ty = expr->Type();
-        if (!src_ty->IsAnyOf<sem::Array, sem::Struct>()) {
+        if (!src_ty->IsAnyOf<type::Array, sem::Struct>()) {
             // We only care about array and struct initializers
             return true;
         }
diff --git a/src/tint/transform/robustness.cc b/src/tint/transform/robustness.cc
index 34fb105..45b138e 100644
--- a/src/tint/transform/robustness.cc
+++ b/src/tint/transform/robustness.cc
@@ -104,7 +104,7 @@
 
                 return b.Call("min", idx(), u32(mat->columns() - 1u));
             },
-            [&](const sem::Array* arr) -> const ast::Expression* {
+            [&](const type::Array* arr) -> const ast::Expression* {
                 const ast::Expression* max = nullptr;
                 if (arr->Count()->Is<type::RuntimeArrayCount>()) {
                     // Size is unknown until runtime.
@@ -122,7 +122,7 @@
                     // Note: Don't be tempted to use the array override variable as an expression
                     // here, the name might be shadowed!
                     b.Diagnostics().add_error(diag::System::Transform,
-                                              sem::Array::kErrExpectedConstantCount);
+                                              type::Array::kErrExpectedConstantCount);
                     return nullptr;
                 }
 
diff --git a/src/tint/transform/single_entry_point.cc b/src/tint/transform/single_entry_point.cc
index 694cd2d..f76eda9 100644
--- a/src/tint/transform/single_entry_point.cc
+++ b/src/tint/transform/single_entry_point.cc
@@ -70,7 +70,7 @@
             decl,  //
             [&](const ast::TypeDecl* ty) {
                 // Strip aliases that reference unused override declarations.
-                if (auto* arr = sem.Get(ty)->As<sem::Array>()) {
+                if (auto* arr = sem.Get(ty)->As<type::Array>()) {
                     auto* refs = sem.TransitivelyReferencedOverrides(arr);
                     if (refs) {
                         for (auto* o : *refs) {
diff --git a/src/tint/transform/spirv_atomic.cc b/src/tint/transform/spirv_atomic.cc
index 56b61c3..6b55e5d 100644
--- a/src/tint/transform/spirv_atomic.cc
+++ b/src/tint/transform/spirv_atomic.cc
@@ -198,7 +198,7 @@
             [&](const type::I32*) { return b.ty.atomic(CreateASTTypeFor(ctx, ty)); },
             [&](const type::U32*) { return b.ty.atomic(CreateASTTypeFor(ctx, ty)); },
             [&](const sem::Struct* str) { return b.ty.type_name(Fork(str->Declaration()).name); },
-            [&](const sem::Array* arr) -> const ast::Type* {
+            [&](const type::Array* arr) -> const ast::Type* {
                 if (arr->Count()->Is<type::RuntimeArrayCount>()) {
                     return b.ty.array(AtomicTypeFor(arr->ElemType()));
                 }
diff --git a/src/tint/transform/std140.cc b/src/tint/transform/std140.cc
index d466a7c..d0bc205 100644
--- a/src/tint/transform/std140.cc
+++ b/src/tint/transform/std140.cc
@@ -129,7 +129,7 @@
     bool ShouldRun() const {
         // Returns true if the type needs to be forked for std140 usage.
         auto needs_fork = [&](const type::Type* ty) {
-            while (auto* arr = ty->As<sem::Array>()) {
+            while (auto* arr = ty->As<type::Array>()) {
                 ty = arr->ElemType();
             }
             if (auto* mat = ty->As<type::Matrix>()) {
@@ -426,7 +426,7 @@
                 }
                 return nullptr;
             },
-            [&](const sem::Array* arr) -> const ast::Type* {
+            [&](const type::Array* arr) -> const ast::Type* {
                 if (auto* std140 = Std140Type(arr->ElemType())) {
                     utils::Vector<const ast::Attribute*, 1> attrs;
                     if (!arr->IsStrideImplicit()) {
@@ -631,7 +631,7 @@
         return Switch(
             ty,  //
             [&](const sem::Struct* str) { return sym.NameFor(str->Name()); },
-            [&](const sem::Array* arr) {
+            [&](const type::Array* arr) {
                 auto count = arr->ConstantCount();
                 if (!count) {
                     // Non-constant counts should not be possible:
@@ -730,7 +730,7 @@
                             << "failed to find std140 matrix info for: " << src->FriendlyName(ty);
                     }
                 },  //
-                [&](const sem::Array* arr) {
+                [&](const type::Array* arr) {
                     // Converting an array. Create a function var for the converted array, and
                     // loop over the input elements, converting each and assigning the result to
                     // the local array.
@@ -1081,7 +1081,7 @@
             auto name = "p" + std::to_string(dyn_idx->slot);
             return Switch(
                 ty,  //
-                [&](const sem::Array* arr) -> ExprTypeName {
+                [&](const type::Array* arr) -> ExprTypeName {
                     auto* idx = dynamic_index(dyn_idx->slot);
                     auto* expr = b.IndexAccessor(lhs, idx);
                     return {expr, arr->ElemType(), name};
@@ -1134,7 +1134,7 @@
                 ty = member->Type();
                 return {expr, ty, member_name};
             },  //
-            [&](const sem::Array* arr) -> ExprTypeName {
+            [&](const type::Array* arr) -> ExprTypeName {
                 auto* expr = b.IndexAccessor(lhs, idx);
                 return {expr, arr->ElemType(), std::to_string(idx)};
             },  //
diff --git a/src/tint/transform/transform.cc b/src/tint/transform/transform.cc
index b47f687..55bb715 100644
--- a/src/tint/transform/transform.cc
+++ b/src/tint/transform/transform.cc
@@ -100,7 +100,7 @@
         auto* el = CreateASTTypeFor(ctx, v->type());
         return ctx.dst->create<ast::Vector>(el, v->Width());
     }
-    if (auto* a = ty->As<sem::Array>()) {
+    if (auto* a = ty->As<type::Array>()) {
         auto* el = CreateASTTypeFor(ctx, a->ElemType());
         utils::Vector<const ast::Attribute*, 1> attrs;
         if (!a->IsStrideImplicit()) {
@@ -133,7 +133,7 @@
         if (auto count = a->ConstantCount()) {
             return ctx.dst->ty.array(el, u32(count.value()), std::move(attrs));
         }
-        TINT_ICE(Transform, ctx.dst->Diagnostics()) << sem::Array::kErrExpectedConstantCount;
+        TINT_ICE(Transform, ctx.dst->Diagnostics()) << type::Array::kErrExpectedConstantCount;
         return ctx.dst->ty.array(el, u32(1), std::move(attrs));
     }
     if (auto* s = ty->As<sem::Struct>()) {
diff --git a/src/tint/transform/transform_test.cc b/src/tint/transform/transform_test.cc
index 8f20c14..2520d0b 100644
--- a/src/tint/transform/transform_test.cc
+++ b/src/tint/transform/transform_test.cc
@@ -69,8 +69,8 @@
 
 TEST_F(CreateASTTypeForTest, ArrayImplicitStride) {
     auto* arr = create([](ProgramBuilder& b) {
-        return b.create<sem::Array>(b.create<type::F32>(), b.create<type::ConstantArrayCount>(2u),
-                                    4u, 4u, 32u, 32u);
+        return b.create<type::Array>(b.create<type::F32>(), b.create<type::ConstantArrayCount>(2u),
+                                     4u, 4u, 32u, 32u);
     });
     ASSERT_TRUE(arr->Is<ast::Array>());
     ASSERT_TRUE(arr->As<ast::Array>()->type->Is<ast::F32>());
@@ -83,8 +83,8 @@
 
 TEST_F(CreateASTTypeForTest, ArrayNonImplicitStride) {
     auto* arr = create([](ProgramBuilder& b) {
-        return b.create<sem::Array>(b.create<type::F32>(), b.create<type::ConstantArrayCount>(2u),
-                                    4u, 4u, 64u, 32u);
+        return b.create<type::Array>(b.create<type::F32>(), b.create<type::ConstantArrayCount>(2u),
+                                     4u, 4u, 64u, 32u);
     });
     ASSERT_TRUE(arr->Is<ast::Array>());
     ASSERT_TRUE(arr->As<ast::Array>()->type->Is<ast::F32>());
diff --git a/src/tint/transform/var_for_dynamic_index.cc b/src/tint/transform/var_for_dynamic_index.cc
index 1039ca2..de69d53 100644
--- a/src/tint/transform/var_for_dynamic_index.cc
+++ b/src/tint/transform/var_for_dynamic_index.cc
@@ -47,7 +47,7 @@
         }
 
         auto* indexed = sem.Get(object_expr);
-        if (!indexed->Type()->IsAnyOf<sem::Array, type::Matrix>()) {
+        if (!indexed->Type()->IsAnyOf<type::Array, type::Matrix>()) {
             // We only care about array and matrices.
             return true;
         }
diff --git a/src/tint/transform/zero_init_workgroup_memory.cc b/src/tint/transform/zero_init_workgroup_memory.cc
index 3f1a4fe..a6fe234 100644
--- a/src/tint/transform/zero_init_workgroup_memory.cc
+++ b/src/tint/transform/zero_init_workgroup_memory.cc
@@ -333,7 +333,7 @@
             return true;
         }
 
-        if (auto* arr = ty->As<sem::Array>()) {
+        if (auto* arr = ty->As<type::Array>()) {
             auto get_el = [&](uint32_t num_values) {
                 // num_values is the number of values to zero for the element type.
                 // The number of iterations required to zero the array and its elements is:
@@ -343,7 +343,7 @@
                 auto count = arr->ConstantCount();
                 if (!count) {
                     ctx.dst->Diagnostics().add_error(diag::System::Transform,
-                                                     sem::Array::kErrExpectedConstantCount);
+                                                     type::Array::kErrExpectedConstantCount);
                     return Expression{};  // error
                 }
                 auto modulo = num_values * count.value();
@@ -449,7 +449,7 @@
                 }
             }
         }
-        if (ty->Is<sem::Array>()) {
+        if (ty->Is<type::Array>()) {
             return false;
         }
         // True for all other storable types
diff --git a/src/tint/sem/array.cc b/src/tint/type/array.cc
similarity index 78%
rename from src/tint/sem/array.cc
rename to src/tint/type/array.cc
index d936c94..1d1fed5 100644
--- a/src/tint/sem/array.cc
+++ b/src/tint/type/array.cc
@@ -1,4 +1,4 @@
-// Copyright 2021 The Tint Authors.
+// Copyright 2022 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.
@@ -12,19 +12,18 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/tint/sem/array.h"
+#include "src/tint/type/array.h"
 
 #include <string>
 
 #include "src/tint/ast/variable.h"
 #include "src/tint/debug.h"
-#include "src/tint/sem/variable.h"
 #include "src/tint/symbol_table.h"
 #include "src/tint/utils/hash.h"
 
-TINT_INSTANTIATE_TYPEINFO(tint::sem::Array);
+TINT_INSTANTIATE_TYPEINFO(tint::type::Array);
 
-namespace tint::sem {
+namespace tint::type {
 
 namespace {
 
@@ -39,8 +38,7 @@
             flags.Add(type::TypeFlag::kCreationFixedFootprint);
         }
     }
-    if (count->IsAnyOf<type::ConstantArrayCount, sem::NamedOverrideArrayCount,
-                       sem::UnnamedOverrideArrayCount>()) {
+    if (!count->Is<type::RuntimeArrayCount>()) {
         if (element->HasFixedFootprint()) {
             flags.Add(type::TypeFlag::kFixedFootprint);
         }
@@ -67,7 +65,7 @@
       size_(size),
       stride_(stride),
       implicit_stride_(implicit_stride) {
-    TINT_ASSERT(Semantic, element_);
+    TINT_ASSERT(Type, element_);
 }
 
 size_t Array::Hash() const {
@@ -90,13 +88,12 @@
         out << "@stride(" << stride_ << ") ";
     }
     out << "array<" << element_->FriendlyName(symbols);
-    if (auto* const_count = count_->As<type::ConstantArrayCount>()) {
-        out << ", " << const_count->value;
-    } else if (auto* named_override_count = count_->As<sem::NamedOverrideArrayCount>()) {
-        out << ", " << symbols.NameFor(named_override_count->variable->Declaration()->symbol);
-    } else if (count_->Is<sem::UnnamedOverrideArrayCount>()) {
-        out << ", [unnamed override-expression]";
+
+    auto count_str = count_->FriendlyName(symbols);
+    if (!count_str.empty()) {
+        out << ", " << count_str;
     }
+
     out << ">";
     return out.str();
 }
@@ -109,4 +106,4 @@
     return size_;
 }
 
-}  // namespace tint::sem
+}  // namespace tint::type
diff --git a/src/tint/sem/array.h b/src/tint/type/array.h
similarity index 91%
rename from src/tint/sem/array.h
rename to src/tint/type/array.h
index 93b9fa6..d6defdd 100644
--- a/src/tint/sem/array.h
+++ b/src/tint/type/array.h
@@ -12,29 +12,22 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef SRC_TINT_SEM_ARRAY_H_
-#define SRC_TINT_SEM_ARRAY_H_
+#ifndef SRC_TINT_TYPE_ARRAY_H_
+#define SRC_TINT_TYPE_ARRAY_H_
 
 #include <stdint.h>
 #include <optional>
 #include <string>
 #include <variant>
 
-#include "src/tint/sem/array_count.h"
-#include "src/tint/sem/node.h"
+#include "src/tint/type/array_count.h"
 #include "src/tint/type/type.h"
 #include "src/tint/utils/compiler_macros.h"
 #include "src/tint/utils/unique_vector.h"
 
-// Forward declarations
-namespace tint::sem {
-class Expression;
-class GlobalVariable;
-}  // namespace tint::sem
+namespace tint::type {
 
-namespace tint::sem {
-
-/// Array holds the semantic information for Array nodes.
+/// Array holds the type information for Array nodes.
 class Array final : public Castable<Array, type::Type> {
   public:
     /// An error message string stating that the array count was expected to be a constant
@@ -117,6 +110,6 @@
     const uint32_t implicit_stride_;
 };
 
-}  // namespace tint::sem
+}  // namespace tint::type
 
-#endif  // SRC_TINT_SEM_ARRAY_H_
+#endif  // SRC_TINT_TYPE_ARRAY_H_
diff --git a/src/tint/type/array_count.cc b/src/tint/type/array_count.cc
index fbf2c3d..f97e94f 100644
--- a/src/tint/type/array_count.cc
+++ b/src/tint/type/array_count.cc
@@ -37,6 +37,10 @@
     return false;
 }
 
+std::string ConstantArrayCount::FriendlyName(const SymbolTable&) const {
+    return std::to_string(value);
+}
+
 RuntimeArrayCount::RuntimeArrayCount() : Base() {}
 RuntimeArrayCount::~RuntimeArrayCount() = default;
 
@@ -48,4 +52,8 @@
     return other.Is<RuntimeArrayCount>();
 }
 
+std::string RuntimeArrayCount::FriendlyName(const SymbolTable&) const {
+    return "";
+}
+
 }  // namespace tint::type
diff --git a/src/tint/type/array_count.h b/src/tint/type/array_count.h
index d00aa3b..76c1588 100644
--- a/src/tint/type/array_count.h
+++ b/src/tint/type/array_count.h
@@ -16,7 +16,9 @@
 #define SRC_TINT_TYPE_ARRAY_COUNT_H_
 
 #include <functional>
+#include <string>
 
+#include "src/tint/symbol_table.h"
 #include "src/tint/type/node.h"
 
 namespace tint::type {
@@ -33,6 +35,10 @@
     /// @returns true if this array count is equal to the given array count
     virtual bool Equals(const ArrayCount& t) const = 0;
 
+    /// @param symbols the symbol table
+    /// @returns the friendly name for this array count
+    virtual std::string FriendlyName(const SymbolTable& symbols) const = 0;
+
   protected:
     ArrayCount();
 };
@@ -57,6 +63,10 @@
     /// @returns true if this array count is equal to the given array count
     bool Equals(const ArrayCount& t) const override;
 
+    /// @param symbols the symbol table
+    /// @returns the friendly name for this array count
+    std::string FriendlyName(const SymbolTable& symbols) const override;
+
     /// The array count constant-expression value.
     uint32_t value;
 };
@@ -78,6 +88,10 @@
     /// @param t other array count
     /// @returns true if this array count is equal to the given array count
     bool Equals(const ArrayCount& t) const override;
+
+    /// @param symbols the symbol table
+    /// @returns the friendly name for this array count
+    std::string FriendlyName(const SymbolTable& symbols) const override;
 };
 
 }  // namespace tint::type
diff --git a/src/tint/sem/array_test.cc b/src/tint/type/array_test.cc
similarity index 98%
rename from src/tint/sem/array_test.cc
rename to src/tint/type/array_test.cc
index ab244b9..f2b3ee7 100644
--- a/src/tint/sem/array_test.cc
+++ b/src/tint/type/array_test.cc
@@ -12,10 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/tint/sem/test_helper.h"
+#include "src/tint/sem/array_count.h"
+#include "src/tint/type/test_helper.h"
 #include "src/tint/type/texture.h"
 
-namespace tint::sem {
+namespace tint::type {
 namespace {
 
 using ArrayTest = TestHelper;
@@ -205,4 +206,4 @@
 }
 
 }  // namespace
-}  // namespace tint::sem
+}  // namespace tint::type
diff --git a/src/tint/type/type.cc b/src/tint/type/type.cc
index 0fb90e3..ca32818 100644
--- a/src/tint/type/type.cc
+++ b/src/tint/type/type.cc
@@ -14,10 +14,9 @@
 
 #include "src/tint/type/type.h"
 
-#include "src/tint/sem/array.h"
-#include "src/tint/sem/struct.h"
 #include "src/tint/type/abstract_float.h"
 #include "src/tint/type/abstract_int.h"
+#include "src/tint/type/array.h"
 #include "src/tint/type/bool.h"
 #include "src/tint/type/f16.h"
 #include "src/tint/type/f32.h"
@@ -26,6 +25,7 @@
 #include "src/tint/type/pointer.h"
 #include "src/tint/type/reference.h"
 #include "src/tint/type/sampler.h"
+#include "src/tint/type/struct.h"
 #include "src/tint/type/texture.h"
 #include "src/tint/type/u32.h"
 #include "src/tint/type/vector.h"
@@ -181,7 +181,7 @@
         [&](const type::AbstractNumeric*) { return true; },
         [&](const type::Vector* v) { return v->type()->HoldsAbstract(); },
         [&](const type::Matrix* m) { return m->type()->HoldsAbstract(); },
-        [&](const sem::Array* a) { return a->ElemType()->HoldsAbstract(); },
+        [&](const type::Array* a) { return a->ElemType()->HoldsAbstract(); },
         [&](const type::StructBase* s) {
             for (auto* m : s->Members()) {
                 if (m->Type()->HoldsAbstract()) {
@@ -232,8 +232,8 @@
             }
             return kNoConversion;
         },
-        [&](const sem::Array* from_arr) {
-            if (auto* to_arr = to->As<sem::Array>()) {
+        [&](const type::Array* from_arr) {
+            if (auto* to_arr = to->As<type::Array>()) {
                 if (from_arr->Count() == to_arr->Count()) {
                     return ConversionRank(from_arr->ElemType(), to_arr->ElemType());
                 }
@@ -273,7 +273,7 @@
             }
             return m->ColumnType();
         },
-        [&](const sem::Array* a) {
+        [&](const type::Array* a) {
             if (count) {
                 if (auto* const_count = a->Count()->As<type::ConstantArrayCount>()) {
                     *count = const_count->value;
diff --git a/src/tint/type/type_manager.h b/src/tint/type/type_manager.h
index 49b420a..31c4c22 100644
--- a/src/tint/type/type_manager.h
+++ b/src/tint/type/type_manager.h
@@ -20,9 +20,9 @@
 #include <unordered_map>
 #include <utility>
 
-#include "src/tint/sem/struct.h"
 #include "src/tint/type/array_count.h"
 #include "src/tint/type/node.h"
+#include "src/tint/type/struct.h"
 #include "src/tint/type/type.h"
 #include "src/tint/utils/unique_allocator.h"
 
diff --git a/src/tint/type/type_test.cc b/src/tint/type/type_test.cc
index c061e40..a363d7e 100644
--- a/src/tint/type/type_test.cc
+++ b/src/tint/type/type_test.cc
@@ -93,63 +93,63 @@
                                                         /* align*/ 4u,
                                                         /* size*/ 4u,
                                                         /* size_no_padding*/ 4u);
-    const sem::Array* arr_i32 = create<sem::Array>(
+    const type::Array* arr_i32 = create<type::Array>(
         /* element */ i32,
         /* count */ create<type::ConstantArrayCount>(5u),
         /* align */ 4u,
         /* size */ 5u * 4u,
         /* stride */ 5u * 4u,
         /* implicit_stride */ 5u * 4u);
-    const sem::Array* arr_ai = create<sem::Array>(
+    const type::Array* arr_ai = create<type::Array>(
         /* element */ ai,
         /* count */ create<type::ConstantArrayCount>(5u),
         /* align */ 4u,
         /* size */ 5u * 4u,
         /* stride */ 5u * 4u,
         /* implicit_stride */ 5u * 4u);
-    const sem::Array* arr_vec3_i32 = create<sem::Array>(
+    const type::Array* arr_vec3_i32 = create<type::Array>(
         /* element */ vec3_i32,
         /* count */ create<ConstantArrayCount>(5u),
         /* align */ 16u,
         /* size */ 5u * 16u,
         /* stride */ 5u * 16u,
         /* implicit_stride */ 5u * 16u);
-    const sem::Array* arr_vec3_ai = create<sem::Array>(
+    const type::Array* arr_vec3_ai = create<type::Array>(
         /* element */ vec3_ai,
         /* count */ create<type::ConstantArrayCount>(5u),
         /* align */ 16u,
         /* size */ 5u * 16u,
         /* stride */ 5u * 16u,
         /* implicit_stride */ 5u * 16u);
-    const sem::Array* arr_mat4x3_f16 = create<sem::Array>(
+    const type::Array* arr_mat4x3_f16 = create<type::Array>(
         /* element */ mat4x3_f16,
         /* count */ create<type::ConstantArrayCount>(5u),
         /* align */ 32u,
         /* size */ 5u * 32u,
         /* stride */ 5u * 32u,
         /* implicit_stride */ 5u * 32u);
-    const sem::Array* arr_mat4x3_f32 = create<sem::Array>(
+    const type::Array* arr_mat4x3_f32 = create<type::Array>(
         /* element */ mat4x3_f32,
         /* count */ create<type::ConstantArrayCount>(5u),
         /* align */ 64u,
         /* size */ 5u * 64u,
         /* stride */ 5u * 64u,
         /* implicit_stride */ 5u * 64u);
-    const sem::Array* arr_mat4x3_af = create<sem::Array>(
+    const type::Array* arr_mat4x3_af = create<type::Array>(
         /* element */ mat4x3_af,
         /* count */ create<type::ConstantArrayCount>(5u),
         /* align */ 64u,
         /* size */ 5u * 64u,
         /* stride */ 5u * 64u,
         /* implicit_stride */ 5u * 64u);
-    const sem::Array* arr_str_f16 = create<sem::Array>(
+    const type::Array* arr_str_f16 = create<type::Array>(
         /* element */ str_f16,
         /* count */ create<type::ConstantArrayCount>(5u),
         /* align */ 4u,
         /* size */ 5u * 4u,
         /* stride */ 5u * 4u,
         /* implicit_stride */ 5u * 4u);
-    const sem::Array* arr_str_af = create<sem::Array>(
+    const type::Array* arr_str_af = create<type::Array>(
         /* element */ str_af,
         /* count */ create<type::ConstantArrayCount>(5u),
         /* align */ 4u,
diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc
index 7b8a53f..d5f8b9c 100644
--- a/src/tint/writer/glsl/generator_impl.cc
+++ b/src/tint/writer/glsl/generator_impl.cc
@@ -27,7 +27,6 @@
 #include "src/tint/ast/interpolate_attribute.h"
 #include "src/tint/ast/variable_decl_statement.h"
 #include "src/tint/debug.h"
-#include "src/tint/sem/array.h"
 #include "src/tint/sem/block_statement.h"
 #include "src/tint/sem/call.h"
 #include "src/tint/sem/constant.h"
@@ -63,6 +62,7 @@
 #include "src/tint/transform/std140.h"
 #include "src/tint/transform/unshadow.h"
 #include "src/tint/transform/zero_init_workgroup_memory.h"
+#include "src/tint/type/array.h"
 #include "src/tint/type/atomic.h"
 #include "src/tint/type/depth_multisampled_texture.h"
 #include "src/tint/type/depth_texture.h"
@@ -295,7 +295,7 @@
         } else if (auto* str = decl->As<ast::Struct>()) {
             auto* sem = builder_.Sem().Get(str);
             bool has_rt_arr = false;
-            if (auto* arr = sem->Members().Back()->Type()->As<sem::Array>()) {
+            if (auto* arr = sem->Members().Back()->Type()->As<type::Array>()) {
                 has_rt_arr = arr->Count()->Is<type::RuntimeArrayCount>();
             }
             bool is_block =
@@ -2355,7 +2355,7 @@
             }
             return true;
         },
-        [&](const sem::Array* a) {
+        [&](const type::Array* a) {
             if (!EmitType(out, a, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
                 return false;
             }
@@ -2364,7 +2364,8 @@
 
             auto count = a->ConstantCount();
             if (!count) {
-                diagnostics_.add_error(diag::System::Writer, sem::Array::kErrExpectedConstantCount);
+                diagnostics_.add_error(diag::System::Writer,
+                                       type::Array::kErrExpectedConstantCount);
                 return false;
             }
 
@@ -2486,7 +2487,7 @@
             }
             EmitZeroValue(out, member->Type());
         }
-    } else if (auto* arr = type->As<sem::Array>()) {
+    } else if (auto* arr = type->As<type::Array>()) {
         if (!EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
             return false;
         }
@@ -2494,7 +2495,7 @@
 
         auto count = arr->ConstantCount();
         if (!count) {
-            diagnostics_.add_error(diag::System::Writer, sem::Array::kErrExpectedConstantCount);
+            diagnostics_.add_error(diag::System::Writer, type::Array::kErrExpectedConstantCount);
             return false;
         }
 
@@ -2837,17 +2838,17 @@
             break;
     }
 
-    if (auto* ary = type->As<sem::Array>()) {
+    if (auto* ary = type->As<type::Array>()) {
         const type::Type* base_type = ary;
         std::vector<uint32_t> sizes;
-        while (auto* arr = base_type->As<sem::Array>()) {
+        while (auto* arr = base_type->As<type::Array>()) {
             if (arr->Count()->Is<type::RuntimeArrayCount>()) {
                 sizes.push_back(0);
             } else {
                 auto count = arr->ConstantCount();
                 if (!count) {
                     diagnostics_.add_error(diag::System::Writer,
-                                           sem::Array::kErrExpectedConstantCount);
+                                           type::Array::kErrExpectedConstantCount);
                     return false;
                 }
                 sizes.push_back(count.value());
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index bccd7bf..92df2fe 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -28,7 +28,6 @@
 #include "src/tint/ast/interpolate_attribute.h"
 #include "src/tint/ast/variable_decl_statement.h"
 #include "src/tint/debug.h"
-#include "src/tint/sem/array.h"
 #include "src/tint/sem/block_statement.h"
 #include "src/tint/sem/call.h"
 #include "src/tint/sem/constant.h"
@@ -63,6 +62,7 @@
 #include "src/tint/transform/unshadow.h"
 #include "src/tint/transform/vectorize_scalar_matrix_initializers.h"
 #include "src/tint/transform/zero_init_workgroup_memory.h"
+#include "src/tint/type/array.h"
 #include "src/tint/type/atomic.h"
 #include "src/tint/type/depth_multisampled_texture.h"
 #include "src/tint/type/depth_texture.h"
@@ -1053,7 +1053,7 @@
         }
     }
 
-    bool brackets = type->IsAnyOf<sem::Array, sem::Struct>();
+    bool brackets = type->IsAnyOf<type::Array, sem::Struct>();
 
     // For single-value vector initializers, swizzle the scalar to the right
     // vector dimension using .x
@@ -2852,7 +2852,7 @@
         auto name = builder_.Symbols().NameFor(func->symbol);
         // If the function returns an array, then we need to declare a typedef for
         // this.
-        if (sem->ReturnType()->Is<sem::Array>()) {
+        if (sem->ReturnType()->Is<type::Array>()) {
             auto typedef_name = UniqueIdentifier(name + "_ret");
             auto pre = line();
             pre << "typedef ";
@@ -3338,7 +3338,7 @@
             }
             return true;
         },
-        [&](const sem::Array* a) {
+        [&](const type::Array* a) {
             if (constant->AllZero()) {
                 out << "(";
                 if (!EmitType(out, a, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
@@ -3353,7 +3353,8 @@
 
             auto count = a->ConstantCount();
             if (!count) {
-                diagnostics_.add_error(diag::System::Writer, sem::Array::kErrExpectedConstantCount);
+                diagnostics_.add_error(diag::System::Writer,
+                                       type::Array::kErrExpectedConstantCount);
                 return false;
             }
 
@@ -3514,7 +3515,7 @@
             TINT_DEFER(out << ")" << value);
             return EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kUndefined, "");
         },
-        [&](const sem::Array*) {
+        [&](const type::Array*) {
             out << "(";
             TINT_DEFER(out << ")" << value);
             return EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kUndefined, "");
@@ -3920,10 +3921,10 @@
 
     return Switch(
         type,
-        [&](const sem::Array* ary) {
+        [&](const type::Array* ary) {
             const type::Type* base_type = ary;
             std::vector<uint32_t> sizes;
-            while (auto* arr = base_type->As<sem::Array>()) {
+            while (auto* arr = base_type->As<type::Array>()) {
                 if (arr->Count()->Is<type::RuntimeArrayCount>()) {
                     TINT_ICE(Writer, diagnostics_)
                         << "runtime arrays may only exist in storage buffers, which should have "
@@ -3933,7 +3934,7 @@
                 const auto count = arr->ConstantCount();
                 if (!count) {
                     diagnostics_.add_error(diag::System::Writer,
-                                           sem::Array::kErrExpectedConstantCount);
+                                           type::Array::kErrExpectedConstantCount);
                     return false;
                 }
 
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index 9f5b9d9..6db24ca 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -31,7 +31,6 @@
 #include "src/tint/ast/module.h"
 #include "src/tint/ast/variable_decl_statement.h"
 #include "src/tint/ast/void.h"
-#include "src/tint/sem/array.h"
 #include "src/tint/sem/call.h"
 #include "src/tint/sem/constant.h"
 #include "src/tint/sem/function.h"
@@ -59,6 +58,7 @@
 #include "src/tint/transform/unshadow.h"
 #include "src/tint/transform/vectorize_scalar_matrix_initializers.h"
 #include "src/tint/transform/zero_init_workgroup_memory.h"
+#include "src/tint/type/array.h"
 #include "src/tint/type/atomic.h"
 #include "src/tint/type/bool.h"
 #include "src/tint/type/depth_multisampled_texture.h"
@@ -805,7 +805,7 @@
 
     bool ok = Switch(
         type,
-        [&](const sem::Array*) {
+        [&](const type::Array*) {
             if (!EmitType(out, type, "")) {
                 return false;
             }
@@ -1642,7 +1642,7 @@
             ScopedParen sp(out);
             return EmitZeroValue(out, mat->type());
         },
-        [&](const sem::Array*) {
+        [&](const type::Array*) {
             out << "{}";
             return true;
         },
@@ -1722,7 +1722,7 @@
             }
             return true;
         },
-        [&](const sem::Array* a) {
+        [&](const type::Array* a) {
             if (!EmitType(out, a, "")) {
                 return false;
             }
@@ -1736,7 +1736,8 @@
 
             auto count = a->ConstantCount();
             if (!count) {
-                diagnostics_.add_error(diag::System::Writer, sem::Array::kErrExpectedConstantCount);
+                diagnostics_.add_error(diag::System::Writer,
+                                       type::Array::kErrExpectedConstantCount);
                 return false;
             }
 
@@ -2536,7 +2537,7 @@
                 << "unhandled atomic type " << atomic->Type()->FriendlyName(builder_.Symbols());
             return false;
         },
-        [&](const sem::Array* arr) {
+        [&](const type::Array* arr) {
             out << ArrayType() << "<";
             if (!EmitType(out, arr->ElemType(), "")) {
                 return false;
@@ -2548,7 +2549,7 @@
                 auto count = arr->ConstantCount();
                 if (!count) {
                     diagnostics_.add_error(diag::System::Writer,
-                                           sem::Array::kErrExpectedConstantCount);
+                                           type::Array::kErrExpectedConstantCount);
                     return false;
                 }
 
@@ -3164,7 +3165,7 @@
             return SizeAndAlign{};
         },
 
-        [&](const sem::Array* arr) {
+        [&](const type::Array* arr) {
             if (!arr->IsStrideImplicit()) {
                 TINT_ICE(Writer, diagnostics_)
                     << "arrays with explicit strides should not exist past the SPIR-V reader";
@@ -3176,7 +3177,7 @@
             if (auto count = arr->ConstantCount()) {
                 return SizeAndAlign{arr->Stride() * count.value(), arr->Align()};
             }
-            diagnostics_.add_error(diag::System::Writer, sem::Array::kErrExpectedConstantCount);
+            diagnostics_.add_error(diag::System::Writer, type::Array::kErrExpectedConstantCount);
             return SizeAndAlign{};
         },
 
diff --git a/src/tint/writer/spirv/builder.cc b/src/tint/writer/spirv/builder.cc
index 5403f7b..14cebf1 100644
--- a/src/tint/writer/spirv/builder.cc
+++ b/src/tint/writer/spirv/builder.cc
@@ -22,7 +22,6 @@
 #include "src/tint/ast/id_attribute.h"
 #include "src/tint/ast/internal_attribute.h"
 #include "src/tint/ast/traverse_expressions.h"
-#include "src/tint/sem/array.h"
 #include "src/tint/sem/builtin.h"
 #include "src/tint/sem/call.h"
 #include "src/tint/sem/constant.h"
@@ -37,6 +36,7 @@
 #include "src/tint/sem/type_initializer.h"
 #include "src/tint/sem/variable.h"
 #include "src/tint/transform/add_block_attribute.h"
+#include "src/tint/type/array.h"
 #include "src/tint/type/atomic.h"
 #include "src/tint/type/depth_multisampled_texture.h"
 #include "src/tint/type/depth_texture.h"
@@ -90,7 +90,7 @@
 /// @param type the given type, which must not be null
 /// @returns the nested matrix type, or nullptr if none
 const type::Matrix* GetNestedMatrixType(const type::Type* type) {
-    while (auto* arr = type->As<sem::Array>()) {
+    while (auto* arr = type->As<type::Array>()) {
         type = arr->ElemType();
     }
     return type->As<type::Matrix>();
@@ -1348,7 +1348,7 @@
         // If the result is not a vector then we should have validated that the
         // value type is a correctly sized vector so we can just use it directly.
         if (result_type == value_type || result_type->Is<type::Matrix>() ||
-            result_type->Is<sem::Array>() || result_type->Is<sem::Struct>()) {
+            result_type->Is<type::Array>() || result_type->Is<sem::Struct>()) {
             ops.push_back(Operand(id));
             continue;
         }
@@ -1702,10 +1702,10 @@
         },
         [&](const type::Vector* v) { return composite(v->Width()); },
         [&](const type::Matrix* m) { return composite(m->columns()); },
-        [&](const sem::Array* a) {
+        [&](const type::Array* a) {
             auto count = a->ConstantCount();
             if (!count) {
-                error_ = sem::Array::kErrExpectedConstantCount;
+                error_ = type::Array::kErrExpectedConstantCount;
                 return static_cast<uint32_t>(0);
             }
             return composite(count.value());
@@ -3673,7 +3673,7 @@
         auto id = std::get<uint32_t>(result);
         bool ok = Switch(
             type,
-            [&](const sem::Array* arr) {  //
+            [&](const type::Array* arr) {  //
                 return GenerateArrayType(arr, result);
             },
             [&](const type::Bool*) {
@@ -3838,7 +3838,7 @@
     return true;
 }
 
-bool Builder::GenerateArrayType(const sem::Array* arr, const Operand& result) {
+bool Builder::GenerateArrayType(const type::Array* arr, const Operand& result) {
     auto elem_type = GenerateTypeIfNeeded(arr->ElemType());
     if (elem_type == 0) {
         return false;
@@ -3850,7 +3850,7 @@
     } else {
         auto count = arr->ConstantCount();
         if (!count) {
-            error_ = sem::Array::kErrExpectedConstantCount;
+            error_ = type::Array::kErrExpectedConstantCount;
             return static_cast<uint32_t>(0);
         }
 
diff --git a/src/tint/writer/spirv/builder.h b/src/tint/writer/spirv/builder.h
index 2f33002..e257680 100644
--- a/src/tint/writer/spirv/builder.h
+++ b/src/tint/writer/spirv/builder.h
@@ -480,7 +480,7 @@
     /// @param ary the array to generate
     /// @param result the result operand
     /// @returns true if the array was successfully generated
-    bool GenerateArrayType(const sem::Array* ary, const Operand& result);
+    bool GenerateArrayType(const type::Array* ary, const Operand& result);
     /// Generates a matrix type declaration
     /// @param mat the matrix to generate
     /// @param result the result operand