tint/writer/msl: Generate an array<T,N> helper

And remove the WrapArraysInStructs transform.

Wrapping arrays in structures becomes troublesome for `const` arrays, as
currently WGSL does not allow `const` structures.

MSL 2.0+ has a builtin array<> helper, but we're targetting MSL 1.2, so
we have to emit our own. Fortunately, it can be done with a few lines of
templated code.

This produces significantly cleaner output.

Change-Id: Ifc92ef21e09befa252a07c856c4b5afdc51cc2e4
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/94540
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@chromium.org>
Reviewed-by: David Neto <dneto@google.com>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 2ba988c..be99389 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -543,8 +543,6 @@
     "transform/vertex_pulling.h",
     "transform/while_to_loop.cc",
     "transform/while_to_loop.h",
-    "transform/wrap_arrays_in_structs.cc",
-    "transform/wrap_arrays_in_structs.h",
     "transform/zero_init_workgroup_memory.cc",
     "transform/zero_init_workgroup_memory.h",
     "utils/bitcast.h",
@@ -1199,7 +1197,6 @@
       "transform/vectorize_scalar_matrix_constructors_test.cc",
       "transform/vertex_pulling_test.cc",
       "transform/while_to_loop_test.cc",
-      "transform/wrap_arrays_in_structs_test.cc",
       "transform/zero_init_workgroup_memory_test.cc",
     ]
   }
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 3734367..10d81d6 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -466,8 +466,6 @@
   transform/vertex_pulling.h
   transform/while_to_loop.cc
   transform/while_to_loop.h
-  transform/wrap_arrays_in_structs.cc
-  transform/wrap_arrays_in_structs.h
   transform/zero_init_workgroup_memory.cc
   transform/zero_init_workgroup_memory.h
   utils/bitcast.h
@@ -1121,7 +1119,6 @@
       transform/vectorize_scalar_matrix_constructors_test.cc
       transform/vertex_pulling_test.cc
       transform/while_to_loop_test.cc
-      transform/wrap_arrays_in_structs_test.cc
       transform/zero_init_workgroup_memory_test.cc
       transform/utils/get_insertion_point_test.cc
       transform/utils/hoist_to_decl_before_test.cc
diff --git a/src/tint/transform/wrap_arrays_in_structs.cc b/src/tint/transform/wrap_arrays_in_structs.cc
deleted file mode 100644
index eb133d7..0000000
--- a/src/tint/transform/wrap_arrays_in_structs.cc
+++ /dev/null
@@ -1,158 +0,0 @@
-// 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/transform/wrap_arrays_in_structs.h"
-
-#include <utility>
-
-#include "src/tint/program_builder.h"
-#include "src/tint/sem/array.h"
-#include "src/tint/sem/call.h"
-#include "src/tint/sem/expression.h"
-#include "src/tint/sem/type_constructor.h"
-#include "src/tint/utils/map.h"
-#include "src/tint/utils/transform.h"
-
-TINT_INSTANTIATE_TYPEINFO(tint::transform::WrapArraysInStructs);
-
-namespace tint::transform {
-
-WrapArraysInStructs::WrappedArrayInfo::WrappedArrayInfo() = default;
-WrapArraysInStructs::WrappedArrayInfo::WrappedArrayInfo(const WrappedArrayInfo&) = default;
-WrapArraysInStructs::WrappedArrayInfo::~WrappedArrayInfo() = default;
-
-WrapArraysInStructs::WrapArraysInStructs() = default;
-
-WrapArraysInStructs::~WrapArraysInStructs() = default;
-
-bool WrapArraysInStructs::ShouldRun(const Program* program, const DataMap&) const {
-    for (auto* node : program->ASTNodes().Objects()) {
-        if (program->Sem().Get<sem::Array>(node->As<ast::Type>())) {
-            return true;
-        }
-    }
-    return false;
-}
-
-void WrapArraysInStructs::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
-    auto& sem = ctx.src->Sem();
-
-    std::unordered_map<const sem::Array*, WrappedArrayInfo> wrapped_arrays;
-    auto wrapper = [&](const sem::Array* array) { return WrapArray(ctx, wrapped_arrays, array); };
-    auto wrapper_typename = [&](const sem::Array* arr) -> ast::TypeName* {
-        auto info = wrapper(arr);
-        return info ? ctx.dst->create<ast::TypeName>(info.wrapper_name) : nullptr;
-    };
-
-    // Replace all array types with their corresponding wrapper
-    ctx.ReplaceAll([&](const ast::Type* ast_type) -> const ast::Type* {
-        auto* type = ctx.src->TypeOf(ast_type);
-        if (auto* array = type->UnwrapRef()->As<sem::Array>()) {
-            return wrapper_typename(array);
-        }
-        return nullptr;
-    });
-
-    // Fix up index accessors so `a[1]` becomes `a.arr[1]`
-    ctx.ReplaceAll(
-        [&](const ast::IndexAccessorExpression* accessor) -> const ast::IndexAccessorExpression* {
-            if (auto* array =
-                    ::tint::As<sem::Array>(sem.Get(accessor->object)->Type()->UnwrapRef())) {
-                if (wrapper(array)) {
-                    // Array is wrapped in a structure. Emit a member accessor to get
-                    // to the actual array.
-                    auto* arr = ctx.Clone(accessor->object);
-                    auto* idx = ctx.Clone(accessor->index);
-                    auto* unwrapped = ctx.dst->MemberAccessor(arr, "arr");
-                    return ctx.dst->IndexAccessor(accessor->source, unwrapped, idx);
-                }
-            }
-            return nullptr;
-        });
-
-    // Fix up array constructors so `A(1,2)` becomes `tint_array_wrapper(A(1,2))`
-    ctx.ReplaceAll([&](const ast::CallExpression* expr) -> const ast::Expression* {
-        if (auto* call = sem.Get(expr)->UnwrapMaterialize()->As<sem::Call>()) {
-            if (auto* ctor = call->Target()->As<sem::TypeConstructor>()) {
-                if (auto* array = ctor->ReturnType()->As<sem::Array>()) {
-                    if (auto w = wrapper(array)) {
-                        // Wrap the array type constructor with another constructor for
-                        // the wrapper
-                        auto* wrapped_array_ty = ctx.dst->ty.type_name(w.wrapper_name);
-                        auto* array_ty = w.array_type(ctx);
-                        auto args = utils::Transform(call->Arguments(),
-                                                     [&](const tint::sem::Expression* s) {
-                                                         return ctx.Clone(s->Declaration());
-                                                     });
-                        auto* arr_ctor = ctx.dst->Construct(array_ty, args);
-                        return ctx.dst->Construct(wrapped_array_ty, arr_ctor);
-                    }
-                }
-            }
-        }
-        return nullptr;
-    });
-
-    ctx.Clone();
-}
-
-WrapArraysInStructs::WrappedArrayInfo WrapArraysInStructs::WrapArray(
-    CloneContext& ctx,
-    std::unordered_map<const sem::Array*, WrappedArrayInfo>& wrapped_arrays,
-    const sem::Array* array) const {
-    if (array->IsRuntimeSized()) {
-        return {};  // We don't want to wrap runtime sized arrays
-    }
-
-    return utils::GetOrCreate(wrapped_arrays, array, [&] {
-        WrappedArrayInfo info;
-
-        // Generate a unique name for the array wrapper
-        info.wrapper_name = ctx.dst->Symbols().New("tint_array_wrapper");
-
-        // Examine the element type. Is it also an array?
-        std::function<const ast::Type*(CloneContext&)> el_type;
-        if (auto* el_array = array->ElemType()->As<sem::Array>()) {
-            // Array of array - call WrapArray() on the element type
-            if (auto el = WrapArray(ctx, wrapped_arrays, el_array)) {
-                el_type = [=](CloneContext& c) {
-                    return c.dst->create<ast::TypeName>(el.wrapper_name);
-                };
-            }
-        }
-
-        // If the element wasn't an array, just create the typical AST type for it
-        if (!el_type) {
-            el_type = [=](CloneContext& c) { return CreateASTTypeFor(c, array->ElemType()); };
-        }
-
-        // Construct the single structure field type
-        info.array_type = [=](CloneContext& c) {
-            ast::AttributeList attrs;
-            if (!array->IsStrideImplicit()) {
-                attrs.emplace_back(c.dst->create<ast::StrideAttribute>(array->Stride()));
-            }
-            return c.dst->ty.array(el_type(c), u32(array->Count()), std::move(attrs));
-        };
-
-        // Structure() will create and append the ast::Struct to the
-        // global declarations of `ctx.dst`. As we haven't finished building the
-        // current module-scope statement or function, this will be placed
-        // immediately before the usage.
-        ctx.dst->Structure(info.wrapper_name, {ctx.dst->Member("arr", info.array_type(ctx))});
-        return info;
-    });
-}
-
-}  // namespace tint::transform
diff --git a/src/tint/transform/wrap_arrays_in_structs.h b/src/tint/transform/wrap_arrays_in_structs.h
deleted file mode 100644
index 4653c6b..0000000
--- a/src/tint/transform/wrap_arrays_in_structs.h
+++ /dev/null
@@ -1,88 +0,0 @@
-// 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.
-
-#ifndef SRC_TINT_TRANSFORM_WRAP_ARRAYS_IN_STRUCTS_H_
-#define SRC_TINT_TRANSFORM_WRAP_ARRAYS_IN_STRUCTS_H_
-
-#include <string>
-#include <unordered_map>
-
-#include "src/tint/transform/transform.h"
-
-// Forward declarations
-namespace tint::ast {
-class Type;
-}  // namespace tint::ast
-
-namespace tint::transform {
-
-/// WrapArraysInStructs is a transform that replaces all array types with a
-/// structure holding a single field of that array type.
-/// Array index expressions and constructors are also adjusted to deal with this
-/// wrapping.
-/// This transform helps with backends that cannot directly return arrays or use
-/// them as parameters.
-class WrapArraysInStructs : public Castable<WrapArraysInStructs, Transform> {
-  public:
-    /// Constructor
-    WrapArraysInStructs();
-
-    /// Destructor
-    ~WrapArraysInStructs() override;
-
-    /// @param program the program to inspect
-    /// @param data optional extra transform-specific input data
-    /// @returns true if this transform should be run for the given program
-    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
-
-  protected:
-    /// Runs the transform using the CloneContext built for transforming a
-    /// program. Run() is responsible for calling Clone() on the CloneContext.
-    /// @param ctx the CloneContext primed with the input program and
-    /// ProgramBuilder
-    /// @param inputs optional extra transform-specific input data
-    /// @param outputs optional extra transform-specific output data
-    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
-
-  private:
-    struct WrappedArrayInfo {
-        WrappedArrayInfo();
-        WrappedArrayInfo(const WrappedArrayInfo&);
-        ~WrappedArrayInfo();
-
-        Symbol wrapper_name;
-        std::function<const ast::Type*(CloneContext&)> array_type;
-
-        operator bool() { return wrapper_name.IsValid(); }
-    };
-
-    /// WrapArray wraps the fixed-size array type in a new structure (if it hasn't
-    /// already been wrapped). WrapArray will recursively wrap arrays-of-arrays.
-    /// The new structure will be added to module-scope type declarations of
-    /// `ctx.dst`.
-    /// @param ctx the CloneContext
-    /// @param wrapped_arrays a map of src array type to the wrapped structure
-    /// name
-    /// @param array the array type
-    /// @return the name of the structure that wraps the array, or an invalid
-    /// Symbol if this array should not be wrapped
-    WrappedArrayInfo WrapArray(
-        CloneContext& ctx,
-        std::unordered_map<const sem::Array*, WrappedArrayInfo>& wrapped_arrays,
-        const sem::Array* array) const;
-};
-
-}  // namespace tint::transform
-
-#endif  // SRC_TINT_TRANSFORM_WRAP_ARRAYS_IN_STRUCTS_H_
diff --git a/src/tint/transform/wrap_arrays_in_structs_test.cc b/src/tint/transform/wrap_arrays_in_structs_test.cc
deleted file mode 100644
index 7a7a6b3..0000000
--- a/src/tint/transform/wrap_arrays_in_structs_test.cc
+++ /dev/null
@@ -1,422 +0,0 @@
-// 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/transform/wrap_arrays_in_structs.h"
-
-#include <memory>
-#include <utility>
-
-#include "src/tint/transform/test_helper.h"
-
-namespace tint::transform {
-namespace {
-
-using WrapArraysInStructsTest = TransformTest;
-
-TEST_F(WrapArraysInStructsTest, ShouldRunEmptyModule) {
-    auto* src = R"()";
-
-    EXPECT_FALSE(ShouldRun<WrapArraysInStructs>(src));
-}
-
-TEST_F(WrapArraysInStructsTest, ShouldRunHasArray) {
-    auto* src = R"(
-var<private> arr : array<i32, 4>;
-)";
-
-    EXPECT_TRUE(ShouldRun<WrapArraysInStructs>(src));
-}
-
-TEST_F(WrapArraysInStructsTest, EmptyModule) {
-    auto* src = R"()";
-    auto* expect = src;
-
-    auto got = Run<WrapArraysInStructs>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(WrapArraysInStructsTest, ArrayAsGlobal) {
-    auto* src = R"(
-var<private> arr : array<i32, 4>;
-)";
-    auto* expect = R"(
-struct tint_array_wrapper {
-  arr : array<i32, 4u>,
-}
-
-var<private> arr : tint_array_wrapper;
-)";
-
-    auto got = Run<WrapArraysInStructs>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(WrapArraysInStructsTest, ArrayAsFunctionVar) {
-    auto* src = R"(
-fn f() {
-  var arr : array<i32, 4>;
-  let x = arr[3];
-}
-)";
-    auto* expect = R"(
-struct tint_array_wrapper {
-  arr : array<i32, 4u>,
-}
-
-fn f() {
-  var arr : tint_array_wrapper;
-  let x = arr.arr[3];
-}
-)";
-
-    auto got = Run<WrapArraysInStructs>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(WrapArraysInStructsTest, ArrayAsParam) {
-    auto* src = R"(
-fn f(a : array<i32, 4>) -> i32 {
-  return a[2];
-}
-)";
-    auto* expect = R"(
-struct tint_array_wrapper {
-  arr : array<i32, 4u>,
-}
-
-fn f(a : tint_array_wrapper) -> i32 {
-  return a.arr[2];
-}
-)";
-
-    auto got = Run<WrapArraysInStructs>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(WrapArraysInStructsTest, ArrayAsReturn) {
-    auto* src = R"(
-fn f() -> array<i32, 4> {
-  return array<i32, 4>(1, 2, 3, 4);
-}
-)";
-    auto* expect = R"(
-struct tint_array_wrapper {
-  arr : array<i32, 4u>,
-}
-
-fn f() -> tint_array_wrapper {
-  return tint_array_wrapper(array<i32, 4u>(1, 2, 3, 4));
-}
-)";
-
-    auto got = Run<WrapArraysInStructs>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(WrapArraysInStructsTest, ArrayAlias) {
-    auto* src = R"(
-type Inner = array<i32, 2>;
-type Array = array<Inner, 2>;
-
-fn f() {
-  var arr : Array;
-  arr = Array();
-  arr = Array(Inner(1, 2), Inner(3, 4));
-  let vals : Array = Array(Inner(1, 2), Inner(3, 4));
-  arr = vals;
-  let x = arr[3];
-}
-)";
-    auto* expect = R"(
-struct tint_array_wrapper {
-  arr : array<i32, 2u>,
-}
-
-type Inner = tint_array_wrapper;
-
-struct tint_array_wrapper_1 {
-  arr : array<tint_array_wrapper, 2u>,
-}
-
-type Array = tint_array_wrapper_1;
-
-fn f() {
-  var arr : tint_array_wrapper_1;
-  arr = tint_array_wrapper_1(array<tint_array_wrapper, 2u>());
-  arr = tint_array_wrapper_1(array<tint_array_wrapper, 2u>(tint_array_wrapper(array<i32, 2u>(1, 2)), tint_array_wrapper(array<i32, 2u>(3, 4))));
-  let vals : tint_array_wrapper_1 = tint_array_wrapper_1(array<tint_array_wrapper, 2u>(tint_array_wrapper(array<i32, 2u>(1, 2)), tint_array_wrapper(array<i32, 2u>(3, 4))));
-  arr = vals;
-  let x = arr.arr[3];
-}
-)";
-
-    auto got = Run<WrapArraysInStructs>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(WrapArraysInStructsTest, ArrayAlias_OutOfOrder) {
-    auto* src = R"(
-fn f() {
-  var arr : Array;
-  arr = Array();
-  arr = Array(Inner(1, 2), Inner(3, 4));
-  let vals : Array = Array(Inner(1, 2), Inner(3, 4));
-  arr = vals;
-  let x = arr[3];
-}
-
-type Array = array<Inner, 2>;
-type Inner = array<i32, 2>;
-)";
-    auto* expect = R"(
-struct tint_array_wrapper_1 {
-  arr : array<i32, 2u>,
-}
-
-struct tint_array_wrapper {
-  arr : array<tint_array_wrapper_1, 2u>,
-}
-
-fn f() {
-  var arr : tint_array_wrapper;
-  arr = tint_array_wrapper(array<tint_array_wrapper_1, 2u>());
-  arr = tint_array_wrapper(array<tint_array_wrapper_1, 2u>(tint_array_wrapper_1(array<i32, 2u>(1, 2)), tint_array_wrapper_1(array<i32, 2u>(3, 4))));
-  let vals : tint_array_wrapper = tint_array_wrapper(array<tint_array_wrapper_1, 2u>(tint_array_wrapper_1(array<i32, 2u>(1, 2)), tint_array_wrapper_1(array<i32, 2u>(3, 4))));
-  arr = vals;
-  let x = arr.arr[3];
-}
-
-type Array = tint_array_wrapper;
-
-type Inner = tint_array_wrapper_1;
-)";
-
-    auto got = Run<WrapArraysInStructs>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(WrapArraysInStructsTest, ArraysInStruct) {
-    auto* src = R"(
-struct S {
-  a : array<i32, 4>,
-  b : array<i32, 8>,
-  c : array<i32, 4>,
-};
-)";
-    auto* expect = R"(
-struct tint_array_wrapper {
-  arr : array<i32, 4u>,
-}
-
-struct tint_array_wrapper_1 {
-  arr : array<i32, 8u>,
-}
-
-struct S {
-  a : tint_array_wrapper,
-  b : tint_array_wrapper_1,
-  c : tint_array_wrapper,
-}
-)";
-
-    auto got = Run<WrapArraysInStructs>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(WrapArraysInStructsTest, ArraysOfArraysInStruct) {
-    auto* src = R"(
-struct S {
-  a : array<i32, 4>,
-  b : array<array<i32, 4>, 4>,
-  c : array<array<array<i32, 4>, 4>, 4>,
-};
-)";
-    auto* expect = R"(
-struct tint_array_wrapper {
-  arr : array<i32, 4u>,
-}
-
-struct tint_array_wrapper_1 {
-  arr : array<tint_array_wrapper, 4u>,
-}
-
-struct tint_array_wrapper_2 {
-  arr : array<tint_array_wrapper_1, 4u>,
-}
-
-struct S {
-  a : tint_array_wrapper,
-  b : tint_array_wrapper_1,
-  c : tint_array_wrapper_2,
-}
-)";
-
-    auto got = Run<WrapArraysInStructs>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(WrapArraysInStructsTest, AccessArraysOfArraysInStruct) {
-    auto* src = R"(
-struct S {
-  a : array<i32, 4>,
-  b : array<array<i32, 4>, 4>,
-  c : array<array<array<i32, 4>, 4>, 4>,
-};
-
-fn f(s : S) -> i32 {
-  return s.a[2] + s.b[1][2] + s.c[3][1][2];
-}
-)";
-    auto* expect = R"(
-struct tint_array_wrapper {
-  arr : array<i32, 4u>,
-}
-
-struct tint_array_wrapper_1 {
-  arr : array<tint_array_wrapper, 4u>,
-}
-
-struct tint_array_wrapper_2 {
-  arr : array<tint_array_wrapper_1, 4u>,
-}
-
-struct S {
-  a : tint_array_wrapper,
-  b : tint_array_wrapper_1,
-  c : tint_array_wrapper_2,
-}
-
-fn f(s : S) -> i32 {
-  return ((s.a.arr[2] + s.b.arr[1].arr[2]) + s.c.arr[3].arr[1].arr[2]);
-}
-)";
-
-    auto got = Run<WrapArraysInStructs>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(WrapArraysInStructsTest, DeclarationOrder) {
-    auto* src = R"(
-type T0 = i32;
-
-type T1 = array<i32, 1>;
-
-type T2 = i32;
-
-fn f1(a : array<i32, 2>) {
-}
-
-type T3 = i32;
-
-fn f2() {
-  var v : array<i32, 3>;
-}
-)";
-    auto* expect = R"(
-type T0 = i32;
-
-struct tint_array_wrapper {
-  arr : array<i32, 1u>,
-}
-
-type T1 = tint_array_wrapper;
-
-type T2 = i32;
-
-struct tint_array_wrapper_1 {
-  arr : array<i32, 2u>,
-}
-
-fn f1(a : tint_array_wrapper_1) {
-}
-
-type T3 = i32;
-
-struct tint_array_wrapper_2 {
-  arr : array<i32, 3u>,
-}
-
-fn f2() {
-  var v : tint_array_wrapper_2;
-}
-)";
-
-    auto got = Run<WrapArraysInStructs>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(WrapArraysInStructsTest, DeclarationOrder_OutOfOrder) {
-    auto* src = R"(
-fn f2() {
-  var v : array<i32, 3>;
-}
-
-type T3 = i32;
-
-fn f1(a : array<i32, 2>) {
-}
-
-type T2 = i32;
-
-type T1 = array<i32, 1>;
-
-type T0 = i32;
-)";
-    auto* expect = R"(
-struct tint_array_wrapper {
-  arr : array<i32, 3u>,
-}
-
-fn f2() {
-  var v : tint_array_wrapper;
-}
-
-type T3 = i32;
-
-struct tint_array_wrapper_1 {
-  arr : array<i32, 2u>,
-}
-
-fn f1(a : tint_array_wrapper_1) {
-}
-
-type T2 = i32;
-
-struct tint_array_wrapper_2 {
-  arr : array<i32, 1u>,
-}
-
-type T1 = tint_array_wrapper_2;
-
-type T0 = i32;
-)";
-
-    auto got = Run<WrapArraysInStructs>(src);
-
-    EXPECT_EQ(expect, str(got));
-}
-
-}  // namespace
-}  // namespace tint::transform
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index 971d16d..7df2ffb 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -71,7 +71,6 @@
 #include "src/tint/transform/unshadow.h"
 #include "src/tint/transform/unwind_discard_functions.h"
 #include "src/tint/transform/vectorize_scalar_matrix_constructors.h"
-#include "src/tint/transform/wrap_arrays_in_structs.h"
 #include "src/tint/transform/zero_init_workgroup_memory.h"
 #include "src/tint/utils/defer.h"
 #include "src/tint/utils/map.h"
@@ -208,7 +207,6 @@
     manager.Add<transform::PromoteInitializersToConstVar>();
 
     manager.Add<transform::VectorizeScalarMatrixConstructors>();
-    manager.Add<transform::WrapArraysInStructs>();
     manager.Add<transform::RemovePhonies>();
     manager.Add<transform::SimplifyPointers>();
     // ArrayLengthFromUniform must come after SimplifyPointers, as
@@ -731,13 +729,33 @@
                                         const sem::TypeConstructor* ctor) {
     auto* type = ctor->ReturnType();
 
-    if (type->IsAnyOf<sem::Array, sem::Struct>()) {
-        out << "{";
-    } else {
-        if (!EmitType(out, type, "")) {
-            return false;
-        }
-        out << "(";
+    const char* terminator = ")";
+    TINT_DEFER(out << terminator);
+
+    bool ok = Switch(
+        type,
+        [&](const sem::Array*) {
+            if (!EmitType(out, type, "")) {
+                return false;
+            }
+            out << "{";
+            terminator = "}";
+            return true;
+        },
+        [&](const sem::Struct*) {
+            out << "{";
+            terminator = "}";
+            return true;
+        },
+        [&](Default) {
+            if (!EmitType(out, type, "")) {
+                return false;
+            }
+            out << "(";
+            return true;
+        });
+    if (!ok) {
+        return false;
     }
 
     int i = 0;
@@ -760,11 +778,6 @@
         i++;
     }
 
-    if (type->IsAnyOf<sem::Array, sem::Struct>()) {
-        out << "}";
-    } else {
-        out << ")";
-    }
     return true;
 }
 
@@ -1561,10 +1574,9 @@
             ScopedParen sp(out);
             return EmitZeroValue(out, mat->type());
         },
-        [&](const sem::Array* arr) {
-            out << "{";
-            TINT_DEFER(out << "}");
-            return EmitZeroValue(out, arr->ElemType());
+        [&](const sem::Array*) {
+            out << "{}";
+            return true;
         },
         [&](const sem::Struct*) {
             out << "{}";
@@ -1772,8 +1784,8 @@
             if (!EmitType(out, type, param_name)) {
                 return false;
             }
-            // Parameter name is output as part of the type for arrays and pointers.
-            if (!type->Is<sem::Array>() && !type->Is<sem::Pointer>()) {
+            // Parameter name is output as part of the type for pointers.
+            if (!type->Is<sem::Pointer>()) {
                 out << " " << program_->Symbols().NameFor(v->symbol);
             }
         }
@@ -1896,8 +1908,8 @@
             if (!EmitType(out, type, param_name)) {
                 return false;
             }
-            // Parameter name is output as part of the type for arrays and pointers.
-            if (!type->Is<sem::Array>() && !type->Is<sem::Pointer>()) {
+            // Parameter name is output as part of the type for pointers.
+            if (!type->Is<sem::Pointer>()) {
                 out << " " << param_name;
             }
 
@@ -2412,29 +2424,12 @@
                 << "unhandled atomic type " << atomic->Type()->FriendlyName(builder_.Symbols());
             return false;
         },
-        [&](const sem::Array* ary) {
-            const sem::Type* base_type = ary;
-            std::vector<uint32_t> sizes;
-            while (auto* arr = base_type->As<sem::Array>()) {
-                if (arr->IsRuntimeSized()) {
-                    sizes.push_back(1);
-                } else {
-                    sizes.push_back(arr->Count());
-                }
-                base_type = arr->ElemType();
-            }
-            if (!EmitType(out, base_type, "")) {
+        [&](const sem::Array* arr) {
+            out << ArrayType() << "<";
+            if (!EmitType(out, arr->ElemType(), "")) {
                 return false;
             }
-            if (!name.empty()) {
-                out << " " << name;
-                if (name_printed) {
-                    *name_printed = true;
-                }
-            }
-            for (uint32_t size : sizes) {
-                out << "[" << size << "]";
-            }
+            out << ", " << (arr->IsRuntimeSized() ? 1u : arr->Count()) << ">";
             return true;
         },
         [&](const sem::Bool*) {
@@ -2469,22 +2464,12 @@
                 return false;
             }
             out << " ";
-            if (ptr->StoreType()->Is<sem::Array>()) {
-                std::string inner = "(*" + name + ")";
-                if (!EmitType(out, ptr->StoreType(), inner)) {
-                    return false;
-                }
-                if (name_printed) {
-                    *name_printed = true;
-                }
-            } else {
-                if (!EmitType(out, ptr->StoreType(), "")) {
-                    return false;
-                }
-                out << "* " << name;
-                if (name_printed) {
-                    *name_printed = true;
-                }
+            if (!EmitType(out, ptr->StoreType(), "")) {
+                return false;
+            }
+            out << "* " << name;
+            if (name_printed) {
+                *name_printed = true;
             }
             return true;
         },
@@ -2700,7 +2685,7 @@
 
         auto out = line(b);
         add_byte_offset_comment(out, msl_offset);
-        out << "int8_t " << name << "[" << size << "];";
+        out << ArrayType() << "<int8_t, " << size << "> " << name << ";";
     };
 
     b->IncrementIndent();
@@ -2738,11 +2723,7 @@
 
         auto* ty = mem->Type();
 
-        // Array member name will be output with the type
-        if (!ty->Is<sem::Array>()) {
-            out << " " << mem_name;
-        }
-
+        out << " " << mem_name;
         // Emit attributes
         if (auto* decl = mem->Declaration()) {
             for (auto* attr : decl->attributes) {
@@ -2945,8 +2926,8 @@
     if (!EmitType(out, type, name)) {
         return false;
     }
-    // Variable name is output as part of the type for arrays and pointers.
-    if (!type->Is<sem::Array>() && !type->Is<sem::Pointer>()) {
+    // Variable name is output as part of the type for pointers.
+    if (!type->Is<sem::Pointer>()) {
         out << " " << name;
     }
 
@@ -2995,8 +2976,8 @@
         return false;
     }
 
-    // Variable name is output as part of the type for arrays and pointers.
-    if (!type->Is<sem::Array>() && !type->Is<sem::Pointer>()) {
+    // Variable name is output as part of the type for pointers.
+    if (!type->Is<sem::Pointer>()) {
         out << " " << name;
     }
 
@@ -3018,9 +2999,7 @@
     if (!EmitType(out, type, program_->Symbols().NameFor(let->symbol))) {
         return false;
     }
-    if (!type->Is<sem::Array>()) {
-        out << " " << program_->Symbols().NameFor(let->symbol);
-    }
+    out << " " << program_->Symbols().NameFor(let->symbol);
 
     if (let->constructor != nullptr) {
         out << " = ";
@@ -3042,9 +3021,7 @@
     if (!EmitType(out, type, program_->Symbols().NameFor(override->symbol))) {
         return false;
     }
-    if (!type->Is<sem::Array>()) {
-        out << " " << program_->Symbols().NameFor(override->symbol);
-    }
+    out << " " << program_->Symbols().NameFor(override->symbol);
 
     out << " [[function_constant(" << global->ConstantId() << ")]];";
 
@@ -3117,8 +3094,8 @@
 
         [&](const sem::Array* arr) {
             if (!arr->IsStrideImplicit()) {
-                TINT_ICE(Writer, diagnostics_) << "arrays with explicit strides not "
-                                                  "exist past the SPIR-V reader";
+                TINT_ICE(Writer, diagnostics_)
+                    << "arrays with explicit strides should not exist past the SPIR-V reader";
                 return SizeAndAlign{};
             }
             auto num_els = std::max<uint32_t>(arr->Count(), 1);
@@ -3205,4 +3182,25 @@
     return true;
 }
 
+const std::string& GeneratorImpl::ArrayType() {
+    if (array_template_name_.empty()) {
+        array_template_name_ = UniqueIdentifier("tint_array");
+        auto* buf = &helpers_;
+        line(buf) << "template<typename T, size_t N>";
+        line(buf) << "struct " << array_template_name_ << " {";
+        line(buf) << "    const constant T& operator[](size_t i) const constant"
+                  << " { return elements[i]; }";
+        for (auto* space : {"device", "thread", "threadgroup"}) {
+            line(buf) << "    " << space << " T& operator[](size_t i) " << space
+                      << " { return elements[i]; }";
+            line(buf) << "    const " << space << " T& operator[](size_t i) const " << space
+                      << " { return elements[i]; }";
+        }
+        line(buf) << "    T elements[N];";
+        line(buf) << "};";
+        line(buf);
+    }
+    return array_template_name_;
+}
+
 }  // namespace tint::writer::msl
diff --git a/src/tint/writer/msl/generator_impl.h b/src/tint/writer/msl/generator_impl.h
index d80d501..f0d4e8c 100644
--- a/src/tint/writer/msl/generator_impl.h
+++ b/src/tint/writer/msl/generator_impl.h
@@ -425,6 +425,10 @@
                            const sem::Builtin* builtin,
                            F&& build);
 
+    /// @returns the name of the templated tint_array helper type, generating it if this is the
+    /// first call.
+    const std::string& ArrayType();
+
     TextBuffer helpers_;  // Helper functions emitted at the top of the output
 
     /// @returns the MSL packed type size and alignment in bytes for the given
@@ -439,13 +443,17 @@
         utils::UnorderedKeyWrapper<std::tuple<ast::StorageClass, const sem::Struct*>>;
     std::unordered_map<ACEWKeyType, std::string> atomicCompareExchangeWeak_;
 
-    /// Unique name of the 'TINT_INVARIANT' preprocessor define. Non-empty only if
-    /// an invariant attribute has been generated.
+    /// Unique name of the 'TINT_INVARIANT' preprocessor define.
+    /// Non-empty only if an invariant attribute has been generated.
     std::string invariant_define_name_;
 
     /// True if matrix-packed_vector operator overloads have been generated.
     bool matrix_packed_vector_overloads_ = false;
 
+    /// Unique name of the tint_array<T, N> template.
+    /// Non-empty only if the template has been generated.
+    std::string array_template_name_;
+
     /// A map from entry point name to a list of dynamic workgroup allocations.
     /// Each entry in the vector is the size of the workgroup allocation that
     /// should be created for that index.
diff --git a/src/tint/writer/msl/generator_impl_function_test.cc b/src/tint/writer/msl/generator_impl_function_test.cc
index f7d2779..4ef8b12 100644
--- a/src/tint/writer/msl/generator_impl_function_test.cc
+++ b/src/tint/writer/msl/generator_impl_function_test.cc
@@ -591,11 +591,20 @@
     EXPECT_EQ(gen.result(), R"(  #include <metal_stdlib>
 
   using namespace metal;
-  struct tint_array_wrapper {
-    float arr[5];
-  };
 
-  void my_func(tint_array_wrapper a) {
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
+  void my_func(tint_array<float, 5> a) {
     return;
   }
 
@@ -616,12 +625,21 @@
     EXPECT_EQ(gen.result(), R"(  #include <metal_stdlib>
 
   using namespace metal;
-  struct tint_array_wrapper {
-    float arr[5];
-  };
 
-  tint_array_wrapper my_func() {
-    tint_array_wrapper const tint_symbol = {.arr={}};
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
+  tint_array<float, 5> my_func() {
+    tint_array<float, 5> const tint_symbol = tint_array<float, 5>{};
     return tint_symbol;
   }
 
diff --git a/src/tint/writer/msl/generator_impl_module_constant_test.cc b/src/tint/writer/msl/generator_impl_module_constant_test.cc
index f23e51a..12d1cf0 100644
--- a/src/tint/writer/msl/generator_impl_module_constant_test.cc
+++ b/src/tint/writer/msl/generator_impl_module_constant_test.cc
@@ -28,7 +28,7 @@
     GeneratorImpl& gen = Build();
 
     ASSERT_TRUE(gen.EmitProgramConstVariable(var)) << gen.error();
-    EXPECT_EQ(gen.result(), "constant float pos[3] = {1.0f, 2.0f, 3.0f};\n");
+    EXPECT_EQ(gen.result(), "constant tint_array<float, 3> pos = tint_array<float, 3>{1.0f, 2.0f, 3.0f};\n");
 }
 
 TEST_F(MslGeneratorImplTest, Emit_SpecConstant) {
diff --git a/src/tint/writer/msl/generator_impl_sanitizer_test.cc b/src/tint/writer/msl/generator_impl_sanitizer_test.cc
index 475025c..9b9f30b 100644
--- a/src/tint/writer/msl/generator_impl_sanitizer_test.cc
+++ b/src/tint/writer/msl/generator_impl_sanitizer_test.cc
@@ -50,12 +50,25 @@
     auto* expect = R"(#include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 struct my_struct {
-  float a[1];
+  tint_array<float, 1> a;
 };
 
 fragment void a_func(const constant tint_symbol* tint_symbol_2 [[buffer(30)]]) {
@@ -95,13 +108,26 @@
     auto* expect = R"(#include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 struct my_struct {
   float z;
-  float a[1];
+  tint_array<float, 1> a;
 };
 
 fragment void a_func(const constant tint_symbol* tint_symbol_2 [[buffer(30)]]) {
@@ -143,12 +169,25 @@
     auto* expect = R"(#include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 struct my_struct {
-  float a[1];
+  tint_array<float, 1> a;
 };
 
 fragment void a_func(const constant tint_symbol* tint_symbol_2 [[buffer(30)]]) {
@@ -196,12 +235,25 @@
     auto* expect = R"(#include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol {
-  /* 0x0000 */ uint4 buffer_size[2];
+  /* 0x0000 */ tint_array<uint4, 2> buffer_size;
 };
 
 struct my_struct {
-  float a[1];
+  tint_array<float, 1> a;
 };
 
 fragment void a_func(const constant tint_symbol* tint_symbol_2 [[buffer(29)]]) {
diff --git a/src/tint/writer/msl/generator_impl_test.cc b/src/tint/writer/msl/generator_impl_test.cc
index 183fef5..914b693 100644
--- a/src/tint/writer/msl/generator_impl_test.cc
+++ b/src/tint/writer/msl/generator_impl_test.cc
@@ -188,25 +188,34 @@
     EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  float2x2 arr[4];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 struct tint_symbol_3 {
-  tint_array_wrapper m;
+  tint_array<float2x2, 4> m;
 };
 
-void comp_main_inner(uint local_invocation_index, threadgroup tint_array_wrapper* const tint_symbol) {
+void comp_main_inner(uint local_invocation_index, threadgroup tint_array<float2x2, 4>* const tint_symbol) {
   for(uint idx = local_invocation_index; (idx < 4u); idx = (idx + 1u)) {
     uint const i = idx;
-    (*(tint_symbol)).arr[i] = float2x2(float2(0.0f), float2(0.0f));
+    (*(tint_symbol))[i] = float2x2(float2(0.0f), float2(0.0f));
   }
   threadgroup_barrier(mem_flags::mem_threadgroup);
-  tint_array_wrapper const x = *(tint_symbol);
+  tint_array<float2x2, 4> const x = *(tint_symbol);
 }
 
 kernel void comp_main(threadgroup tint_symbol_3* tint_symbol_2 [[threadgroup(0)]], uint local_invocation_index [[thread_index_in_threadgroup]]) {
-  threadgroup tint_array_wrapper* const tint_symbol_1 = &((*(tint_symbol_2)).m);
+  threadgroup tint_array<float2x2, 4>* const tint_symbol_1 = &((*(tint_symbol_2)).m);
   comp_main_inner(local_invocation_index, tint_symbol_1);
   return;
 }
diff --git a/src/tint/writer/msl/generator_impl_type_test.cc b/src/tint/writer/msl/generator_impl_type_test.cc
index 257468b..b8ab89d 100644
--- a/src/tint/writer/msl/generator_impl_type_test.cc
+++ b/src/tint/writer/msl/generator_impl_type_test.cc
@@ -31,6 +31,20 @@
 namespace tint::writer::msl {
 namespace {
 
+void FormatMSLField(std::stringstream& out,
+                    const char* addr,
+                    const char* type,
+                    size_t array_count,
+                    const char* name) {
+    out << "  /* " << std::string(addr) << " */ ";
+    if (array_count == 0) {
+        out << type << " ";
+    } else {
+        out << "tint_array<" << type << ", " << std::to_string(array_count) << "> ";
+    }
+    out << name << ";\n";
+}
+
 #define CHECK_TYPE_SIZE_AND_ALIGN(TYPE, SIZE, ALIGN)      \
     static_assert(sizeof(TYPE) == SIZE, "Bad type size"); \
     static_assert(alignof(TYPE) == ALIGN, "Bad type alignment")
@@ -69,7 +83,7 @@
 
     std::stringstream out;
     ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), "ary")) << gen.error();
-    EXPECT_EQ(out.str(), "bool ary[4]");
+    EXPECT_EQ(out.str(), "tint_array<bool, 4>");
 }
 
 TEST_F(MslGeneratorImplTest, EmitType_ArrayOfArray) {
@@ -81,7 +95,7 @@
 
     std::stringstream out;
     ASSERT_TRUE(gen.EmitType(out, program->TypeOf(b), "ary")) << gen.error();
-    EXPECT_EQ(out.str(), "bool ary[5][4]");
+    EXPECT_EQ(out.str(), "tint_array<tint_array<bool, 4>, 5>");
 }
 
 TEST_F(MslGeneratorImplTest, EmitType_ArrayOfArrayOfArray) {
@@ -94,7 +108,7 @@
 
     std::stringstream out;
     ASSERT_TRUE(gen.EmitType(out, program->TypeOf(c), "ary")) << gen.error();
-    EXPECT_EQ(out.str(), "bool ary[6][5][4]");
+    EXPECT_EQ(out.str(), "tint_array<tint_array<tint_array<bool, 4>, 5>, 6>");
 }
 
 TEST_F(MslGeneratorImplTest, EmitType_Array_WithoutName) {
@@ -105,7 +119,7 @@
 
     std::stringstream out;
     ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), "")) << gen.error();
-    EXPECT_EQ(out.str(), "bool[4]");
+    EXPECT_EQ(out.str(), "tint_array<bool, 4>");
 }
 
 TEST_F(MslGeneratorImplTest, EmitType_RuntimeArray) {
@@ -116,7 +130,7 @@
 
     std::stringstream out;
     ASSERT_TRUE(gen.EmitType(out, program->TypeOf(arr), "ary")) << gen.error();
-    EXPECT_EQ(out.str(), "bool ary[1]");
+    EXPECT_EQ(out.str(), "tint_array<bool, 1>");
 }
 
 TEST_F(MslGeneratorImplTest, EmitType_Bool) {
@@ -245,54 +259,58 @@
     auto* sem_s = program->TypeOf(s)->As<sem::Struct>();
     ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.error();
 
-    // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, NAME, SUFFIX)
+    // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, ARRAY_COUNT, NAME)
     // for each field of the structure s.
-#define ALL_FIELDS()                               \
-    FIELD(0x0000, int, a, /*NO SUFFIX*/)           \
-    FIELD(0x0004, int8_t, tint_pad, [124])         \
-    FIELD(0x0080, float, b, /*NO SUFFIX*/)         \
-    FIELD(0x0084, int8_t, tint_pad_1, [124])       \
-    FIELD(0x0100, float2, c, /*NO SUFFIX*/)        \
-    FIELD(0x0108, uint, d, /*NO SUFFIX*/)          \
-    FIELD(0x010c, int8_t, tint_pad_2, [4])         \
-    FIELD(0x0110, packed_float3, e, /*NO SUFFIX*/) \
-    FIELD(0x011c, uint, f, /*NO SUFFIX*/)          \
-    FIELD(0x0120, float4, g, /*NO SUFFIX*/)        \
-    FIELD(0x0130, uint, h, /*NO SUFFIX*/)          \
-    FIELD(0x0134, int8_t, tint_pad_3, [4])         \
-    FIELD(0x0138, float2x2, i, /*NO SUFFIX*/)      \
-    FIELD(0x0148, uint, j, /*NO SUFFIX*/)          \
-    FIELD(0x014c, int8_t, tint_pad_4, [4])         \
-    FIELD(0x0150, float2x3, k, /*NO SUFFIX*/)      \
-    FIELD(0x0170, uint, l, /*NO SUFFIX*/)          \
-    FIELD(0x0174, int8_t, tint_pad_5, [12])        \
-    FIELD(0x0180, float2x4, m, /*NO SUFFIX*/)      \
-    FIELD(0x01a0, uint, n, /*NO SUFFIX*/)          \
-    FIELD(0x01a4, int8_t, tint_pad_6, [4])         \
-    FIELD(0x01a8, float3x2, o, /*NO SUFFIX*/)      \
-    FIELD(0x01c0, uint, p, /*NO SUFFIX*/)          \
-    FIELD(0x01c4, int8_t, tint_pad_7, [12])        \
-    FIELD(0x01d0, float3x3, q, /*NO SUFFIX*/)      \
-    FIELD(0x0200, uint, r, /*NO SUFFIX*/)          \
-    FIELD(0x0204, int8_t, tint_pad_8, [12])        \
-    FIELD(0x0210, float3x4, s, /*NO SUFFIX*/)      \
-    FIELD(0x0240, uint, t, /*NO SUFFIX*/)          \
-    FIELD(0x0244, int8_t, tint_pad_9, [4])         \
-    FIELD(0x0248, float4x2, u, /*NO SUFFIX*/)      \
-    FIELD(0x0268, uint, v, /*NO SUFFIX*/)          \
-    FIELD(0x026c, int8_t, tint_pad_10, [4])        \
-    FIELD(0x0270, float4x3, w, /*NO SUFFIX*/)      \
-    FIELD(0x02b0, uint, x, /*NO SUFFIX*/)          \
-    FIELD(0x02b4, int8_t, tint_pad_11, [12])       \
-    FIELD(0x02c0, float4x4, y, /*NO SUFFIX*/)      \
-    FIELD(0x0300, float, z, /*NO SUFFIX*/)         \
-    FIELD(0x0304, int8_t, tint_pad_12, [124])
+#define ALL_FIELDS()                       \
+    FIELD(0x0000, int, 0, a)               \
+    FIELD(0x0004, int8_t, 124, tint_pad)   \
+    FIELD(0x0080, float, 0, b)             \
+    FIELD(0x0084, int8_t, 124, tint_pad_1) \
+    FIELD(0x0100, float2, 0, c)            \
+    FIELD(0x0108, uint, 0, d)              \
+    FIELD(0x010c, int8_t, 4, tint_pad_2)   \
+    FIELD(0x0110, packed_float3, 0, e)     \
+    FIELD(0x011c, uint, 0, f)              \
+    FIELD(0x0120, float4, 0, g)            \
+    FIELD(0x0130, uint, 0, h)              \
+    FIELD(0x0134, int8_t, 4, tint_pad_3)   \
+    FIELD(0x0138, float2x2, 0, i)          \
+    FIELD(0x0148, uint, 0, j)              \
+    FIELD(0x014c, int8_t, 4, tint_pad_4)   \
+    FIELD(0x0150, float2x3, 0, k)          \
+    FIELD(0x0170, uint, 0, l)              \
+    FIELD(0x0174, int8_t, 12, tint_pad_5)  \
+    FIELD(0x0180, float2x4, 0, m)          \
+    FIELD(0x01a0, uint, 0, n)              \
+    FIELD(0x01a4, int8_t, 4, tint_pad_6)   \
+    FIELD(0x01a8, float3x2, 0, o)          \
+    FIELD(0x01c0, uint, 0, p)              \
+    FIELD(0x01c4, int8_t, 12, tint_pad_7)  \
+    FIELD(0x01d0, float3x3, 0, q)          \
+    FIELD(0x0200, uint, 0, r)              \
+    FIELD(0x0204, int8_t, 12, tint_pad_8)  \
+    FIELD(0x0210, float3x4, 0, s)          \
+    FIELD(0x0240, uint, 0, t)              \
+    FIELD(0x0244, int8_t, 4, tint_pad_9)   \
+    FIELD(0x0248, float4x2, 0, u)          \
+    FIELD(0x0268, uint, 0, v)              \
+    FIELD(0x026c, int8_t, 4, tint_pad_10)  \
+    FIELD(0x0270, float4x3, 0, w)          \
+    FIELD(0x02b0, uint, 0, x)              \
+    FIELD(0x02b4, int8_t, 12, tint_pad_11) \
+    FIELD(0x02c0, float4x4, 0, y)          \
+    FIELD(0x0300, float, 0, z)             \
+    FIELD(0x0304, int8_t, 124, tint_pad_12)
 
     // Check that the generated string is as expected.
-#define FIELD(ADDR, TYPE, NAME, SUFFIX) "  /* " #ADDR " */ " #TYPE " " #NAME #SUFFIX ";\n"
-    auto* expect = "struct S {\n" ALL_FIELDS() "};\n";
+    std::stringstream expect;
+    expect << "struct S {\n";
+#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) \
+    FormatMSLField(expect, #ADDR, #TYPE, ARRAY_COUNT, #NAME);
+    ALL_FIELDS()
 #undef FIELD
-    EXPECT_EQ(buf.String(), expect);
+    expect << "};\n";
+    EXPECT_EQ(buf.String(), expect.str());
 
     // 1.4 Metal and C++14
     // The Metal programming language is a C++14-based Specification with
@@ -304,12 +322,12 @@
     // layout is as expected for C++14 / MSL.
     {
         struct S {
-#define FIELD(ADDR, TYPE, NAME, SUFFIX) TYPE NAME SUFFIX;
+#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) std::array<TYPE, ARRAY_COUNT ? ARRAY_COUNT : 1> NAME;
             ALL_FIELDS()
 #undef FIELD
         };
 
-#define FIELD(ADDR, TYPE, NAME, SUFFIX) \
+#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) \
     EXPECT_EQ(ADDR, static_cast<int>(offsetof(S, NAME))) << "Field " << #NAME;
         ALL_FIELDS()
 #undef FIELD
@@ -350,22 +368,26 @@
     auto* sem_s = program->TypeOf(s)->As<sem::Struct>();
     ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.error();
 
-    // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, NAME, SUFFIX)
+    // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, ARRAY_COUNT, NAME)
     // for each field of the structure s.
-#define ALL_FIELDS()                         \
-    FIELD(0x0000, int, a, /*NO SUFFIX*/)     \
-    FIELD(0x0004, int8_t, tint_pad, [508])   \
-    FIELD(0x0200, inner_x, b, /*NO SUFFIX*/) \
-    FIELD(0x0600, float, c, /*NO SUFFIX*/)   \
-    FIELD(0x0604, inner_y, d, /*NO SUFFIX*/) \
-    FIELD(0x0808, float, e, /*NO SUFFIX*/)   \
-    FIELD(0x080c, int8_t, tint_pad_1, [500])
+#define ALL_FIELDS()                     \
+    FIELD(0x0000, int, 0, a)             \
+    FIELD(0x0004, int8_t, 508, tint_pad) \
+    FIELD(0x0200, inner_x, 0, b)         \
+    FIELD(0x0600, float, 0, c)           \
+    FIELD(0x0604, inner_y, 0, d)         \
+    FIELD(0x0808, float, 0, e)           \
+    FIELD(0x080c, int8_t, 500, tint_pad_1)
 
     // Check that the generated string is as expected.
-#define FIELD(ADDR, TYPE, NAME, SUFFIX) "  /* " #ADDR " */ " #TYPE " " #NAME #SUFFIX ";\n"
-    auto* expect = "struct S {\n" ALL_FIELDS() "};\n";
+    std::stringstream expect;
+    expect << "struct S {\n";
+#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) \
+    FormatMSLField(expect, #ADDR, #TYPE, ARRAY_COUNT, #NAME);
+    ALL_FIELDS()
 #undef FIELD
-    EXPECT_EQ(buf.String(), expect);
+    expect << "};\n";
+    EXPECT_EQ(buf.String(), expect.str());
 
     // 1.4 Metal and C++14
     // The Metal programming language is a C++14-based Specification with
@@ -389,12 +411,12 @@
         CHECK_TYPE_SIZE_AND_ALIGN(inner_y, 516, 4);
 
         struct S {
-#define FIELD(ADDR, TYPE, NAME, SUFFIX) TYPE NAME SUFFIX;
+#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) std::array<TYPE, ARRAY_COUNT ? ARRAY_COUNT : 1> NAME;
             ALL_FIELDS()
 #undef FIELD
         };
 
-#define FIELD(ADDR, TYPE, NAME, SUFFIX) \
+#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) \
     EXPECT_EQ(ADDR, static_cast<int>(offsetof(S, NAME))) << "Field " << #NAME;
         ALL_FIELDS()
 #undef FIELD
@@ -440,23 +462,27 @@
     auto* sem_s = program->TypeOf(s)->As<sem::Struct>();
     ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.error();
 
-    // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, NAME, SUFFIX)
+    // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, ARRAY_COUNT, NAME)
     // for each field of the structure s.
-#define ALL_FIELDS()                       \
-    FIELD(0x0000, int, a, /*NO SUFFIX*/)   \
-    FIELD(0x0004, float, b, [7])           \
-    FIELD(0x0020, float, c, /*NO SUFFIX*/) \
-    FIELD(0x0024, int8_t, tint_pad, [476]) \
-    FIELD(0x0200, inner, d, [4])           \
-    FIELD(0x1200, float, e, /*NO SUFFIX*/) \
-    FIELD(0x1204, float, f, [1])           \
-    FIELD(0x1208, int8_t, tint_pad_1, [504])
+#define ALL_FIELDS()                     \
+    FIELD(0x0000, int, 0, a)             \
+    FIELD(0x0004, float, 7, b)           \
+    FIELD(0x0020, float, 0, c)           \
+    FIELD(0x0024, int8_t, 476, tint_pad) \
+    FIELD(0x0200, inner, 4, d)           \
+    FIELD(0x1200, float, 0, e)           \
+    FIELD(0x1204, float, 1, f)           \
+    FIELD(0x1208, int8_t, 504, tint_pad_1)
 
     // Check that the generated string is as expected.
-#define FIELD(ADDR, TYPE, NAME, SUFFIX) "  /* " #ADDR " */ " #TYPE " " #NAME #SUFFIX ";\n"
-    auto* expect = "struct S {\n" ALL_FIELDS() "};\n";
+    std::stringstream expect;
+    expect << "struct S {\n";
+#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) \
+    FormatMSLField(expect, #ADDR, #TYPE, ARRAY_COUNT, #NAME);
+    ALL_FIELDS()
 #undef FIELD
-    EXPECT_EQ(buf.String(), expect);
+    expect << "};\n";
+    EXPECT_EQ(buf.String(), expect.str());
 
     // 1.4 Metal and C++14
     // The Metal programming language is a C++14-based Specification with
@@ -486,12 +512,12 @@
         CHECK_TYPE_SIZE_AND_ALIGN(array_z, 4, 4);
 
         struct S {
-#define FIELD(ADDR, TYPE, NAME, SUFFIX) TYPE NAME SUFFIX;
+#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) std::array<TYPE, ARRAY_COUNT ? ARRAY_COUNT : 1> NAME;
             ALL_FIELDS()
 #undef FIELD
         };
 
-#define FIELD(ADDR, TYPE, NAME, SUFFIX) \
+#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) \
     EXPECT_EQ(ADDR, static_cast<int>(offsetof(S, NAME))) << "Field " << #NAME;
         ALL_FIELDS()
 #undef FIELD
@@ -522,20 +548,24 @@
     auto* sem_s = program->TypeOf(s)->As<sem::Struct>();
     ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.error();
 
-    // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, NAME, SUFFIX)
+    // ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, ARRAY_COUNT, NAME)
     // for each field of the structure s.
-#define ALL_FIELDS()                      \
-    FIELD(0x0000, int, a, /*NO SUFFIX*/)  \
-    FIELD(0x0004, int8_t, tint_pad, [12]) \
-    FIELD(0x0010, float3, b, [4])         \
-    FIELD(0x0050, int, c, /*NO SUFFIX*/)  \
-    FIELD(0x0054, int8_t, tint_pad_1, [12])
+#define ALL_FIELDS()                    \
+    FIELD(0x0000, int, 0, a)            \
+    FIELD(0x0004, int8_t, 12, tint_pad) \
+    FIELD(0x0010, float3, 4, b)         \
+    FIELD(0x0050, int, 0, c)            \
+    FIELD(0x0054, int8_t, 12, tint_pad_1)
 
     // Check that the generated string is as expected.
-#define FIELD(ADDR, TYPE, NAME, SUFFIX) "  /* " #ADDR " */ " #TYPE " " #NAME #SUFFIX ";\n"
-    auto* expect = "struct S {\n" ALL_FIELDS() "};\n";
+    std::stringstream expect;
+    expect << "struct S {\n";
+#define FIELD(ADDR, TYPE, ARRAY_COUNT, NAME) \
+    FormatMSLField(expect, #ADDR, #TYPE, ARRAY_COUNT, #NAME);
+    ALL_FIELDS()
 #undef FIELD
-    EXPECT_EQ(buf.String(), expect);
+    expect << "};\n";
+    EXPECT_EQ(buf.String(), expect.str());
 }
 
 TEST_F(MslGeneratorImplTest, AttemptTintPadSymbolCollision) {
@@ -583,44 +613,44 @@
     ASSERT_TRUE(gen.EmitStructType(&buf, sem_s)) << gen.error();
     EXPECT_EQ(buf.String(), R"(struct S {
   /* 0x0000 */ int tint_pad_2;
-  /* 0x0004 */ int8_t tint_pad_10[124];
+  /* 0x0004 */ tint_array<int8_t, 124> tint_pad_10;
   /* 0x0080 */ float tint_pad_20;
-  /* 0x0084 */ int8_t tint_pad_11[124];
+  /* 0x0084 */ tint_array<int8_t, 124> tint_pad_11;
   /* 0x0100 */ float2 tint_pad_33;
   /* 0x0108 */ uint tint_pad_1;
-  /* 0x010c */ int8_t tint_pad_12[4];
+  /* 0x010c */ tint_array<int8_t, 4> tint_pad_12;
   /* 0x0110 */ packed_float3 tint_pad_3;
   /* 0x011c */ uint tint_pad_7;
   /* 0x0120 */ float4 tint_pad_25;
   /* 0x0130 */ uint tint_pad_5;
-  /* 0x0134 */ int8_t tint_pad_13[4];
+  /* 0x0134 */ tint_array<int8_t, 4> tint_pad_13;
   /* 0x0138 */ float2x2 tint_pad_27;
   /* 0x0148 */ uint tint_pad_24;
-  /* 0x014c */ int8_t tint_pad_14[4];
+  /* 0x014c */ tint_array<int8_t, 4> tint_pad_14;
   /* 0x0150 */ float2x3 tint_pad_23;
   /* 0x0170 */ uint tint_pad;
-  /* 0x0174 */ int8_t tint_pad_15[12];
+  /* 0x0174 */ tint_array<int8_t, 12> tint_pad_15;
   /* 0x0180 */ float2x4 tint_pad_8;
   /* 0x01a0 */ uint tint_pad_26;
-  /* 0x01a4 */ int8_t tint_pad_16[4];
+  /* 0x01a4 */ tint_array<int8_t, 4> tint_pad_16;
   /* 0x01a8 */ float3x2 tint_pad_29;
   /* 0x01c0 */ uint tint_pad_6;
-  /* 0x01c4 */ int8_t tint_pad_17[12];
+  /* 0x01c4 */ tint_array<int8_t, 12> tint_pad_17;
   /* 0x01d0 */ float3x3 tint_pad_22;
   /* 0x0200 */ uint tint_pad_32;
-  /* 0x0204 */ int8_t tint_pad_18[12];
+  /* 0x0204 */ tint_array<int8_t, 12> tint_pad_18;
   /* 0x0210 */ float3x4 tint_pad_34;
   /* 0x0240 */ uint tint_pad_35;
-  /* 0x0244 */ int8_t tint_pad_19[4];
+  /* 0x0244 */ tint_array<int8_t, 4> tint_pad_19;
   /* 0x0248 */ float4x2 tint_pad_30;
   /* 0x0268 */ uint tint_pad_9;
-  /* 0x026c */ int8_t tint_pad_36[4];
+  /* 0x026c */ tint_array<int8_t, 4> tint_pad_36;
   /* 0x0270 */ float4x3 tint_pad_31;
   /* 0x02b0 */ uint tint_pad_28;
-  /* 0x02b4 */ int8_t tint_pad_37[12];
+  /* 0x02b4 */ tint_array<int8_t, 12> tint_pad_37;
   /* 0x02c0 */ float4x4 tint_pad_4;
   /* 0x0300 */ float tint_pad_21;
-  /* 0x0304 */ int8_t tint_pad_38[124];
+  /* 0x0304 */ tint_array<int8_t, 124> tint_pad_38;
 };
 )");
 }
diff --git a/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc b/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc
index e41cc3b..f477572 100644
--- a/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc
+++ b/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc
@@ -61,7 +61,7 @@
     gen.increment_indent();
 
     ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
-    EXPECT_EQ(gen.result(), "  float a[5] = {0.0f};\n");
+    EXPECT_EQ(gen.result(), "  tint_array<float, 5> a = {};\n");
 }
 
 TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Struct) {
diff --git a/test/tint/array/assign_to_function_var.wgsl.expected.msl b/test/tint/array/assign_to_function_var.wgsl.expected.msl
index 39cf432..dabd986 100644
--- a/test/tint/array/assign_to_function_var.wgsl.expected.msl
+++ b/test/tint/array/assign_to_function_var.wgsl.expected.msl
@@ -1,16 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  /* 0x0000 */ int4 arr[4];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 struct S {
-  /* 0x0000 */ tint_array_wrapper arr;
+  /* 0x0000 */ tint_array<int4, 4> arr;
 };
 
-tint_array_wrapper ret_arr() {
-  tint_array_wrapper const tint_symbol_1 = {.arr={}};
+tint_array<int4, 4> ret_arr() {
+  tint_array<int4, 4> const tint_symbol_1 = tint_array<int4, 4>{};
   return tint_symbol_1;
 }
 
@@ -19,26 +28,14 @@
   return tint_symbol_2;
 }
 
-struct tint_array_wrapper_3 {
-  int arr[2];
-};
-
-struct tint_array_wrapper_2 {
-  tint_array_wrapper_3 arr[3];
-};
-
-struct tint_array_wrapper_1 {
-  tint_array_wrapper_2 arr[4];
-};
-
-void foo(tint_array_wrapper src_param, thread tint_array_wrapper* const tint_symbol_4, threadgroup tint_array_wrapper* const tint_symbol_5, const constant S* const tint_symbol_6, device S* const tint_symbol_7) {
-  tint_array_wrapper src_function = {};
-  tint_array_wrapper dst = {};
-  tint_array_wrapper const tint_symbol_3 = {.arr={int4(1), int4(2), int4(3), int4(3)}};
+void foo(tint_array<int4, 4> src_param, thread tint_array<int4, 4>* const tint_symbol_4, threadgroup tint_array<int4, 4>* const tint_symbol_5, const constant S* const tint_symbol_6, device S* const tint_symbol_7) {
+  tint_array<int4, 4> src_function = {};
+  tint_array<int4, 4> dst = {};
+  tint_array<int4, 4> const tint_symbol_3 = tint_array<int4, 4>{int4(1), int4(2), int4(3), int4(3)};
   dst = tint_symbol_3;
   dst = src_param;
   dst = ret_arr();
-  tint_array_wrapper const src_let = {.arr={}};
+  tint_array<int4, 4> const src_let = tint_array<int4, 4>{};
   dst = src_let;
   dst = src_function;
   dst = *(tint_symbol_4);
@@ -47,8 +44,8 @@
   dst = tint_symbol.arr;
   dst = (*(tint_symbol_6)).arr;
   dst = (*(tint_symbol_7)).arr;
-  tint_array_wrapper_1 dst_nested = {};
-  tint_array_wrapper_1 src_nested = {};
+  tint_array<tint_array<tint_array<int, 2>, 3>, 4> dst_nested = {};
+  tint_array<tint_array<tint_array<int, 2>, 3>, 4> src_nested = {};
   dst_nested = src_nested;
 }
 
diff --git a/test/tint/array/assign_to_private_var.wgsl.expected.msl b/test/tint/array/assign_to_private_var.wgsl.expected.msl
index be8972f..8ec45d8 100644
--- a/test/tint/array/assign_to_private_var.wgsl.expected.msl
+++ b/test/tint/array/assign_to_private_var.wgsl.expected.msl
@@ -1,28 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  /* 0x0000 */ int4 arr[4];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 struct S {
-  /* 0x0000 */ tint_array_wrapper arr;
+  /* 0x0000 */ tint_array<int4, 4> arr;
 };
 
-struct tint_array_wrapper_3 {
-  int arr[2];
-};
-
-struct tint_array_wrapper_2 {
-  tint_array_wrapper_3 arr[3];
-};
-
-struct tint_array_wrapper_1 {
-  tint_array_wrapper_2 arr[4];
-};
-
-tint_array_wrapper ret_arr() {
-  tint_array_wrapper const tint_symbol_1 = {.arr={}};
+tint_array<int4, 4> ret_arr() {
+  tint_array<int4, 4> const tint_symbol_1 = tint_array<int4, 4>{};
   return tint_symbol_1;
 }
 
@@ -31,13 +28,13 @@
   return tint_symbol_2;
 }
 
-void foo(tint_array_wrapper src_param, thread tint_array_wrapper* const tint_symbol_4, thread tint_array_wrapper* const tint_symbol_5, threadgroup tint_array_wrapper* const tint_symbol_6, const constant S* const tint_symbol_7, device S* const tint_symbol_8, thread tint_array_wrapper_1* const tint_symbol_9) {
-  tint_array_wrapper src_function = {};
-  tint_array_wrapper const tint_symbol_3 = {.arr={int4(1), int4(2), int4(3), int4(3)}};
+void foo(tint_array<int4, 4> src_param, thread tint_array<int4, 4>* const tint_symbol_4, thread tint_array<int4, 4>* const tint_symbol_5, threadgroup tint_array<int4, 4>* const tint_symbol_6, const constant S* const tint_symbol_7, device S* const tint_symbol_8, thread tint_array<tint_array<tint_array<int, 2>, 3>, 4>* const tint_symbol_9) {
+  tint_array<int4, 4> src_function = {};
+  tint_array<int4, 4> const tint_symbol_3 = tint_array<int4, 4>{int4(1), int4(2), int4(3), int4(3)};
   *(tint_symbol_4) = tint_symbol_3;
   *(tint_symbol_4) = src_param;
   *(tint_symbol_4) = ret_arr();
-  tint_array_wrapper const src_let = {.arr={}};
+  tint_array<int4, 4> const src_let = tint_array<int4, 4>{};
   *(tint_symbol_4) = src_let;
   *(tint_symbol_4) = src_function;
   *(tint_symbol_4) = *(tint_symbol_5);
@@ -46,7 +43,7 @@
   *(tint_symbol_4) = tint_symbol.arr;
   *(tint_symbol_4) = (*(tint_symbol_7)).arr;
   *(tint_symbol_4) = (*(tint_symbol_8)).arr;
-  tint_array_wrapper_1 src_nested = {};
+  tint_array<tint_array<tint_array<int, 2>, 3>, 4> src_nested = {};
   *(tint_symbol_9) = src_nested;
 }
 
diff --git a/test/tint/array/assign_to_storage_var.wgsl.expected.msl b/test/tint/array/assign_to_storage_var.wgsl.expected.msl
index 3bf3ccb..3165ec8 100644
--- a/test/tint/array/assign_to_storage_var.wgsl.expected.msl
+++ b/test/tint/array/assign_to_storage_var.wgsl.expected.msl
@@ -1,32 +1,29 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  /* 0x0000 */ int4 arr[4];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 struct S {
-  /* 0x0000 */ tint_array_wrapper arr;
-};
-
-struct tint_array_wrapper_3 {
-  /* 0x0000 */ int arr[2];
-};
-
-struct tint_array_wrapper_2 {
-  /* 0x0000 */ tint_array_wrapper_3 arr[3];
-};
-
-struct tint_array_wrapper_1 {
-  /* 0x0000 */ tint_array_wrapper_2 arr[4];
+  /* 0x0000 */ tint_array<int4, 4> arr;
 };
 
 struct S_nested {
-  /* 0x0000 */ tint_array_wrapper_1 arr;
+  /* 0x0000 */ tint_array<tint_array<tint_array<int, 2>, 3>, 4> arr;
 };
 
-tint_array_wrapper ret_arr() {
-  tint_array_wrapper const tint_symbol_2 = {.arr={}};
+tint_array<int4, 4> ret_arr() {
+  tint_array<int4, 4> const tint_symbol_2 = tint_array<int4, 4>{};
   return tint_symbol_2;
 }
 
@@ -35,14 +32,14 @@
   return tint_symbol_3;
 }
 
-void foo(tint_array_wrapper src_param, device S* const tint_symbol_5, thread tint_array_wrapper* const tint_symbol_6, threadgroup tint_array_wrapper* const tint_symbol_7, const constant S* const tint_symbol_8, device S* const tint_symbol_9, device S_nested* const tint_symbol_10) {
-  tint_array_wrapper src_function = {};
-  tint_array_wrapper const tint_symbol_4 = {.arr={int4(1), int4(2), int4(3), int4(3)}};
+void foo(tint_array<int4, 4> src_param, device S* const tint_symbol_5, thread tint_array<int4, 4>* const tint_symbol_6, threadgroup tint_array<int4, 4>* const tint_symbol_7, const constant S* const tint_symbol_8, device S* const tint_symbol_9, device S_nested* const tint_symbol_10) {
+  tint_array<int4, 4> src_function = {};
+  tint_array<int4, 4> const tint_symbol_4 = tint_array<int4, 4>{int4(1), int4(2), int4(3), int4(3)};
   (*(tint_symbol_5)).arr = tint_symbol_4;
   (*(tint_symbol_5)).arr = src_param;
-  tint_array_wrapper const tint_symbol = ret_arr();
+  tint_array<int4, 4> const tint_symbol = ret_arr();
   (*(tint_symbol_5)).arr = tint_symbol;
-  tint_array_wrapper const src_let = {.arr={}};
+  tint_array<int4, 4> const src_let = tint_array<int4, 4>{};
   (*(tint_symbol_5)).arr = src_let;
   (*(tint_symbol_5)).arr = src_function;
   (*(tint_symbol_5)).arr = *(tint_symbol_6);
@@ -51,7 +48,7 @@
   (*(tint_symbol_5)).arr = tint_symbol_1.arr;
   (*(tint_symbol_5)).arr = (*(tint_symbol_8)).arr;
   (*(tint_symbol_5)).arr = (*(tint_symbol_9)).arr;
-  tint_array_wrapper_1 src_nested = {};
+  tint_array<tint_array<tint_array<int, 2>, 3>, 4> src_nested = {};
   (*(tint_symbol_10)).arr = src_nested;
 }
 
diff --git a/test/tint/array/assign_to_subexpr.wgsl.expected.msl b/test/tint/array/assign_to_subexpr.wgsl.expected.msl
index 735dc30..8851fcd 100644
--- a/test/tint/array/assign_to_subexpr.wgsl.expected.msl
+++ b/test/tint/array/assign_to_subexpr.wgsl.expected.msl
@@ -1,27 +1,32 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  int arr[4];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 struct S {
-  tint_array_wrapper arr;
-};
-
-struct tint_array_wrapper_1 {
-  tint_array_wrapper arr[2];
+  tint_array<int, 4> arr;
 };
 
 void foo() {
-  tint_array_wrapper const src = {.arr={}};
-  tint_array_wrapper dst = {};
+  tint_array<int, 4> const src = tint_array<int, 4>{};
+  tint_array<int, 4> dst = {};
   S dst_struct = {};
-  tint_array_wrapper_1 dst_array = {};
+  tint_array<tint_array<int, 4>, 2> dst_array = {};
   dst_struct.arr = src;
-  dst_array.arr[1] = src;
+  dst_array[1] = src;
   dst = src;
   dst_struct.arr = src;
-  dst_array.arr[0] = src;
+  dst_array[0] = src;
 }
 
diff --git a/test/tint/array/assign_to_workgroup_var.wgsl.expected.msl b/test/tint/array/assign_to_workgroup_var.wgsl.expected.msl
index 9fab331..74df1bb 100644
--- a/test/tint/array/assign_to_workgroup_var.wgsl.expected.msl
+++ b/test/tint/array/assign_to_workgroup_var.wgsl.expected.msl
@@ -1,28 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  /* 0x0000 */ int4 arr[4];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 struct S {
-  /* 0x0000 */ tint_array_wrapper arr;
+  /* 0x0000 */ tint_array<int4, 4> arr;
 };
 
-struct tint_array_wrapper_3 {
-  int arr[2];
-};
-
-struct tint_array_wrapper_2 {
-  tint_array_wrapper_3 arr[3];
-};
-
-struct tint_array_wrapper_1 {
-  tint_array_wrapper_2 arr[4];
-};
-
-tint_array_wrapper ret_arr() {
-  tint_array_wrapper const tint_symbol_1 = {.arr={}};
+tint_array<int4, 4> ret_arr() {
+  tint_array<int4, 4> const tint_symbol_1 = tint_array<int4, 4>{};
   return tint_symbol_1;
 }
 
@@ -31,13 +28,13 @@
   return tint_symbol_2;
 }
 
-void foo(tint_array_wrapper src_param, threadgroup tint_array_wrapper* const tint_symbol_4, thread tint_array_wrapper* const tint_symbol_5, threadgroup tint_array_wrapper* const tint_symbol_6, const constant S* const tint_symbol_7, device S* const tint_symbol_8, threadgroup tint_array_wrapper_1* const tint_symbol_9) {
-  tint_array_wrapper src_function = {};
-  tint_array_wrapper const tint_symbol_3 = {.arr={int4(1), int4(2), int4(3), int4(3)}};
+void foo(tint_array<int4, 4> src_param, threadgroup tint_array<int4, 4>* const tint_symbol_4, thread tint_array<int4, 4>* const tint_symbol_5, threadgroup tint_array<int4, 4>* const tint_symbol_6, const constant S* const tint_symbol_7, device S* const tint_symbol_8, threadgroup tint_array<tint_array<tint_array<int, 2>, 3>, 4>* const tint_symbol_9) {
+  tint_array<int4, 4> src_function = {};
+  tint_array<int4, 4> const tint_symbol_3 = tint_array<int4, 4>{int4(1), int4(2), int4(3), int4(3)};
   *(tint_symbol_4) = tint_symbol_3;
   *(tint_symbol_4) = src_param;
   *(tint_symbol_4) = ret_arr();
-  tint_array_wrapper const src_let = {.arr={}};
+  tint_array<int4, 4> const src_let = tint_array<int4, 4>{};
   *(tint_symbol_4) = src_let;
   *(tint_symbol_4) = src_function;
   *(tint_symbol_4) = *(tint_symbol_5);
@@ -46,7 +43,7 @@
   *(tint_symbol_4) = tint_symbol.arr;
   *(tint_symbol_4) = (*(tint_symbol_7)).arr;
   *(tint_symbol_4) = (*(tint_symbol_8)).arr;
-  tint_array_wrapper_1 src_nested = {};
+  tint_array<tint_array<tint_array<int, 2>, 3>, 4> src_nested = {};
   *(tint_symbol_9) = src_nested;
 }
 
diff --git a/test/tint/array/function_parameter.wgsl.expected.msl b/test/tint/array/function_parameter.wgsl.expected.msl
index 277889e..bf68a61 100644
--- a/test/tint/array/function_parameter.wgsl.expected.msl
+++ b/test/tint/array/function_parameter.wgsl.expected.msl
@@ -1,34 +1,35 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  float arr[4];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
-float f1(tint_array_wrapper a) {
-  return a.arr[3];
+float f1(tint_array<float, 4> a) {
+  return a[3];
 }
 
-struct tint_array_wrapper_1 {
-  tint_array_wrapper arr[3];
-};
-
-float f2(tint_array_wrapper_1 a) {
-  return a.arr[2].arr[3];
+float f2(tint_array<tint_array<float, 4>, 3> a) {
+  return a[2][3];
 }
 
-struct tint_array_wrapper_2 {
-  tint_array_wrapper_1 arr[2];
-};
-
-float f3(tint_array_wrapper_2 a) {
-  return a.arr[1].arr[2].arr[3];
+float f3(tint_array<tint_array<tint_array<float, 4>, 3>, 2> a) {
+  return a[1][2][3];
 }
 
 kernel void tint_symbol() {
-  tint_array_wrapper const a1 = {.arr={}};
-  tint_array_wrapper_1 const a2 = {.arr={}};
-  tint_array_wrapper_2 const a3 = {.arr={}};
+  tint_array<float, 4> const a1 = tint_array<float, 4>{};
+  tint_array<tint_array<float, 4>, 3> const a2 = tint_array<tint_array<float, 4>, 3>{};
+  tint_array<tint_array<tint_array<float, 4>, 3>, 2> const a3 = tint_array<tint_array<tint_array<float, 4>, 3>, 2>{};
   float const v1 = f1(a1);
   float const v2 = f2(a2);
   float const v3 = f3(a3);
diff --git a/test/tint/array/function_return_type.wgsl.expected.msl b/test/tint/array/function_return_type.wgsl.expected.msl
index 8d7634a..703fc88 100644
--- a/test/tint/array/function_return_type.wgsl.expected.msl
+++ b/test/tint/array/function_return_type.wgsl.expected.msl
@@ -1,42 +1,43 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  float arr[4];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
-tint_array_wrapper f1() {
-  tint_array_wrapper const tint_symbol_6 = {.arr={}};
+tint_array<float, 4> f1() {
+  tint_array<float, 4> const tint_symbol_6 = tint_array<float, 4>{};
   return tint_symbol_6;
 }
 
-struct tint_array_wrapper_1 {
-  tint_array_wrapper arr[3];
-};
-
-tint_array_wrapper_1 f2() {
-  tint_array_wrapper const tint_symbol_1 = f1();
-  tint_array_wrapper const tint_symbol_2 = f1();
-  tint_array_wrapper const tint_symbol_3 = f1();
-  tint_array_wrapper_1 const tint_symbol_7 = {.arr={tint_symbol_1, tint_symbol_2, tint_symbol_3}};
+tint_array<tint_array<float, 4>, 3> f2() {
+  tint_array<float, 4> const tint_symbol_1 = f1();
+  tint_array<float, 4> const tint_symbol_2 = f1();
+  tint_array<float, 4> const tint_symbol_3 = f1();
+  tint_array<tint_array<float, 4>, 3> const tint_symbol_7 = tint_array<tint_array<float, 4>, 3>{tint_symbol_1, tint_symbol_2, tint_symbol_3};
   return tint_symbol_7;
 }
 
-struct tint_array_wrapper_2 {
-  tint_array_wrapper_1 arr[2];
-};
-
-tint_array_wrapper_2 f3() {
-  tint_array_wrapper_1 const tint_symbol_4 = f2();
-  tint_array_wrapper_1 const tint_symbol_5 = f2();
-  tint_array_wrapper_2 const tint_symbol_8 = {.arr={tint_symbol_4, tint_symbol_5}};
+tint_array<tint_array<tint_array<float, 4>, 3>, 2> f3() {
+  tint_array<tint_array<float, 4>, 3> const tint_symbol_4 = f2();
+  tint_array<tint_array<float, 4>, 3> const tint_symbol_5 = f2();
+  tint_array<tint_array<tint_array<float, 4>, 3>, 2> const tint_symbol_8 = tint_array<tint_array<tint_array<float, 4>, 3>, 2>{tint_symbol_4, tint_symbol_5};
   return tint_symbol_8;
 }
 
 kernel void tint_symbol() {
-  tint_array_wrapper const a1 = f1();
-  tint_array_wrapper_1 const a2 = f2();
-  tint_array_wrapper_2 const a3 = f3();
+  tint_array<float, 4> const a1 = f1();
+  tint_array<tint_array<float, 4>, 3> const a2 = f2();
+  tint_array<tint_array<tint_array<float, 4>, 3>, 2> const a3 = f3();
   return;
 }
 
diff --git a/test/tint/array/size.wgsl.expected.msl b/test/tint/array/size.wgsl.expected.msl
index 0150ece..27994b2 100644
--- a/test/tint/array/size.wgsl.expected.msl
+++ b/test/tint/array/size.wgsl.expected.msl
@@ -1,19 +1,28 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 constant int slen = 4;
 
 constant uint ulen = 4u;
 
-struct tint_array_wrapper {
-  float arr[4];
-};
-
 fragment void tint_symbol() {
-  tint_array_wrapper signed_literal = {};
-  tint_array_wrapper unsigned_literal = {};
-  tint_array_wrapper signed_constant = {};
-  tint_array_wrapper unsigned_constant = {};
+  tint_array<float, 4> signed_literal = {};
+  tint_array<float, 4> unsigned_literal = {};
+  tint_array<float, 4> signed_constant = {};
+  tint_array<float, 4> unsigned_constant = {};
   signed_literal = unsigned_constant;
   signed_constant = unsigned_literal;
   return;
diff --git a/test/tint/array/strides.spvasm.expected.msl b/test/tint/array/strides.spvasm.expected.msl
index 8db28bd..47d578e 100644
--- a/test/tint/array/strides.spvasm.expected.msl
+++ b/test/tint/array/strides.spvasm.expected.msl
@@ -1,40 +1,41 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct strided_arr {
   /* 0x0000 */ float el;
-  /* 0x0004 */ int8_t tint_pad[4];
-};
-
-struct tint_array_wrapper {
-  /* 0x0000 */ strided_arr arr[2];
-};
-
-struct tint_array_wrapper_1 {
-  /* 0x0000 */ tint_array_wrapper arr[3];
+  /* 0x0004 */ tint_array<int8_t, 4> tint_pad;
 };
 
 struct strided_arr_1 {
-  /* 0x0000 */ tint_array_wrapper_1 el;
-  /* 0x0030 */ int8_t tint_pad_1[80];
-};
-
-struct tint_array_wrapper_2 {
-  /* 0x0000 */ strided_arr_1 arr[4];
+  /* 0x0000 */ tint_array<tint_array<strided_arr, 2>, 3> el;
+  /* 0x0030 */ tint_array<int8_t, 80> tint_pad_1;
 };
 
 struct S {
-  /* 0x0000 */ tint_array_wrapper_2 a;
+  /* 0x0000 */ tint_array<strided_arr_1, 4> a;
 };
 
 void f_1(device S* const tint_symbol_1) {
-  tint_array_wrapper_2 const x_19 = (*(tint_symbol_1)).a;
-  tint_array_wrapper_1 const x_24 = (*(tint_symbol_1)).a.arr[3].el;
-  tint_array_wrapper const x_28 = (*(tint_symbol_1)).a.arr[3].el.arr[2];
-  float const x_32 = (*(tint_symbol_1)).a.arr[3].el.arr[2].arr[1].el;
-  tint_array_wrapper_2 const tint_symbol = {.arr={}};
+  tint_array<strided_arr_1, 4> const x_19 = (*(tint_symbol_1)).a;
+  tint_array<tint_array<strided_arr, 2>, 3> const x_24 = (*(tint_symbol_1)).a[3].el;
+  tint_array<strided_arr, 2> const x_28 = (*(tint_symbol_1)).a[3].el[2];
+  float const x_32 = (*(tint_symbol_1)).a[3].el[2][1].el;
+  tint_array<strided_arr_1, 4> const tint_symbol = tint_array<strided_arr_1, 4>{};
   (*(tint_symbol_1)).a = tint_symbol;
-  (*(tint_symbol_1)).a.arr[3].el.arr[2].arr[1].el = 5.0f;
+  (*(tint_symbol_1)).a[3].el[2][1].el = 5.0f;
   return;
 }
 
diff --git a/test/tint/array/type_constructor.wgsl.expected.msl b/test/tint/array/type_constructor.wgsl.expected.msl
index 092ccee..efe30ae 100644
--- a/test/tint/array/type_constructor.wgsl.expected.msl
+++ b/test/tint/array/type_constructor.wgsl.expected.msl
@@ -1,56 +1,53 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  int arr[4];
-};
 
-struct tint_array_wrapper_2 {
-  tint_array_wrapper arr[3];
-};
-
-struct tint_array_wrapper_1 {
-  tint_array_wrapper_2 arr[2];
-};
-
-struct tint_array_wrapper_3 {
-  tint_array_wrapper arr[2];
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 kernel void tint_symbol() {
   int const x = 42;
-  tint_array_wrapper const empty = {.arr={}};
-  tint_array_wrapper const nonempty = {.arr={1, 2, 3, 4}};
-  tint_array_wrapper const nonempty_with_expr = {.arr={1, 42, as_type<int>((as_type<uint>(42) + as_type<uint>(1))), nonempty.arr[3]}};
-  tint_array_wrapper_1 const nested_empty = {.arr={}};
-  tint_array_wrapper const tint_symbol_1 = {.arr={1, 2, 3, 4}};
-  tint_array_wrapper const tint_symbol_2 = {.arr={5, 6, 7, 8}};
-  tint_array_wrapper const tint_symbol_3 = {.arr={9, 10, 11, 12}};
-  tint_array_wrapper_2 const tint_symbol_4 = {.arr={tint_symbol_1, tint_symbol_2, tint_symbol_3}};
-  tint_array_wrapper const tint_symbol_5 = {.arr={13, 14, 15, 16}};
-  tint_array_wrapper const tint_symbol_6 = {.arr={17, 18, 19, 20}};
-  tint_array_wrapper const tint_symbol_7 = {.arr={21, 22, 23, 24}};
-  tint_array_wrapper_2 const tint_symbol_8 = {.arr={tint_symbol_5, tint_symbol_6, tint_symbol_7}};
-  tint_array_wrapper_1 const nested_nonempty = {.arr={tint_symbol_4, tint_symbol_8}};
-  tint_array_wrapper const tint_symbol_9 = {.arr={1, 2, 42, as_type<int>((as_type<uint>(42) + as_type<uint>(1)))}};
-  tint_array_wrapper const tint_symbol_10 = {.arr={5, 6, nonempty.arr[2], as_type<int>((as_type<uint>(nonempty.arr[3]) + as_type<uint>(1)))}};
-  tint_array_wrapper_2 const tint_symbol_11 = {.arr={tint_symbol_9, tint_symbol_10, nonempty}};
-  tint_array_wrapper_1 const nested_nonempty_with_expr = {.arr={tint_symbol_11, nested_nonempty.arr[1]}};
-  tint_array_wrapper const tint_symbol_12 = {.arr={}};
-  int const subexpr_empty = tint_symbol_12.arr[1];
-  tint_array_wrapper const tint_symbol_13 = {.arr={1, 2, 3, 4}};
-  int const subexpr_nonempty = tint_symbol_13.arr[2];
-  tint_array_wrapper const tint_symbol_14 = {.arr={1, 42, as_type<int>((as_type<uint>(42) + as_type<uint>(1))), nonempty.arr[3]}};
-  int const subexpr_nonempty_with_expr = tint_symbol_14.arr[2];
-  tint_array_wrapper_3 const tint_symbol_15 = {.arr={}};
-  tint_array_wrapper const subexpr_nested_empty = tint_symbol_15.arr[1];
-  tint_array_wrapper const tint_symbol_16 = {.arr={1, 2, 3, 4}};
-  tint_array_wrapper const tint_symbol_17 = {.arr={5, 6, 7, 8}};
-  tint_array_wrapper_3 const tint_symbol_18 = {.arr={tint_symbol_16, tint_symbol_17}};
-  tint_array_wrapper const subexpr_nested_nonempty = tint_symbol_18.arr[1];
-  tint_array_wrapper const tint_symbol_19 = {.arr={1, 42, as_type<int>((as_type<uint>(42) + as_type<uint>(1))), nonempty.arr[3]}};
-  tint_array_wrapper_3 const tint_symbol_20 = {.arr={tint_symbol_19, nested_nonempty.arr[1].arr[2]}};
-  tint_array_wrapper const subexpr_nested_nonempty_with_expr = tint_symbol_20.arr[1];
+  tint_array<int, 4> const empty = tint_array<int, 4>{};
+  tint_array<int, 4> const nonempty = tint_array<int, 4>{1, 2, 3, 4};
+  tint_array<int, 4> const nonempty_with_expr = tint_array<int, 4>{1, 42, as_type<int>((as_type<uint>(42) + as_type<uint>(1))), 4};
+  tint_array<tint_array<tint_array<int, 4>, 3>, 2> const nested_empty = tint_array<tint_array<tint_array<int, 4>, 3>, 2>{};
+  tint_array<int, 4> const tint_symbol_1 = tint_array<int, 4>{1, 2, 3, 4};
+  tint_array<int, 4> const tint_symbol_2 = tint_array<int, 4>{5, 6, 7, 8};
+  tint_array<int, 4> const tint_symbol_3 = tint_array<int, 4>{9, 10, 11, 12};
+  tint_array<tint_array<int, 4>, 3> const tint_symbol_4 = tint_array<tint_array<int, 4>, 3>{tint_symbol_1, tint_symbol_2, tint_symbol_3};
+  tint_array<int, 4> const tint_symbol_5 = tint_array<int, 4>{13, 14, 15, 16};
+  tint_array<int, 4> const tint_symbol_6 = tint_array<int, 4>{17, 18, 19, 20};
+  tint_array<int, 4> const tint_symbol_7 = tint_array<int, 4>{21, 22, 23, 24};
+  tint_array<tint_array<int, 4>, 3> const tint_symbol_8 = tint_array<tint_array<int, 4>, 3>{tint_symbol_5, tint_symbol_6, tint_symbol_7};
+  tint_array<tint_array<tint_array<int, 4>, 3>, 2> const nested_nonempty = tint_array<tint_array<tint_array<int, 4>, 3>, 2>{tint_symbol_4, tint_symbol_8};
+  tint_array<int, 4> const tint_symbol_9 = tint_array<int, 4>{1, 2, 42, as_type<int>((as_type<uint>(42) + as_type<uint>(1)))};
+  tint_array<int, 4> const tint_symbol_10 = tint_array<int, 4>{5, 6, 3, as_type<int>((as_type<uint>(4) + as_type<uint>(1)))};
+  tint_array<tint_array<int, 4>, 3> const tint_symbol_11 = tint_array<tint_array<int, 4>, 3>{tint_symbol_9, tint_symbol_10, nonempty};
+  tint_array<tint_array<tint_array<int, 4>, 3>, 2> const nested_nonempty_with_expr = tint_array<tint_array<tint_array<int, 4>, 3>, 2>{tint_symbol_11, nested_nonempty[1]};
+  tint_array<int, 4> const tint_symbol_12 = tint_array<int, 4>{};
+  int const subexpr_empty = 0;
+  tint_array<int, 4> const tint_symbol_13 = tint_array<int, 4>{1, 2, 3, 4};
+  int const subexpr_nonempty = 3;
+  tint_array<int, 4> const tint_symbol_14 = tint_array<int, 4>{1, 42, as_type<int>((as_type<uint>(42) + as_type<uint>(1))), 4};
+  int const subexpr_nonempty_with_expr = tint_symbol_14[2];
+  tint_array<tint_array<int, 4>, 2> const tint_symbol_15 = tint_array<tint_array<int, 4>, 2>{};
+  tint_array<int, 4> const subexpr_nested_empty = tint_symbol_15[1];
+  tint_array<int, 4> const tint_symbol_16 = tint_array<int, 4>{1, 2, 3, 4};
+  tint_array<int, 4> const tint_symbol_17 = tint_array<int, 4>{5, 6, 7, 8};
+  tint_array<tint_array<int, 4>, 2> const tint_symbol_18 = tint_array<tint_array<int, 4>, 2>{tint_symbol_16, tint_symbol_17};
+  tint_array<int, 4> const subexpr_nested_nonempty = tint_symbol_18[1];
+  tint_array<int, 4> const tint_symbol_19 = tint_array<int, 4>{1, 42, as_type<int>((as_type<uint>(42) + as_type<uint>(1))), 4};
+  tint_array<tint_array<int, 4>, 2> const tint_symbol_20 = tint_array<tint_array<int, 4>, 2>{tint_symbol_19, nested_nonempty[1][2]};
+  tint_array<int, 4> const subexpr_nested_nonempty_with_expr = tint_symbol_20[1];
   return;
 }
 
diff --git a/test/tint/buffer/storage/dynamic_index/read.wgsl.expected.msl b/test/tint/buffer/storage/dynamic_index/read.wgsl.expected.msl
index 530148b..549e70f 100644
--- a/test/tint/buffer/storage/dynamic_index/read.wgsl.expected.msl
+++ b/test/tint/buffer/storage/dynamic_index/read.wgsl.expected.msl
@@ -12,8 +12,16 @@
   return vec<T, M>(lhs) * rhs;
 }
 
-struct tint_array_wrapper {
-  /* 0x0000 */ int4 arr[4];
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 struct Inner {
@@ -25,12 +33,12 @@
   /* 0x002c */ float f;
   /* 0x0030 */ float2x3 g;
   /* 0x0050 */ float3x2 h;
-  /* 0x0068 */ int8_t tint_pad[8];
-  /* 0x0070 */ tint_array_wrapper i;
+  /* 0x0068 */ tint_array<int8_t, 8> tint_pad;
+  /* 0x0070 */ tint_array<int4, 4> i;
 };
 
 struct S {
-  /* 0x0000 */ Inner arr[1];
+  /* 0x0000 */ tint_array<Inner, 1> arr;
 };
 
 void tint_symbol_inner(uint idx, const device S* const tint_symbol_1) {
@@ -42,7 +50,7 @@
   float const f = (*(tint_symbol_1)).arr[idx].f;
   float2x3 const g = (*(tint_symbol_1)).arr[idx].g;
   float3x2 const h = (*(tint_symbol_1)).arr[idx].h;
-  tint_array_wrapper const i = (*(tint_symbol_1)).arr[idx].i;
+  tint_array<int4, 4> const i = (*(tint_symbol_1)).arr[idx].i;
 }
 
 kernel void tint_symbol(const device S* tint_symbol_2 [[buffer(0)]], uint idx [[thread_index_in_threadgroup]]) {
diff --git a/test/tint/buffer/storage/dynamic_index/write.wgsl.expected.msl b/test/tint/buffer/storage/dynamic_index/write.wgsl.expected.msl
index 291f5a9..e2430df 100644
--- a/test/tint/buffer/storage/dynamic_index/write.wgsl.expected.msl
+++ b/test/tint/buffer/storage/dynamic_index/write.wgsl.expected.msl
@@ -12,8 +12,16 @@
   return vec<T, M>(lhs) * rhs;
 }
 
-struct tint_array_wrapper {
-  /* 0x0000 */ int4 arr[4];
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 struct Inner {
@@ -25,12 +33,12 @@
   /* 0x002c */ float f;
   /* 0x0030 */ float2x3 g;
   /* 0x0050 */ float3x2 h;
-  /* 0x0068 */ int8_t tint_pad[8];
-  /* 0x0070 */ tint_array_wrapper i;
+  /* 0x0068 */ tint_array<int8_t, 8> tint_pad;
+  /* 0x0070 */ tint_array<int4, 4> i;
 };
 
 struct S {
-  /* 0x0000 */ Inner arr[1];
+  /* 0x0000 */ tint_array<Inner, 1> arr;
 };
 
 void tint_symbol_inner(uint idx, device S* const tint_symbol_2) {
@@ -42,7 +50,7 @@
   (*(tint_symbol_2)).arr[idx].f = 0.0f;
   (*(tint_symbol_2)).arr[idx].g = float2x3(float3(0.0f), float3(0.0f));
   (*(tint_symbol_2)).arr[idx].h = float3x2(float2(0.0f), float2(0.0f), float2(0.0f));
-  tint_array_wrapper const tint_symbol_1 = {.arr={}};
+  tint_array<int4, 4> const tint_symbol_1 = tint_array<int4, 4>{};
   (*(tint_symbol_2)).arr[idx].i = tint_symbol_1;
 }
 
diff --git a/test/tint/buffer/storage/static_index/read.wgsl.expected.msl b/test/tint/buffer/storage/static_index/read.wgsl.expected.msl
index 8a4f444..8f891a2 100644
--- a/test/tint/buffer/storage/static_index/read.wgsl.expected.msl
+++ b/test/tint/buffer/storage/static_index/read.wgsl.expected.msl
@@ -12,12 +12,20 @@
   return vec<T, M>(lhs) * rhs;
 }
 
-struct Inner {
-  /* 0x0000 */ int x;
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
-struct tint_array_wrapper {
-  /* 0x0000 */ Inner arr[4];
+struct Inner {
+  /* 0x0000 */ int x;
 };
 
 struct S {
@@ -30,8 +38,8 @@
   /* 0x0030 */ float2x3 g;
   /* 0x0050 */ float3x2 h;
   /* 0x0068 */ Inner i;
-  /* 0x006c */ tint_array_wrapper j;
-  /* 0x007c */ int8_t tint_pad[4];
+  /* 0x006c */ tint_array<Inner, 4> j;
+  /* 0x007c */ tint_array<int8_t, 4> tint_pad;
 };
 
 kernel void tint_symbol(const device S* tint_symbol_1 [[buffer(0)]]) {
@@ -44,7 +52,7 @@
   float2x3 const g = (*(tint_symbol_1)).g;
   float3x2 const h = (*(tint_symbol_1)).h;
   Inner const i = (*(tint_symbol_1)).i;
-  tint_array_wrapper const j = (*(tint_symbol_1)).j;
+  tint_array<Inner, 4> const j = (*(tint_symbol_1)).j;
   return;
 }
 
diff --git a/test/tint/buffer/storage/static_index/write.wgsl.expected.msl b/test/tint/buffer/storage/static_index/write.wgsl.expected.msl
index ae3ab67..afb9718 100644
--- a/test/tint/buffer/storage/static_index/write.wgsl.expected.msl
+++ b/test/tint/buffer/storage/static_index/write.wgsl.expected.msl
@@ -12,12 +12,20 @@
   return vec<T, M>(lhs) * rhs;
 }
 
-struct Inner {
-  /* 0x0000 */ int x;
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
-struct tint_array_wrapper {
-  /* 0x0000 */ Inner arr[4];
+struct Inner {
+  /* 0x0000 */ int x;
 };
 
 struct S {
@@ -30,8 +38,8 @@
   /* 0x0030 */ float2x3 g;
   /* 0x0050 */ float3x2 h;
   /* 0x0068 */ Inner i;
-  /* 0x006c */ tint_array_wrapper j;
-  /* 0x007c */ int8_t tint_pad[4];
+  /* 0x006c */ tint_array<Inner, 4> j;
+  /* 0x007c */ tint_array<int8_t, 4> tint_pad;
 };
 
 kernel void tint_symbol(device S* tint_symbol_3 [[buffer(0)]]) {
@@ -45,7 +53,7 @@
   (*(tint_symbol_3)).h = float3x2(float2(0.0f), float2(0.0f), float2(0.0f));
   Inner const tint_symbol_1 = {};
   (*(tint_symbol_3)).i = tint_symbol_1;
-  tint_array_wrapper const tint_symbol_2 = {.arr={}};
+  tint_array<Inner, 4> const tint_symbol_2 = tint_array<Inner, 4>{};
   (*(tint_symbol_3)).j = tint_symbol_2;
   return;
 }
diff --git a/test/tint/buffer/storage/types/array.wgsl.expected.msl b/test/tint/buffer/storage/types/array.wgsl.expected.msl
index 685b94a..9155f75 100644
--- a/test/tint/buffer/storage/types/array.wgsl.expected.msl
+++ b/test/tint/buffer/storage/types/array.wgsl.expected.msl
@@ -1,11 +1,20 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  /* 0x0000 */ float arr[4];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
-kernel void tint_symbol(device tint_array_wrapper* tint_symbol_1 [[buffer(0)]], const device tint_array_wrapper* tint_symbol_2 [[buffer(1)]]) {
+kernel void tint_symbol(device tint_array<float, 4>* tint_symbol_1 [[buffer(0)]], const device tint_array<float, 4>* tint_symbol_2 [[buffer(1)]]) {
   *(tint_symbol_1) = *(tint_symbol_2);
   return;
 }
diff --git a/test/tint/buffer/storage/types/runtime_array.wgsl.expected.msl b/test/tint/buffer/storage/types/runtime_array.wgsl.expected.msl
index 47fcb21..836dba1 100644
--- a/test/tint/buffer/storage/types/runtime_array.wgsl.expected.msl
+++ b/test/tint/buffer/storage/types/runtime_array.wgsl.expected.msl
@@ -1,16 +1,29 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct S {
   /* 0x0000 */ float f;
 };
 
 struct tint_symbol_2 {
-  /* 0x0000 */ S arr[1];
+  /* 0x0000 */ tint_array<S, 1> arr;
 };
 
 struct tint_symbol_4 {
-  /* 0x0000 */ S arr[1];
+  /* 0x0000 */ tint_array<S, 1> arr;
 };
 
 kernel void tint_symbol(device tint_symbol_2* tint_symbol_1 [[buffer(0)]], const device tint_symbol_4* tint_symbol_3 [[buffer(1)]]) {
diff --git a/test/tint/buffer/uniform/dynamic_index/read.wgsl.expected.msl b/test/tint/buffer/uniform/dynamic_index/read.wgsl.expected.msl
index d781b86..e361022 100644
--- a/test/tint/buffer/uniform/dynamic_index/read.wgsl.expected.msl
+++ b/test/tint/buffer/uniform/dynamic_index/read.wgsl.expected.msl
@@ -12,8 +12,16 @@
   return vec<T, M>(lhs) * rhs;
 }
 
-struct tint_array_wrapper {
-  /* 0x0000 */ int4 arr[4];
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 struct Inner {
@@ -27,30 +35,26 @@
   /* 0x0038 */ int2 h;
   /* 0x0040 */ float2x3 i;
   /* 0x0060 */ float3x2 j;
-  /* 0x0078 */ int8_t tint_pad[8];
-  /* 0x0080 */ tint_array_wrapper k;
-};
-
-struct tint_array_wrapper_1 {
-  /* 0x0000 */ Inner arr[8];
+  /* 0x0078 */ tint_array<int8_t, 8> tint_pad;
+  /* 0x0080 */ tint_array<int4, 4> k;
 };
 
 struct S {
-  /* 0x0000 */ tint_array_wrapper_1 arr;
+  /* 0x0000 */ tint_array<Inner, 8> arr;
 };
 
 void tint_symbol_inner(uint idx, const constant S* const tint_symbol_1) {
-  int3 const a = (*(tint_symbol_1)).arr.arr[idx].a;
-  int const b = (*(tint_symbol_1)).arr.arr[idx].b;
-  uint3 const c = (*(tint_symbol_1)).arr.arr[idx].c;
-  uint const d = (*(tint_symbol_1)).arr.arr[idx].d;
-  float3 const e = (*(tint_symbol_1)).arr.arr[idx].e;
-  float const f = (*(tint_symbol_1)).arr.arr[idx].f;
-  int2 const g = (*(tint_symbol_1)).arr.arr[idx].g;
-  int2 const h = (*(tint_symbol_1)).arr.arr[idx].h;
-  float2x3 const i = (*(tint_symbol_1)).arr.arr[idx].i;
-  float3x2 const j = (*(tint_symbol_1)).arr.arr[idx].j;
-  tint_array_wrapper const k = (*(tint_symbol_1)).arr.arr[idx].k;
+  int3 const a = (*(tint_symbol_1)).arr[idx].a;
+  int const b = (*(tint_symbol_1)).arr[idx].b;
+  uint3 const c = (*(tint_symbol_1)).arr[idx].c;
+  uint const d = (*(tint_symbol_1)).arr[idx].d;
+  float3 const e = (*(tint_symbol_1)).arr[idx].e;
+  float const f = (*(tint_symbol_1)).arr[idx].f;
+  int2 const g = (*(tint_symbol_1)).arr[idx].g;
+  int2 const h = (*(tint_symbol_1)).arr[idx].h;
+  float2x3 const i = (*(tint_symbol_1)).arr[idx].i;
+  float3x2 const j = (*(tint_symbol_1)).arr[idx].j;
+  tint_array<int4, 4> const k = (*(tint_symbol_1)).arr[idx].k;
 }
 
 kernel void tint_symbol(const constant S* tint_symbol_2 [[buffer(0)]], uint idx [[thread_index_in_threadgroup]]) {
diff --git a/test/tint/buffer/uniform/static_index/read.wgsl.expected.msl b/test/tint/buffer/uniform/static_index/read.wgsl.expected.msl
index 26abf26..26497bd 100644
--- a/test/tint/buffer/uniform/static_index/read.wgsl.expected.msl
+++ b/test/tint/buffer/uniform/static_index/read.wgsl.expected.msl
@@ -2,6 +2,18 @@
 
 using namespace metal;
 
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 template<typename T, int N, int M>
 inline vec<T, M> operator*(matrix<T, N, M> lhs, packed_vec<T, N> rhs) {
   return lhs * vec<T, N>(rhs);
@@ -14,11 +26,7 @@
 
 struct Inner {
   /* 0x0000 */ int x;
-  /* 0x0004 */ int8_t tint_pad[12];
-};
-
-struct tint_array_wrapper {
-  /* 0x0000 */ Inner arr[4];
+  /* 0x0004 */ tint_array<int8_t, 12> tint_pad;
 };
 
 struct S {
@@ -32,9 +40,9 @@
   /* 0x0038 */ int2 h;
   /* 0x0040 */ float2x3 i;
   /* 0x0060 */ float3x2 j;
-  /* 0x0078 */ int8_t tint_pad_1[8];
+  /* 0x0078 */ tint_array<int8_t, 8> tint_pad_1;
   /* 0x0080 */ Inner k;
-  /* 0x0090 */ tint_array_wrapper l;
+  /* 0x0090 */ tint_array<Inner, 4> l;
 };
 
 kernel void tint_symbol(const constant S* tint_symbol_1 [[buffer(0)]]) {
@@ -49,7 +57,7 @@
   float2x3 const i = (*(tint_symbol_1)).i;
   float3x2 const j = (*(tint_symbol_1)).j;
   Inner const k = (*(tint_symbol_1)).k;
-  tint_array_wrapper const l = (*(tint_symbol_1)).l;
+  tint_array<Inner, 4> const l = (*(tint_symbol_1)).l;
   return;
 }
 
diff --git a/test/tint/buffer/uniform/types/array.wgsl.expected.msl b/test/tint/buffer/uniform/types/array.wgsl.expected.msl
index 71c0b99..730f4b1 100644
--- a/test/tint/buffer/uniform/types/array.wgsl.expected.msl
+++ b/test/tint/buffer/uniform/types/array.wgsl.expected.msl
@@ -1,12 +1,21 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  /* 0x0000 */ float4 arr[4];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
-kernel void tint_symbol(const constant tint_array_wrapper* tint_symbol_1 [[buffer(0)]]) {
-  tint_array_wrapper const x = *(tint_symbol_1);
+kernel void tint_symbol(const constant tint_array<float4, 4>* tint_symbol_1 [[buffer(0)]]) {
+  tint_array<float4, 4> const x = *(tint_symbol_1);
   return;
 }
 
diff --git a/test/tint/bug/chromium/1273230.wgsl.expected.msl b/test/tint/bug/chromium/1273230.wgsl.expected.msl
index 0052f59..08f7854 100644
--- a/test/tint/bug/chromium/1273230.wgsl.expected.msl
+++ b/test/tint/bug/chromium/1273230.wgsl.expected.msl
@@ -12,6 +12,18 @@
   return vec<T, M>(lhs) * rhs;
 }
 
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 void marg8uintin() {
 }
 
@@ -21,9 +33,9 @@
   /* 0x0008 */ uint puuuuuuuuuuuuuuuuad1;
   /* 0x000c */ uint pad2;
   /* 0x0010 */ packed_float3 bbMin;
-  /* 0x001c */ int8_t tint_pad[4];
+  /* 0x001c */ tint_array<int8_t, 4> tint_pad;
   /* 0x0020 */ packed_float3 bbMax;
-  /* 0x002c */ int8_t tint_pad_1[4];
+  /* 0x002c */ tint_array<int8_t, 4> tint_pad_1;
 };
 
 struct Dbg {
@@ -42,23 +54,23 @@
 };
 
 struct F32s {
-  /* 0x0000 */ float values[1];
+  /* 0x0000 */ tint_array<float, 1> values;
 };
 
 struct U32s {
-  /* 0x0000 */ uint values[1];
+  /* 0x0000 */ tint_array<uint, 1> values;
 };
 
 struct I32s {
-  int values[1];
+  tint_array<int, 1> values;
 };
 
 struct AU32s {
-  /* 0x0000 */ atomic_uint values[1];
+  /* 0x0000 */ tint_array<atomic_uint, 1> values;
 };
 
 struct AI32s {
-  /* 0x0000 */ atomic_int values[1];
+  /* 0x0000 */ tint_array<atomic_int, 1> values;
 };
 
 float3 toVoxelPos(float3 position, const constant Uniforms* const tint_symbol) {
diff --git a/test/tint/bug/chromium/1290107.wgsl.expected.msl b/test/tint/bug/chromium/1290107.wgsl.expected.msl
index 5b9d426..de7b6e3 100644
--- a/test/tint/bug/chromium/1290107.wgsl.expected.msl
+++ b/test/tint/bug/chromium/1290107.wgsl.expected.msl
@@ -1,8 +1,21 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol_1 {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 struct S {
diff --git a/test/tint/bug/dawn/947.wgsl.expected.msl b/test/tint/bug/dawn/947.wgsl.expected.msl
index f65b451..68d2045 100644
--- a/test/tint/bug/dawn/947.wgsl.expected.msl
+++ b/test/tint/bug/dawn/947.wgsl.expected.msl
@@ -13,6 +13,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Uniforms {
   /* 0x0000 */ float2 u_scale;
   /* 0x0008 */ float2 u_offset;
@@ -28,19 +41,15 @@
   float4 position [[position]];
 };
 
-struct tint_array_wrapper {
-  float2 arr[3];
-};
-
 VertexOutputs vs_main_inner(uint VertexIndex, const constant Uniforms* const tint_symbol_5) {
-  tint_array_wrapper texcoord = {.arr={float2(-0.5f, 0.0f), float2(1.5f, 0.0f), float2(0.5f, 2.0f)}};
+  tint_array<float2, 3> texcoord = tint_array<float2, 3>{float2(-0.5f, 0.0f), float2(1.5f, 0.0f), float2(0.5f, 2.0f)};
   VertexOutputs output = {};
-  output.position = float4(((texcoord.arr[VertexIndex] * 2.0f) - float2(1.0f)), 0.0f, 1.0f);
+  output.position = float4(((texcoord[VertexIndex] * 2.0f) - float2(1.0f)), 0.0f, 1.0f);
   bool flipY = ((*(tint_symbol_5)).u_scale[1] < 0.0f);
   if (flipY) {
-    output.texcoords = ((((texcoord.arr[VertexIndex] * (*(tint_symbol_5)).u_scale) + (*(tint_symbol_5)).u_offset) * float2(1.0f, -1.0f)) + float2(0.0f, 1.0f));
+    output.texcoords = ((((texcoord[VertexIndex] * (*(tint_symbol_5)).u_scale) + (*(tint_symbol_5)).u_offset) * float2(1.0f, -1.0f)) + float2(0.0f, 1.0f));
   } else {
-    output.texcoords = ((((texcoord.arr[VertexIndex] * float2(1.0f, -1.0f)) + float2(0.0f, 1.0f)) * (*(tint_symbol_5)).u_scale) + (*(tint_symbol_5)).u_offset);
+    output.texcoords = ((((texcoord[VertexIndex] * float2(1.0f, -1.0f)) + float2(0.0f, 1.0f)) * (*(tint_symbol_5)).u_scale) + (*(tint_symbol_5)).u_offset);
   }
   return output;
 }
diff --git a/test/tint/bug/fxc/dyn_array_idx/read/function.wgsl.expected.msl b/test/tint/bug/fxc/dyn_array_idx/read/function.wgsl.expected.msl
index 271eaa1..8f724a6 100644
--- a/test/tint/bug/fxc/dyn_array_idx/read/function.wgsl.expected.msl
+++ b/test/tint/bug/fxc/dyn_array_idx/read/function.wgsl.expected.msl
@@ -1,16 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct UBO {
   /* 0x0000 */ int dynamic_idx;
 };
 
-struct tint_array_wrapper {
-  int arr[64];
-};
-
 struct S {
-  tint_array_wrapper data;
+  tint_array<int, 64> data;
 };
 
 struct Result {
@@ -19,7 +28,7 @@
 
 kernel void f(device Result* tint_symbol [[buffer(1)]], const constant UBO* tint_symbol_1 [[buffer(0)]]) {
   S s = {};
-  (*(tint_symbol)).out = s.data.arr[(*(tint_symbol_1)).dynamic_idx];
+  (*(tint_symbol)).out = s.data[(*(tint_symbol_1)).dynamic_idx];
   return;
 }
 
diff --git a/test/tint/bug/fxc/dyn_array_idx/read/private.wgsl.expected.msl b/test/tint/bug/fxc/dyn_array_idx/read/private.wgsl.expected.msl
index 00b6f03..8ac8114 100644
--- a/test/tint/bug/fxc/dyn_array_idx/read/private.wgsl.expected.msl
+++ b/test/tint/bug/fxc/dyn_array_idx/read/private.wgsl.expected.msl
@@ -1,16 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct UBO {
   /* 0x0000 */ int dynamic_idx;
 };
 
-struct tint_array_wrapper {
-  int arr[64];
-};
-
 struct S {
-  tint_array_wrapper data;
+  tint_array<int, 64> data;
 };
 
 struct Result {
@@ -19,7 +28,7 @@
 
 kernel void f(device Result* tint_symbol [[buffer(1)]], const constant UBO* tint_symbol_2 [[buffer(0)]]) {
   thread S tint_symbol_1 = {};
-  (*(tint_symbol)).out = tint_symbol_1.data.arr[(*(tint_symbol_2)).dynamic_idx];
+  (*(tint_symbol)).out = tint_symbol_1.data[(*(tint_symbol_2)).dynamic_idx];
   return;
 }
 
diff --git a/test/tint/bug/fxc/dyn_array_idx/read/storage.wgsl.expected.msl b/test/tint/bug/fxc/dyn_array_idx/read/storage.wgsl.expected.msl
index 92fb6e6..c802c8f 100644
--- a/test/tint/bug/fxc/dyn_array_idx/read/storage.wgsl.expected.msl
+++ b/test/tint/bug/fxc/dyn_array_idx/read/storage.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct UBO {
   /* 0x0000 */ int dynamic_idx;
 };
@@ -9,16 +22,12 @@
   /* 0x0000 */ int out;
 };
 
-struct tint_array_wrapper {
-  /* 0x0000 */ int arr[4];
-};
-
 struct SSBO {
-  /* 0x0000 */ tint_array_wrapper data;
+  /* 0x0000 */ tint_array<int, 4> data;
 };
 
 kernel void f(device Result* tint_symbol [[buffer(1)]], device SSBO* tint_symbol_1 [[buffer(2)]], const constant UBO* tint_symbol_2 [[buffer(0)]]) {
-  (*(tint_symbol)).out = (*(tint_symbol_1)).data.arr[(*(tint_symbol_2)).dynamic_idx];
+  (*(tint_symbol)).out = (*(tint_symbol_1)).data[(*(tint_symbol_2)).dynamic_idx];
   return;
 }
 
diff --git a/test/tint/bug/fxc/dyn_array_idx/read/uniform.wgsl.expected.msl b/test/tint/bug/fxc/dyn_array_idx/read/uniform.wgsl.expected.msl
index 48d71be..c9ab959 100644
--- a/test/tint/bug/fxc/dyn_array_idx/read/uniform.wgsl.expected.msl
+++ b/test/tint/bug/fxc/dyn_array_idx/read/uniform.wgsl.expected.msl
@@ -1,14 +1,23 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  /* 0x0000 */ int4 arr[4];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 struct UBO {
-  /* 0x0000 */ tint_array_wrapper data;
+  /* 0x0000 */ tint_array<int4, 4> data;
   /* 0x0040 */ int dynamic_idx;
-  /* 0x0044 */ int8_t tint_pad[12];
+  /* 0x0044 */ tint_array<int8_t, 12> tint_pad;
 };
 
 struct Result {
@@ -16,7 +25,7 @@
 };
 
 kernel void f(device Result* tint_symbol [[buffer(1)]], const constant UBO* tint_symbol_1 [[buffer(0)]]) {
-  (*(tint_symbol)).out = (*(tint_symbol_1)).data.arr[(*(tint_symbol_1)).dynamic_idx][0];
+  (*(tint_symbol)).out = (*(tint_symbol_1)).data[(*(tint_symbol_1)).dynamic_idx][0];
   return;
 }
 
diff --git a/test/tint/bug/fxc/dyn_array_idx/read/workgroup.wgsl.expected.msl b/test/tint/bug/fxc/dyn_array_idx/read/workgroup.wgsl.expected.msl
index 2566aba..b8557fd 100644
--- a/test/tint/bug/fxc/dyn_array_idx/read/workgroup.wgsl.expected.msl
+++ b/test/tint/bug/fxc/dyn_array_idx/read/workgroup.wgsl.expected.msl
@@ -1,16 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct UBO {
   /* 0x0000 */ int dynamic_idx;
 };
 
-struct tint_array_wrapper {
-  int arr[64];
-};
-
 struct S {
-  tint_array_wrapper data;
+  tint_array<int, 64> data;
 };
 
 struct Result {
@@ -20,10 +29,10 @@
 void f_inner(uint local_invocation_index, threadgroup S* const tint_symbol, device Result* const tint_symbol_1, const constant UBO* const tint_symbol_2) {
   for(uint idx = local_invocation_index; (idx < 64u); idx = (idx + 1u)) {
     uint const i = idx;
-    (*(tint_symbol)).data.arr[i] = 0;
+    (*(tint_symbol)).data[i] = 0;
   }
   threadgroup_barrier(mem_flags::mem_threadgroup);
-  (*(tint_symbol_1)).out = (*(tint_symbol)).data.arr[(*(tint_symbol_2)).dynamic_idx];
+  (*(tint_symbol_1)).out = (*(tint_symbol)).data[(*(tint_symbol_2)).dynamic_idx];
 }
 
 kernel void f(device Result* tint_symbol_4 [[buffer(1)]], const constant UBO* tint_symbol_5 [[buffer(0)]], uint local_invocation_index [[thread_index_in_threadgroup]]) {
diff --git a/test/tint/bug/fxc/dyn_array_idx/write/function.wgsl.expected.msl b/test/tint/bug/fxc/dyn_array_idx/write/function.wgsl.expected.msl
index c4e1dff..1fe02f1 100644
--- a/test/tint/bug/fxc/dyn_array_idx/write/function.wgsl.expected.msl
+++ b/test/tint/bug/fxc/dyn_array_idx/write/function.wgsl.expected.msl
@@ -1,16 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct UBO {
   /* 0x0000 */ int dynamic_idx;
 };
 
-struct tint_array_wrapper {
-  int arr[64];
-};
-
 struct S {
-  tint_array_wrapper data;
+  tint_array<int, 64> data;
 };
 
 struct Result {
@@ -19,8 +28,8 @@
 
 kernel void f(const constant UBO* tint_symbol [[buffer(0)]], device Result* tint_symbol_1 [[buffer(1)]]) {
   S s = {};
-  s.data.arr[(*(tint_symbol)).dynamic_idx] = 1;
-  (*(tint_symbol_1)).out = s.data.arr[3];
+  s.data[(*(tint_symbol)).dynamic_idx] = 1;
+  (*(tint_symbol_1)).out = s.data[3];
   return;
 }
 
diff --git a/test/tint/bug/fxc/dyn_array_idx/write/function_via_param.wgsl.expected.msl b/test/tint/bug/fxc/dyn_array_idx/write/function_via_param.wgsl.expected.msl
index b9efff8..5aabd3a 100644
--- a/test/tint/bug/fxc/dyn_array_idx/write/function_via_param.wgsl.expected.msl
+++ b/test/tint/bug/fxc/dyn_array_idx/write/function_via_param.wgsl.expected.msl
@@ -1,16 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct UBO {
   /* 0x0000 */ int dynamic_idx;
 };
 
-struct tint_array_wrapper {
-  int arr[64];
-};
-
 struct S {
-  tint_array_wrapper data;
+  tint_array<int, 64> data;
 };
 
 struct Result {
@@ -18,13 +27,13 @@
 };
 
 void x(thread S* const p, const constant UBO* const tint_symbol) {
-  (*(p)).data.arr[(*(tint_symbol)).dynamic_idx] = 1;
+  (*(p)).data[(*(tint_symbol)).dynamic_idx] = 1;
 }
 
 kernel void f(const constant UBO* tint_symbol_1 [[buffer(0)]], device Result* tint_symbol_2 [[buffer(1)]]) {
   S s = {};
   x(&(s), tint_symbol_1);
-  (*(tint_symbol_2)).out = s.data.arr[3];
+  (*(tint_symbol_2)).out = s.data[3];
   return;
 }
 
diff --git a/test/tint/bug/fxc/dyn_array_idx/write/private.wgsl.expected.msl b/test/tint/bug/fxc/dyn_array_idx/write/private.wgsl.expected.msl
index 2a59ee4..6f94f24 100644
--- a/test/tint/bug/fxc/dyn_array_idx/write/private.wgsl.expected.msl
+++ b/test/tint/bug/fxc/dyn_array_idx/write/private.wgsl.expected.msl
@@ -1,16 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct UBO {
   /* 0x0000 */ int dynamic_idx;
 };
 
-struct tint_array_wrapper {
-  int arr[64];
-};
-
 struct S {
-  tint_array_wrapper data;
+  tint_array<int, 64> data;
 };
 
 struct Result {
@@ -19,8 +28,8 @@
 
 kernel void f(const constant UBO* tint_symbol_1 [[buffer(0)]], device Result* tint_symbol_2 [[buffer(1)]]) {
   thread S tint_symbol = {};
-  tint_symbol.data.arr[(*(tint_symbol_1)).dynamic_idx] = 1;
-  (*(tint_symbol_2)).out = tint_symbol.data.arr[3];
+  tint_symbol.data[(*(tint_symbol_1)).dynamic_idx] = 1;
+  (*(tint_symbol_2)).out = tint_symbol.data[3];
   return;
 }
 
diff --git a/test/tint/bug/fxc/dyn_array_idx/write/private_via_param.wgsl.expected.msl b/test/tint/bug/fxc/dyn_array_idx/write/private_via_param.wgsl.expected.msl
index a27679e..0a31f28 100644
--- a/test/tint/bug/fxc/dyn_array_idx/write/private_via_param.wgsl.expected.msl
+++ b/test/tint/bug/fxc/dyn_array_idx/write/private_via_param.wgsl.expected.msl
@@ -1,16 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct UBO {
   /* 0x0000 */ int dynamic_idx;
 };
 
-struct tint_array_wrapper {
-  int arr[64];
-};
-
 struct S {
-  tint_array_wrapper data;
+  tint_array<int, 64> data;
 };
 
 struct Result {
@@ -18,13 +27,13 @@
 };
 
 void x(thread S* const p, const constant UBO* const tint_symbol) {
-  (*(p)).data.arr[(*(tint_symbol)).dynamic_idx] = 1;
+  (*(p)).data[(*(tint_symbol)).dynamic_idx] = 1;
 }
 
 kernel void f(const constant UBO* tint_symbol_2 [[buffer(0)]], device Result* tint_symbol_3 [[buffer(1)]]) {
   thread S tint_symbol_1 = {};
   x(&(tint_symbol_1), tint_symbol_2);
-  (*(tint_symbol_3)).out = tint_symbol_1.data.arr[3];
+  (*(tint_symbol_3)).out = tint_symbol_1.data[3];
   return;
 }
 
diff --git a/test/tint/bug/fxc/dyn_array_idx/write/storage.wgsl.expected.msl b/test/tint/bug/fxc/dyn_array_idx/write/storage.wgsl.expected.msl
index ca1afb5..747fc40 100644
--- a/test/tint/bug/fxc/dyn_array_idx/write/storage.wgsl.expected.msl
+++ b/test/tint/bug/fxc/dyn_array_idx/write/storage.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct UBO {
   /* 0x0000 */ int dynamic_idx;
 };
@@ -9,17 +22,13 @@
   /* 0x0000 */ int out;
 };
 
-struct tint_array_wrapper {
-  /* 0x0000 */ int arr[4];
-};
-
 struct SSBO {
-  /* 0x0000 */ tint_array_wrapper data;
+  /* 0x0000 */ tint_array<int, 4> data;
 };
 
 kernel void f(device SSBO* tint_symbol [[buffer(1)]], const constant UBO* tint_symbol_1 [[buffer(0)]], device Result* tint_symbol_2 [[buffer(2)]]) {
-  (*(tint_symbol)).data.arr[(*(tint_symbol_1)).dynamic_idx] = 1;
-  (*(tint_symbol_2)).out = (*(tint_symbol)).data.arr[3];
+  (*(tint_symbol)).data[(*(tint_symbol_1)).dynamic_idx] = 1;
+  (*(tint_symbol_2)).out = (*(tint_symbol)).data[3];
   return;
 }
 
diff --git a/test/tint/bug/fxc/dyn_array_idx/write/workgroup.wgsl.expected.msl b/test/tint/bug/fxc/dyn_array_idx/write/workgroup.wgsl.expected.msl
index 2a33967..fa5f687 100644
--- a/test/tint/bug/fxc/dyn_array_idx/write/workgroup.wgsl.expected.msl
+++ b/test/tint/bug/fxc/dyn_array_idx/write/workgroup.wgsl.expected.msl
@@ -1,16 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct UBO {
   /* 0x0000 */ int dynamic_idx;
 };
 
-struct tint_array_wrapper {
-  int arr[64];
-};
-
 struct S {
-  tint_array_wrapper data;
+  tint_array<int, 64> data;
 };
 
 struct Result {
@@ -20,11 +29,11 @@
 void f_inner(uint local_invocation_index, threadgroup S* const tint_symbol, const constant UBO* const tint_symbol_1, device Result* const tint_symbol_2) {
   for(uint idx = local_invocation_index; (idx < 64u); idx = (idx + 1u)) {
     uint const i = idx;
-    (*(tint_symbol)).data.arr[i] = 0;
+    (*(tint_symbol)).data[i] = 0;
   }
   threadgroup_barrier(mem_flags::mem_threadgroup);
-  (*(tint_symbol)).data.arr[(*(tint_symbol_1)).dynamic_idx] = 1;
-  (*(tint_symbol_2)).out = (*(tint_symbol)).data.arr[3];
+  (*(tint_symbol)).data[(*(tint_symbol_1)).dynamic_idx] = 1;
+  (*(tint_symbol_2)).out = (*(tint_symbol)).data[3];
 }
 
 kernel void f(const constant UBO* tint_symbol_4 [[buffer(0)]], device Result* tint_symbol_5 [[buffer(1)]], uint local_invocation_index [[thread_index_in_threadgroup]]) {
diff --git a/test/tint/bug/fxc/indexed_assign_to_array_in_struct/1206.wgsl.expected.msl b/test/tint/bug/fxc/indexed_assign_to_array_in_struct/1206.wgsl.expected.msl
index de2ff47..6507f58 100644
--- a/test/tint/bug/fxc/indexed_assign_to_array_in_struct/1206.wgsl.expected.msl
+++ b/test/tint/bug/fxc/indexed_assign_to_array_in_struct/1206.wgsl.expected.msl
@@ -2,6 +2,18 @@
 
 using namespace metal;
 
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 template<typename T, int N, int M>
 inline vec<T, M> operator*(matrix<T, N, M> lhs, packed_vec<T, N> rhs) {
   return lhs * vec<T, N>(rhs);
@@ -16,26 +28,22 @@
   /* 0x0000 */ uint i;
 };
 
-struct tint_array_wrapper {
-  /* 0x0000 */ float3 arr[8];
-};
-
 struct Particle {
-  /* 0x0000 */ tint_array_wrapper position;
+  /* 0x0000 */ tint_array<float3, 8> position;
   /* 0x0080 */ float lifetime;
-  /* 0x0084 */ int8_t tint_pad[12];
+  /* 0x0084 */ tint_array<int8_t, 12> tint_pad;
   /* 0x0090 */ float4 color;
   /* 0x00a0 */ packed_float3 velocity;
-  /* 0x00ac */ int8_t tint_pad_1[4];
+  /* 0x00ac */ tint_array<int8_t, 4> tint_pad_1;
 };
 
 struct Particles {
-  /* 0x0000 */ Particle p[1];
+  /* 0x0000 */ tint_array<Particle, 1> p;
 };
 
 kernel void tint_symbol(const device Particles* tint_symbol_1 [[buffer(1)]], const constant Simulation* tint_symbol_2 [[buffer(0)]]) {
   Particle particle = (*(tint_symbol_1)).p[0];
-  particle.position.arr[(*(tint_symbol_2)).i] = particle.position.arr[(*(tint_symbol_2)).i];
+  particle.position[(*(tint_symbol_2)).i] = particle.position[(*(tint_symbol_2)).i];
   return;
 }
 
diff --git a/test/tint/bug/tint/1046.wgsl.expected.msl b/test/tint/bug/tint/1046.wgsl.expected.msl
index feb5d4d..c79bc0a 100644
--- a/test/tint/bug/tint/1046.wgsl.expected.msl
+++ b/test/tint/bug/tint/1046.wgsl.expected.msl
@@ -1,12 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct PointLight {
   float4 position;
 };
 
 struct PointLights {
-  PointLight values[1];
+  tint_array<PointLight, 1> values;
 };
 
 struct Uniforms {
@@ -14,7 +27,7 @@
   /* 0x0040 */ float4x4 proj;
   /* 0x0080 */ uint numPointLights;
   /* 0x0084 */ uint color_source;
-  /* 0x0088 */ int8_t tint_pad[8];
+  /* 0x0088 */ tint_array<int8_t, 8> tint_pad;
   /* 0x0090 */ float4 color;
 };
 
diff --git a/test/tint/bug/tint/1088.spvasm.expected.msl b/test/tint/bug/tint/1088.spvasm.expected.msl
index b2533c6..392fd22 100644
--- a/test/tint/bug/tint/1088.spvasm.expected.msl
+++ b/test/tint/bug/tint/1088.spvasm.expected.msl
@@ -1,25 +1,30 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  /* 0x0000 */ float4x4 arr[2];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 struct strided_arr {
   /* 0x0000 */ float el;
-  /* 0x0004 */ int8_t tint_pad[12];
-};
-
-struct tint_array_wrapper_1 {
-  /* 0x0000 */ strided_arr arr[4];
+  /* 0x0004 */ tint_array<int8_t, 12> tint_pad;
 };
 
 struct LeftOver {
   /* 0x0000 */ float4x4 worldViewProjection;
   /* 0x0040 */ float time;
-  /* 0x0044 */ int8_t tint_pad_1[12];
-  /* 0x0050 */ tint_array_wrapper test2;
-  /* 0x00d0 */ tint_array_wrapper_1 test;
+  /* 0x0044 */ tint_array<int8_t, 12> tint_pad_1;
+  /* 0x0050 */ tint_array<float4x4, 2> test2;
+  /* 0x00d0 */ tint_array<strided_arr, 4> test;
 };
 
 void main_1(thread float3* const tint_symbol_5, const constant LeftOver* const tint_symbol_6, thread float4* const tint_symbol_7, thread float2* const tint_symbol_8, thread float2* const tint_symbol_9) {
@@ -30,7 +35,7 @@
   float4 const x_21 = q;
   p = float3(x_21[0], x_21[1], x_21[2]);
   float const x_27 = p[0];
-  float const x_41 = (*(tint_symbol_6)).test.arr[0].el;
+  float const x_41 = (*(tint_symbol_6)).test[0].el;
   float const x_45 = (*(tint_symbol_5))[1];
   float const x_49 = (*(tint_symbol_6)).time;
   p[0] = (x_27 + sin(((x_41 * x_45) + x_49)));
diff --git a/test/tint/bug/tint/1113.wgsl.expected.msl b/test/tint/bug/tint/1113.wgsl.expected.msl
index 2c4dcc8..b62d4c5 100644
--- a/test/tint/bug/tint/1113.wgsl.expected.msl
+++ b/test/tint/bug/tint/1113.wgsl.expected.msl
@@ -12,15 +12,27 @@
   return vec<T, M>(lhs) * rhs;
 }
 
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Uniforms {
   /* 0x0000 */ uint numTriangles;
   /* 0x0004 */ uint gridSize;
   /* 0x0008 */ uint pad1;
   /* 0x000c */ uint pad2;
   /* 0x0010 */ packed_float3 bbMin;
-  /* 0x001c */ int8_t tint_pad[4];
+  /* 0x001c */ tint_array<int8_t, 4> tint_pad;
   /* 0x0020 */ packed_float3 bbMax;
-  /* 0x002c */ int8_t tint_pad_1[4];
+  /* 0x002c */ tint_array<int8_t, 4> tint_pad_1;
 };
 
 struct Dbg {
@@ -39,23 +51,23 @@
 };
 
 struct F32s {
-  /* 0x0000 */ float values[1];
+  /* 0x0000 */ tint_array<float, 1> values;
 };
 
 struct U32s {
-  /* 0x0000 */ uint values[1];
+  /* 0x0000 */ tint_array<uint, 1> values;
 };
 
 struct I32s {
-  int values[1];
+  tint_array<int, 1> values;
 };
 
 struct AU32s {
-  /* 0x0000 */ atomic_uint values[1];
+  /* 0x0000 */ tint_array<atomic_uint, 1> values;
 };
 
 struct AI32s {
-  /* 0x0000 */ atomic_int values[1];
+  /* 0x0000 */ tint_array<atomic_int, 1> values;
 };
 
 float3 toVoxelPos(float3 position, const constant Uniforms* const tint_symbol_1) {
diff --git a/test/tint/bug/tint/1121.wgsl.expected.msl b/test/tint/bug/tint/1121.wgsl.expected.msl
index 4800aec..5e8c10f 100644
--- a/test/tint/bug/tint/1121.wgsl.expected.msl
+++ b/test/tint/bug/tint/1121.wgsl.expected.msl
@@ -12,6 +12,18 @@
   return vec<T, M>(lhs) * rhs;
 }
 
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct LightData {
   /* 0x0000 */ float4 position;
   /* 0x0010 */ packed_float3 color;
@@ -19,24 +31,16 @@
 };
 
 struct LightsBuffer {
-  /* 0x0000 */ LightData lights[1];
-};
-
-struct tint_array_wrapper {
-  /* 0x0000 */ uint arr[64];
+  /* 0x0000 */ tint_array<LightData, 1> lights;
 };
 
 struct TileLightIdData {
   /* 0x0000 */ atomic_uint count;
-  /* 0x0004 */ tint_array_wrapper lightId;
-};
-
-struct tint_array_wrapper_1 {
-  /* 0x0000 */ TileLightIdData arr[4];
+  /* 0x0004 */ tint_array<uint, 64> lightId;
 };
 
 struct Tiles {
-  /* 0x0000 */ tint_array_wrapper_1 data;
+  /* 0x0000 */ tint_array<TileLightIdData, 4> data;
 };
 
 struct Config {
@@ -56,10 +60,6 @@
   /* 0x00a0 */ float4 fullScreenSize;
 };
 
-struct tint_array_wrapper_2 {
-  float4 arr[6];
-};
-
 void tint_symbol_inner(uint3 GlobalInvocationID, const constant Config* const tint_symbol_1, device LightsBuffer* const tint_symbol_2, const constant Uniforms* const tint_symbol_3, device Tiles* const tint_symbol_4) {
   uint index = GlobalInvocationID[0];
   if ((index >= (*(tint_symbol_1)).numLights)) {
@@ -78,9 +78,9 @@
   float lightRadius = (*(tint_symbol_2)).lights[index].radius;
   float4 boxMin = (lightPos - float4(float3(lightRadius), 0.0f));
   float4 boxMax = (lightPos + float4(float3(lightRadius), 0.0f));
-  tint_array_wrapper_2 frustumPlanes = {};
-  frustumPlanes.arr[4] = float4(0.0f, 0.0f, -1.0f, viewNear);
-  frustumPlanes.arr[5] = float4(0.0f, 0.0f, 1.0f, -(viewFar));
+  tint_array<float4, 6> frustumPlanes = {};
+  frustumPlanes[4] = float4(0.0f, 0.0f, -1.0f, viewNear);
+  frustumPlanes[5] = float4(0.0f, 0.0f, 1.0f, -(viewFar));
   int const TILE_SIZE = 16;
   int const TILE_COUNT_X = 2;
   int const TILE_COUNT_Y = 2;
@@ -91,41 +91,41 @@
       float2 ceilCoord = (((2.0f * float2(as_type<int2>((as_type<uint2>(tilePixel0Idx) + as_type<uint2>(int2(16)))))) / float4((*(tint_symbol_3)).fullScreenSize).xy) - float2(1.0f));
       float2 viewFloorCoord = float2((((-(viewNear) * floorCoord[0]) - (M[2][0] * viewNear)) / M[0][0]), (((-(viewNear) * floorCoord[1]) - (M[2][1] * viewNear)) / M[1][1]));
       float2 viewCeilCoord = float2((((-(viewNear) * ceilCoord[0]) - (M[2][0] * viewNear)) / M[0][0]), (((-(viewNear) * ceilCoord[1]) - (M[2][1] * viewNear)) / M[1][1]));
-      frustumPlanes.arr[0] = float4(1.0f, 0.0f, (-(viewFloorCoord[0]) / viewNear), 0.0f);
-      frustumPlanes.arr[1] = float4(-1.0f, 0.0f, (viewCeilCoord[0] / viewNear), 0.0f);
-      frustumPlanes.arr[2] = float4(0.0f, 1.0f, (-(viewFloorCoord[1]) / viewNear), 0.0f);
-      frustumPlanes.arr[3] = float4(0.0f, -1.0f, (viewCeilCoord[1] / viewNear), 0.0f);
+      frustumPlanes[0] = float4(1.0f, 0.0f, (-(viewFloorCoord[0]) / viewNear), 0.0f);
+      frustumPlanes[1] = float4(-1.0f, 0.0f, (viewCeilCoord[0] / viewNear), 0.0f);
+      frustumPlanes[2] = float4(0.0f, 1.0f, (-(viewFloorCoord[1]) / viewNear), 0.0f);
+      frustumPlanes[3] = float4(0.0f, -1.0f, (viewCeilCoord[1] / viewNear), 0.0f);
       float dp = 0.0f;
       for(uint i = 0u; (i < 6u); i = (i + 1u)) {
         float4 p = 0.0f;
-        if ((frustumPlanes.arr[i][0] > 0.0f)) {
+        if ((frustumPlanes[i][0] > 0.0f)) {
           p[0] = boxMax[0];
         } else {
           p[0] = boxMin[0];
         }
-        if ((frustumPlanes.arr[i][1] > 0.0f)) {
+        if ((frustumPlanes[i][1] > 0.0f)) {
           p[1] = boxMax[1];
         } else {
           p[1] = boxMin[1];
         }
-        if ((frustumPlanes.arr[i][2] > 0.0f)) {
+        if ((frustumPlanes[i][2] > 0.0f)) {
           p[2] = boxMax[2];
         } else {
           p[2] = boxMin[2];
         }
         p[3] = 1.0f;
-        dp = (dp + fmin(0.0f, dot(p, frustumPlanes.arr[i])));
+        dp = (dp + fmin(0.0f, dot(p, frustumPlanes[i])));
       }
       if ((dp >= 0.0f)) {
         uint tileId = uint(as_type<int>((as_type<uint>(x_1) + as_type<uint>(as_type<int>((as_type<uint>(y_1) * as_type<uint>(2)))))));
         if (((tileId < 0u) || (tileId >= (*(tint_symbol_1)).numTiles))) {
           continue;
         }
-        uint offset = atomic_fetch_add_explicit(&((*(tint_symbol_4)).data.arr[tileId].count), 1u, memory_order_relaxed);
+        uint offset = atomic_fetch_add_explicit(&((*(tint_symbol_4)).data[tileId].count), 1u, memory_order_relaxed);
         if ((offset >= (*(tint_symbol_1)).numTileLightSlot)) {
           continue;
         }
-        (*(tint_symbol_4)).data.arr[tileId].lightId.arr[offset] = GlobalInvocationID[0];
+        (*(tint_symbol_4)).data[tileId].lightId[offset] = GlobalInvocationID[0];
       }
     }
   }
diff --git a/test/tint/bug/tint/1321.wgsl.expected.msl b/test/tint/bug/tint/1321.wgsl.expected.msl
index 8c96d14..c67bb76 100644
--- a/test/tint/bug/tint/1321.wgsl.expected.msl
+++ b/test/tint/bug/tint/1321.wgsl.expected.msl
@@ -1,20 +1,29 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 int foo() {
   return 1;
 }
 
-struct tint_array_wrapper {
-  float arr[4];
-};
-
 fragment void tint_symbol() {
-  tint_array_wrapper arr = {.arr={}};
+  tint_array<float, 4> arr = tint_array<float, 4>{};
   int const tint_symbol_1 = foo();
   int const a_save = tint_symbol_1;
   for(; ; ) {
-    float const x = arr.arr[a_save];
+    float const x = arr[a_save];
     break;
   }
   return;
diff --git a/test/tint/bug/tint/1385.wgsl.expected.msl b/test/tint/bug/tint/1385.wgsl.expected.msl
index 838babd..c57afce 100644
--- a/test/tint/bug/tint/1385.wgsl.expected.msl
+++ b/test/tint/bug/tint/1385.wgsl.expected.msl
@@ -1,11 +1,24 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_symbol_3 {
-  /* 0x0000 */ int arr[1];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
-int foo(const device int (*const tint_symbol_1)[1]) {
+struct tint_symbol_3 {
+  /* 0x0000 */ tint_array<int, 1> arr;
+};
+
+int foo(const device tint_array<int, 1>* const tint_symbol_1) {
   return (*(tint_symbol_1))[0];
 }
 
diff --git a/test/tint/bug/tint/1520.spvasm.expected.msl b/test/tint/bug/tint/1520.spvasm.expected.msl
index 909a052..a556dee 100644
--- a/test/tint/bug/tint/1520.spvasm.expected.msl
+++ b/test/tint/bug/tint/1520.spvasm.expected.msl
@@ -1,10 +1,23 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct UniformBuffer {
-  /* 0x0000 */ int8_t tint_pad[16];
+  /* 0x0000 */ tint_array<int8_t, 16> tint_pad;
   /* 0x0010 */ float unknownInput_S1_c0;
-  /* 0x0014 */ int8_t tint_pad_1[12];
+  /* 0x0014 */ tint_array<int8_t, 12> tint_pad_1;
   /* 0x0020 */ float4 ucolorRed_S1_c0;
   /* 0x0030 */ float4 ucolorGreen_S1_c0;
   /* 0x0040 */ float3x3 umatrix_S1;
diff --git a/test/tint/bug/tint/221.wgsl.expected.msl b/test/tint/bug/tint/221.wgsl.expected.msl
index 880fc07..ad7d150 100644
--- a/test/tint/bug/tint/221.wgsl.expected.msl
+++ b/test/tint/bug/tint/221.wgsl.expected.msl
@@ -1,13 +1,22 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  /* 0x0000 */ uint arr[50];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 struct Buf {
   /* 0x0000 */ uint count;
-  /* 0x0004 */ tint_array_wrapper data;
+  /* 0x0004 */ tint_array<uint, 50> data;
 };
 
 kernel void tint_symbol(device Buf* tint_symbol_1 [[buffer(0)]]) {
@@ -19,14 +28,14 @@
     uint const p_save = i;
     if (((i % 2u) == 0u)) {
       {
-        (*(tint_symbol_1)).data.arr[p_save] = ((*(tint_symbol_1)).data.arr[p_save] * 2u);
+        (*(tint_symbol_1)).data[p_save] = ((*(tint_symbol_1)).data[p_save] * 2u);
         i = (i + 1u);
       }
       continue;
     }
-    (*(tint_symbol_1)).data.arr[p_save] = 0u;
+    (*(tint_symbol_1)).data[p_save] = 0u;
     {
-      (*(tint_symbol_1)).data.arr[p_save] = ((*(tint_symbol_1)).data.arr[p_save] * 2u);
+      (*(tint_symbol_1)).data[p_save] = ((*(tint_symbol_1)).data[p_save] * 2u);
       i = (i + 1u);
     }
   }
diff --git a/test/tint/bug/tint/294.wgsl.expected.msl b/test/tint/bug/tint/294.wgsl.expected.msl
index 09c1cf5..77c8cea 100644
--- a/test/tint/bug/tint/294.wgsl.expected.msl
+++ b/test/tint/bug/tint/294.wgsl.expected.msl
@@ -1,12 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Light {
   float3 position;
   float3 colour;
 };
 
 struct Lights {
-  Light light[1];
+  tint_array<Light, 1> light;
 };
 
diff --git a/test/tint/bug/tint/403.wgsl.expected.msl b/test/tint/bug/tint/403.wgsl.expected.msl
index 94e0fbe..10c2d04 100644
--- a/test/tint/bug/tint/403.wgsl.expected.msl
+++ b/test/tint/bug/tint/403.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct vertexUniformBuffer1 {
   /* 0x0000 */ float2x2 transform1;
 };
@@ -13,18 +26,14 @@
   float4 value [[position]];
 };
 
-struct tint_array_wrapper {
-  float2 arr[3];
-};
-
 float4 tint_symbol_inner(uint gl_VertexIndex, const constant vertexUniformBuffer1* const tint_symbol_3, const constant vertexUniformBuffer2* const tint_symbol_4) {
-  tint_array_wrapper indexable = {};
+  tint_array<float2, 3> indexable = {};
   float2x2 const x_23 = (*(tint_symbol_3)).transform1;
   float2x2 const x_28 = (*(tint_symbol_4)).transform2;
   uint const x_46 = gl_VertexIndex;
-  tint_array_wrapper const tint_symbol_2 = {.arr={float2(-1.0f, 1.0f), float2(1.0f), float2(-1.0f)}};
+  tint_array<float2, 3> const tint_symbol_2 = tint_array<float2, 3>{float2(-1.0f, 1.0f), float2(1.0f), float2(-1.0f)};
   indexable = tint_symbol_2;
-  float2 const x_51 = indexable.arr[x_46];
+  float2 const x_51 = indexable[x_46];
   float2 const x_52 = (float2x2((x_23[0u] + x_28[0u]), (x_23[1u] + x_28[1u])) * x_51);
   return float4(x_52[0], x_52[1], 0.0f, 1.0f);
 }
diff --git a/test/tint/bug/tint/534.wgsl.expected.msl b/test/tint/bug/tint/534.wgsl.expected.msl
index a766b37..2323162 100644
--- a/test/tint/bug/tint/534.wgsl.expected.msl
+++ b/test/tint/bug/tint/534.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Uniforms {
   /* 0x0000 */ uint dstTextureFlipY;
   /* 0x0004 */ uint isFloat16;
@@ -9,7 +22,7 @@
 };
 
 struct OutputBuf {
-  /* 0x0000 */ uint result[1];
+  /* 0x0000 */ tint_array<uint, 1> result;
 };
 
 uint ConvertToFp16FloatValue(float fp32) {
diff --git a/test/tint/bug/tint/744.wgsl.expected.msl b/test/tint/bug/tint/744.wgsl.expected.msl
index c4dc5b4..7c01635 100644
--- a/test/tint/bug/tint/744.wgsl.expected.msl
+++ b/test/tint/bug/tint/744.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Uniforms {
   /* 0x0000 */ uint2 aShape;
   /* 0x0008 */ uint2 bShape;
@@ -8,7 +21,7 @@
 };
 
 struct Matrix {
-  /* 0x0000 */ uint numbers[1];
+  /* 0x0000 */ tint_array<uint, 1> numbers;
 };
 
 void tint_symbol_inner(uint3 global_id, const constant Uniforms* const tint_symbol_1, const device Matrix* const tint_symbol_2, const device Matrix* const tint_symbol_3, device Matrix* const tint_symbol_4) {
diff --git a/test/tint/bug/tint/749.spvasm.expected.msl b/test/tint/bug/tint/749.spvasm.expected.msl
index 89108e5..f14f399 100644
--- a/test/tint/bug/tint/749.spvasm.expected.msl
+++ b/test/tint/bug/tint/749.spvasm.expected.msl
@@ -1,12 +1,21 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  int arr[10];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 struct QuicksortObject {
-  tint_array_wrapper numbers;
+  tint_array<int, 10> numbers;
 };
 
 struct buf0 {
@@ -31,10 +40,10 @@
   temp = 0;
   temp = x_935;
   int const x_30_save = x_28;
-  int const x_936 = (*(tint_symbol_81)).numbers.arr[x_30_save];
-  (*(tint_symbol_81)).numbers.arr[x_30_save] = 0;
-  (*(tint_symbol_81)).numbers.arr[x_30_save] = x_936;
-  int const x_31 = (*(tint_symbol_81)).numbers.arr[x_30_save];
+  int const x_936 = (*(tint_symbol_81)).numbers[x_30_save];
+  (*(tint_symbol_81)).numbers[x_30_save] = 0;
+  (*(tint_symbol_81)).numbers[x_30_save] = x_936;
+  int const x_31 = (*(tint_symbol_81)).numbers[x_30_save];
   int const x_937 = temp;
   temp = 0;
   temp = x_937;
@@ -47,30 +56,30 @@
   *(i) = 0;
   *(i) = x_939;
   int const x_32 = *(i);
-  int const x_940 = (*(tint_symbol_81)).numbers.arr[x_30_save];
-  (*(tint_symbol_81)).numbers.arr[x_30_save] = 0;
-  (*(tint_symbol_81)).numbers.arr[x_30_save] = x_940;
+  int const x_940 = (*(tint_symbol_81)).numbers[x_30_save];
+  (*(tint_symbol_81)).numbers[x_30_save] = 0;
+  (*(tint_symbol_81)).numbers[x_30_save] = x_940;
   int const x_33 = *(j);
   int const x_941 = *(i);
   *(i) = 0;
   *(i) = x_941;
   float3 const x_526 = float3(x_525[0], x_525[2], x_525[2]);
-  int const x_942 = (*(tint_symbol_81)).numbers.arr[x_30_save];
-  (*(tint_symbol_81)).numbers.arr[x_30_save] = 0;
-  (*(tint_symbol_81)).numbers.arr[x_30_save] = x_942;
+  int const x_942 = (*(tint_symbol_81)).numbers[x_30_save];
+  (*(tint_symbol_81)).numbers[x_30_save] = 0;
+  (*(tint_symbol_81)).numbers[x_30_save] = x_942;
   int const x_34_save = x_33;
-  int const x_35 = (*(tint_symbol_81)).numbers.arr[x_34_save];
+  int const x_35 = (*(tint_symbol_81)).numbers[x_34_save];
   QuicksortObject const x_943 = *(tint_symbol_81);
-  tint_array_wrapper const tint_symbol_2 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+  tint_array<int, 10> const tint_symbol_2 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   QuicksortObject const tint_symbol_3 = {.numbers=tint_symbol_2};
   *(tint_symbol_81) = tint_symbol_3;
   *(tint_symbol_81) = x_943;
   float2 const x_527 = float2(x_526[0], x_526[0]);
   int const x_36_save = x_32;
   float3 const x_528 = float3(x_524[0], x_524[2], x_524[0]);
-  (*(tint_symbol_81)).numbers.arr[x_36_save] = x_35;
+  (*(tint_symbol_81)).numbers[x_36_save] = x_35;
   QuicksortObject const x_944 = *(tint_symbol_81);
-  tint_array_wrapper const tint_symbol_4 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+  tint_array<int, 10> const tint_symbol_4 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   QuicksortObject const tint_symbol_5 = {.numbers=tint_symbol_4};
   *(tint_symbol_81) = tint_symbol_5;
   *(tint_symbol_81) = x_944;
@@ -83,27 +92,27 @@
   temp = 0;
   temp = x_946;
   float2 const x_530 = float2(x_529[2], x_529[1]);
-  int const x_947 = (*(tint_symbol_81)).numbers.arr[x_34_save];
-  (*(tint_symbol_81)).numbers.arr[x_34_save] = 0;
-  (*(tint_symbol_81)).numbers.arr[x_34_save] = x_947;
+  int const x_947 = (*(tint_symbol_81)).numbers[x_34_save];
+  (*(tint_symbol_81)).numbers[x_34_save] = 0;
+  (*(tint_symbol_81)).numbers[x_34_save] = x_947;
   int const x_38 = temp;
   int const x_948 = *(j);
   *(j) = 0;
   *(j) = x_948;
   float3 const x_531 = float3(x_527[0], x_526[1], x_526[0]);
-  int const x_949 = (*(tint_symbol_81)).numbers.arr[x_36_save];
-  (*(tint_symbol_81)).numbers.arr[x_36_save] = 0;
-  (*(tint_symbol_81)).numbers.arr[x_36_save] = x_949;
+  int const x_949 = (*(tint_symbol_81)).numbers[x_36_save];
+  (*(tint_symbol_81)).numbers[x_36_save] = 0;
+  (*(tint_symbol_81)).numbers[x_36_save] = x_949;
   QuicksortObject const x_950 = *(tint_symbol_81);
-  tint_array_wrapper const tint_symbol_6 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+  tint_array<int, 10> const tint_symbol_6 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   QuicksortObject const tint_symbol_7 = {.numbers=tint_symbol_6};
   *(tint_symbol_81) = tint_symbol_7;
   *(tint_symbol_81) = x_950;
   float3 const x_532 = float3(x_528[0], x_528[1], x_528[0]);
-  int const x_951 = (*(tint_symbol_81)).numbers.arr[x_34_save];
-  (*(tint_symbol_81)).numbers.arr[x_34_save] = 0;
-  (*(tint_symbol_81)).numbers.arr[x_34_save] = x_951;
-  (*(tint_symbol_81)).numbers.arr[x_37] = x_38;
+  int const x_951 = (*(tint_symbol_81)).numbers[x_34_save];
+  (*(tint_symbol_81)).numbers[x_34_save] = 0;
+  (*(tint_symbol_81)).numbers[x_34_save] = x_951;
+  (*(tint_symbol_81)).numbers[x_37] = x_38;
   return;
 }
 
@@ -125,10 +134,10 @@
   *(l) = 0;
   *(l) = x_953;
   int const x_42_save = x_41;
-  int const x_954 = (*(tint_symbol_82)).numbers.arr[x_42_save];
-  (*(tint_symbol_82)).numbers.arr[x_42_save] = 0;
-  (*(tint_symbol_82)).numbers.arr[x_42_save] = x_954;
-  int const x_43 = (*(tint_symbol_82)).numbers.arr[x_42_save];
+  int const x_954 = (*(tint_symbol_82)).numbers[x_42_save];
+  (*(tint_symbol_82)).numbers[x_42_save] = 0;
+  (*(tint_symbol_82)).numbers[x_42_save] = x_954;
+  int const x_43 = (*(tint_symbol_82)).numbers[x_42_save];
   int const x_955 = param_3;
   param_3 = 0;
   param_3 = x_955;
@@ -153,7 +162,7 @@
   float3 const x_536 = float3(x_534[0], x_534[2], x_535[0]);
   j_1 = 10;
   QuicksortObject const x_960 = *(tint_symbol_82);
-  tint_array_wrapper const tint_symbol_8 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+  tint_array<int, 10> const tint_symbol_8 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   QuicksortObject const tint_symbol_9 = {.numbers=tint_symbol_8};
   *(tint_symbol_82) = tint_symbol_9;
   *(tint_symbol_82) = x_960;
@@ -170,7 +179,7 @@
     pivot = x_963;
     x_537 = float2(float3(1.0f, 2.0f, 3.0f)[1], float3(1.0f, 2.0f, 3.0f)[2]);
     QuicksortObject const x_964 = *(tint_symbol_82);
-    tint_array_wrapper const tint_symbol_10 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+    tint_array<int, 10> const tint_symbol_10 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     QuicksortObject const tint_symbol_11 = {.numbers=tint_symbol_10};
     *(tint_symbol_82) = tint_symbol_11;
     *(tint_symbol_82) = x_964;
@@ -193,9 +202,9 @@
       break;
     }
     int const x_60 = j_1;
-    int const x_969 = (*(tint_symbol_82)).numbers.arr[x_42_save];
-    (*(tint_symbol_82)).numbers.arr[x_42_save] = 0;
-    (*(tint_symbol_82)).numbers.arr[x_42_save] = x_969;
+    int const x_969 = (*(tint_symbol_82)).numbers[x_42_save];
+    (*(tint_symbol_82)).numbers[x_42_save] = 0;
+    (*(tint_symbol_82)).numbers[x_42_save] = x_969;
     int const x_61_save = x_60;
     int const x_970 = *(h);
     *(h) = 0;
@@ -204,9 +213,9 @@
     int const x_971 = param_1;
     param_1 = 0;
     param_1 = x_971;
-    int const x_62 = (*(tint_symbol_82)).numbers.arr[x_61_save];
+    int const x_62 = (*(tint_symbol_82)).numbers[x_61_save];
     QuicksortObject const x_972 = *(tint_symbol_82);
-    tint_array_wrapper const tint_symbol_12 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+    tint_array<int, 10> const tint_symbol_12 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     QuicksortObject const tint_symbol_13 = {.numbers=tint_symbol_12};
     *(tint_symbol_82) = tint_symbol_13;
     *(tint_symbol_82) = x_972;
@@ -267,7 +276,7 @@
       param_1 = x_985;
     }
     QuicksortObject const x_986 = *(tint_symbol_82);
-    tint_array_wrapper const tint_symbol_14 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+    tint_array<int, 10> const tint_symbol_14 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     QuicksortObject const tint_symbol_15 = {.numbers=tint_symbol_14};
     *(tint_symbol_82) = tint_symbol_15;
     *(tint_symbol_82) = x_986;
@@ -280,9 +289,9 @@
       *(h) = 0;
       *(h) = x_988;
       float3 const x_547 = float3(x_539[0], x_541[2], x_541[2]);
-      int const x_989 = (*(tint_symbol_82)).numbers.arr[x_61_save];
-      (*(tint_symbol_82)).numbers.arr[x_61_save] = 0;
-      (*(tint_symbol_82)).numbers.arr[x_61_save] = x_989;
+      int const x_989 = (*(tint_symbol_82)).numbers[x_61_save];
+      (*(tint_symbol_82)).numbers[x_61_save] = 0;
+      (*(tint_symbol_82)).numbers[x_61_save] = x_989;
       int const x_990 = param;
       param = 0;
       param = x_990;
@@ -291,18 +300,18 @@
       param_1 = 0;
       param_1 = x_991;
       float3 const x_548 = float3(x_541[1], x_541[2], x_541[0]);
-      int const x_992 = (*(tint_symbol_82)).numbers.arr[x_61_save];
-      (*(tint_symbol_82)).numbers.arr[x_61_save] = 0;
-      (*(tint_symbol_82)).numbers.arr[x_61_save] = x_992;
+      int const x_992 = (*(tint_symbol_82)).numbers[x_61_save];
+      (*(tint_symbol_82)).numbers[x_61_save] = 0;
+      (*(tint_symbol_82)).numbers[x_61_save] = x_992;
     }
   }
   int const x_76 = i_1;
-  int const x_993 = (*(tint_symbol_82)).numbers.arr[x_42_save];
-  (*(tint_symbol_82)).numbers.arr[x_42_save] = 0;
-  (*(tint_symbol_82)).numbers.arr[x_42_save] = x_993;
+  int const x_993 = (*(tint_symbol_82)).numbers[x_42_save];
+  (*(tint_symbol_82)).numbers[x_42_save] = 0;
+  (*(tint_symbol_82)).numbers[x_42_save] = x_993;
   float2 const x_549 = float2(x_534[0], x_534[1]);
   QuicksortObject const x_994 = *(tint_symbol_82);
-  tint_array_wrapper const tint_symbol_16 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+  tint_array<int, 10> const tint_symbol_16 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   QuicksortObject const tint_symbol_17 = {.numbers=tint_symbol_16};
   *(tint_symbol_82) = tint_symbol_17;
   *(tint_symbol_82) = x_994;
@@ -364,15 +373,15 @@
   int p = 0;
   int l_1 = 0;
   int top = 0;
-  tint_array_wrapper stack = {};
+  tint_array<int, 10> stack = {};
   int param_5 = 0;
   l_1 = 0;
   int const x_1007 = param_5;
   param_5 = 0;
   param_5 = x_1007;
   h_1 = 9;
-  tint_array_wrapper const x_1008 = stack;
-  tint_array_wrapper const tint_symbol_18 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+  tint_array<int, 10> const x_1008 = stack;
+  tint_array<int, 10> const tint_symbol_18 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   stack = tint_symbol_18;
   stack = x_1008;
   float2 const x_556 = float2(float3(1.0f, 2.0f, 3.0f)[1], float3(1.0f, 2.0f, 3.0f)[1]);
@@ -406,21 +415,21 @@
   param_4 = x_1015;
   int const x_95 = l_1;
   QuicksortObject const x_1016 = *(tint_symbol_83);
-  tint_array_wrapper const tint_symbol_19 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+  tint_array<int, 10> const tint_symbol_19 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   QuicksortObject const tint_symbol_20 = {.numbers=tint_symbol_19};
   *(tint_symbol_83) = tint_symbol_20;
   *(tint_symbol_83) = x_1016;
   float3 const x_560 = float3(x_559[1], x_559[0], x_557[0]);
   int const x_96_save = x_94;
-  tint_array_wrapper const x_1017 = stack;
-  tint_array_wrapper const tint_symbol_21 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+  tint_array<int, 10> const x_1017 = stack;
+  tint_array<int, 10> const tint_symbol_21 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   stack = tint_symbol_21;
   stack = x_1017;
   float3 const x_561 = float3(x_556[1], x_556[1], x_556[1]);
   int const x_1018 = l_1;
   l_1 = 0;
   l_1 = 0;
-  stack.arr[x_96_save] = x_95;
+  stack[x_96_save] = x_95;
   int const x_1019 = param_5;
   param_5 = 0;
   param_5 = x_1019;
@@ -429,13 +438,13 @@
   param_4 = 0;
   param_4 = x_1020;
   float3 const x_562 = float3(float3(1.0f, 2.0f, 3.0f)[2], x_558[1], float3(1.0f, 2.0f, 3.0f)[1]);
-  int const x_1021 = stack.arr[x_96_save];
-  stack.arr[x_96_save] = 0;
-  stack.arr[x_96_save] = x_1021;
+  int const x_1021 = stack[x_96_save];
+  stack[x_96_save] = 0;
+  stack[x_96_save] = x_1021;
   int const x_98 = as_type<int>((as_type<uint>(x_97) + as_type<uint>(1)));
-  int const x_1022 = stack.arr[x_96_save];
-  stack.arr[x_96_save] = 0;
-  stack.arr[x_96_save] = x_1022;
+  int const x_1022 = stack[x_96_save];
+  stack[x_96_save] = 0;
+  stack[x_96_save] = x_1022;
   float3 const x_563 = float3(x_559[0], x_559[2], x_556[1]);
   top = x_98;
   int const x_1023 = param_4;
@@ -457,19 +466,19 @@
   int const x_1027 = p;
   p = 0;
   p = x_1027;
-  stack.arr[x_100_save] = x_99;
+  stack[x_100_save] = x_99;
   while (true) {
     float3 const x_566 = float3(x_563[0], x_563[0], x_563[0]);
     int const x_1028 = h_1;
     h_1 = 0;
     h_1 = x_1028;
-    tint_array_wrapper const x_1029 = stack;
-    tint_array_wrapper const tint_symbol_22 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+    tint_array<int, 10> const x_1029 = stack;
+    tint_array<int, 10> const tint_symbol_22 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     stack = tint_symbol_22;
     stack = x_1029;
     int const x_106 = top;
-    tint_array_wrapper const x_1030 = stack;
-    tint_array_wrapper const tint_symbol_23 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+    tint_array<int, 10> const x_1030 = stack;
+    tint_array<int, 10> const tint_symbol_23 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     stack = tint_symbol_23;
     stack = x_1030;
     float2 const x_567 = float2(x_558[0], x_564[2]);
@@ -481,7 +490,7 @@
       break;
     }
     QuicksortObject const x_1032 = *(tint_symbol_83);
-    tint_array_wrapper const tint_symbol_24 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+    tint_array<int, 10> const tint_symbol_24 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     QuicksortObject const tint_symbol_25 = {.numbers=tint_symbol_24};
     *(tint_symbol_83) = tint_symbol_25;
     *(tint_symbol_83) = x_1032;
@@ -503,12 +512,12 @@
     p = 0;
     p = x_1036;
     int const x_110_save = x_108;
-    int const x_1037 = stack.arr[x_96_save];
-    stack.arr[x_96_save] = 0;
-    stack.arr[x_96_save] = x_1037;
-    int const x_111 = stack.arr[x_110_save];
-    tint_array_wrapper const x_1038 = stack;
-    tint_array_wrapper const tint_symbol_26 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+    int const x_1037 = stack[x_96_save];
+    stack[x_96_save] = 0;
+    stack[x_96_save] = x_1037;
+    int const x_111 = stack[x_110_save];
+    tint_array<int, 10> const x_1038 = stack;
+    tint_array<int, 10> const tint_symbol_26 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     stack = tint_symbol_26;
     stack = x_1038;
     float3 const x_571 = float3(x_559[1], x_559[0], x_564[1]);
@@ -516,8 +525,8 @@
     l_1 = 0;
     l_1 = x_1039;
     h_1 = x_111;
-    tint_array_wrapper const x_1040 = stack;
-    tint_array_wrapper const tint_symbol_27 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+    tint_array<int, 10> const x_1040 = stack;
+    tint_array<int, 10> const tint_symbol_27 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     stack = tint_symbol_27;
     stack = x_1040;
     float2 const x_572 = float2(x_562[1], x_561[1]);
@@ -528,9 +537,9 @@
     int const x_1042 = param_4;
     param_4 = 0;
     param_4 = x_1042;
-    int const x_1043 = stack.arr[x_100_save];
-    stack.arr[x_100_save] = 0;
-    stack.arr[x_100_save] = x_1043;
+    int const x_1043 = stack[x_100_save];
+    stack[x_100_save] = 0;
+    stack[x_100_save] = x_1043;
     float2 const x_573 = float2(float3(1.0f, 2.0f, 3.0f)[1], float3(1.0f, 2.0f, 3.0f)[2]);
     top = as_type<int>((as_type<uint>(x_112) - as_type<uint>(1)));
     int const x_1044 = param_5;
@@ -542,10 +551,10 @@
     h_1 = x_1045;
     int const x_114_save = x_112;
     float2 const x_575 = float2(x_564[1], x_564[2]);
-    int const x_1046 = stack.arr[x_100_save];
-    stack.arr[x_100_save] = 0;
-    stack.arr[x_100_save] = x_1046;
-    int const x_115 = stack.arr[x_114_save];
+    int const x_1046 = stack[x_100_save];
+    stack[x_100_save] = 0;
+    stack[x_100_save] = x_1046;
+    int const x_115 = stack[x_114_save];
     int const x_1047 = p;
     p = 0;
     p = x_1047;
@@ -559,16 +568,16 @@
     top = x_1049;
     int const x_118 = l_1;
     param_4 = x_118;
-    int const x_1050 = stack.arr[x_110_save];
-    stack.arr[x_110_save] = 0;
-    stack.arr[x_110_save] = x_1050;
+    int const x_1050 = stack[x_110_save];
+    stack[x_110_save] = 0;
+    stack[x_110_save] = x_1050;
     float2 const x_577 = float2(x_569[1], x_569[2]);
     int const x_120 = h_1;
     float2 const x_578 = float2(x_558[0], float3(1.0f, 2.0f, 3.0f)[1]);
     param_5 = x_120;
-    int const x_1051 = stack.arr[x_100_save];
-    stack.arr[x_100_save] = 0;
-    stack.arr[x_100_save] = x_1051;
+    int const x_1051 = stack[x_100_save];
+    stack[x_100_save] = 0;
+    stack[x_100_save] = x_1051;
     int const x_121 = performPartition_i1_i1_(&(param_4), &(param_5), tint_symbol_83);
     float2 const x_579 = float2(x_567[0], x_568[0]);
     int const x_1052 = param_5;
@@ -590,140 +599,140 @@
     h_1 = 0;
     h_1 = x_1056;
     int const x_124 = l_1;
-    int const x_1057 = stack.arr[x_110_save];
-    stack.arr[x_110_save] = 0;
-    stack.arr[x_110_save] = x_1057;
+    int const x_1057 = stack[x_110_save];
+    stack[x_110_save] = 0;
+    stack[x_110_save] = x_1057;
     int const x_1058 = h_1;
     h_1 = 0;
     h_1 = x_1058;
     float2 const x_582 = float2(x_567[1], x_573[0]);
-    int const x_1059 = stack.arr[x_100_save];
-    stack.arr[x_100_save] = 0;
-    stack.arr[x_100_save] = x_1059;
+    int const x_1059 = stack[x_100_save];
+    stack[x_100_save] = 0;
+    stack[x_100_save] = x_1059;
     if ((as_type<int>((as_type<uint>(x_122) - as_type<uint>(as_type<int>(1u)))) > x_124)) {
       int const x_1060 = param_4;
       param_4 = 0;
       param_4 = x_1060;
       int const x_128 = top;
       float2 const x_583 = float2(x_571[1], x_556[1]);
-      int const x_1061 = stack.arr[x_100_save];
-      stack.arr[x_100_save] = 0;
-      stack.arr[x_100_save] = x_1061;
-      tint_array_wrapper const x_1062 = stack;
-      tint_array_wrapper const tint_symbol_28 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+      int const x_1061 = stack[x_100_save];
+      stack[x_100_save] = 0;
+      stack[x_100_save] = x_1061;
+      tint_array<int, 10> const x_1062 = stack;
+      tint_array<int, 10> const tint_symbol_28 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
       stack = tint_symbol_28;
       stack = x_1062;
       float2 const x_584 = float2(x_569[2], x_569[1]);
       float3 const x_585 = float3(x_580[1], x_577[0], x_577[0]);
       int const x_130 = l_1;
-      int const x_1063 = stack.arr[x_114_save];
-      stack.arr[x_114_save] = 0;
-      stack.arr[x_114_save] = x_1063;
+      int const x_1063 = stack[x_114_save];
+      stack[x_114_save] = 0;
+      stack[x_114_save] = x_1063;
       float2 const x_586 = float2(x_564[0], x_585[0]);
       int const x_1064 = param_5;
       param_5 = 0;
       param_5 = x_1064;
       int const x_131_save = as_type<int>((as_type<uint>(1) + as_type<uint>(x_128)));
-      int const x_1065 = stack.arr[x_110_save];
-      stack.arr[x_110_save] = 0;
-      stack.arr[x_110_save] = x_1065;
+      int const x_1065 = stack[x_110_save];
+      stack[x_110_save] = 0;
+      stack[x_110_save] = x_1065;
       float3 const x_587 = float3(x_566[1], x_566[1], x_563[0]);
       int const x_1066 = param_5;
       param_5 = 0;
       param_5 = x_1066;
-      stack.arr[x_131_save] = x_130;
+      stack[x_131_save] = x_130;
       int const x_132 = top;
-      int const x_1067 = stack.arr[x_100_save];
-      stack.arr[x_100_save] = 0;
-      stack.arr[x_100_save] = x_1067;
+      int const x_1067 = stack[x_100_save];
+      stack[x_100_save] = 0;
+      stack[x_100_save] = x_1067;
       float2 const x_588 = float2(x_575[1], x_575[0]);
-      int const x_1068 = stack.arr[x_131_save];
-      stack.arr[x_131_save] = 0;
-      stack.arr[x_131_save] = x_1068;
+      int const x_1068 = stack[x_131_save];
+      stack[x_131_save] = 0;
+      stack[x_131_save] = x_1068;
       int const x_133 = as_type<int>((1u + as_type<uint>(x_132)));
-      int const x_1069 = stack.arr[x_100_save];
-      stack.arr[x_100_save] = 0;
-      stack.arr[x_100_save] = x_1069;
+      int const x_1069 = stack[x_100_save];
+      stack[x_100_save] = 0;
+      stack[x_100_save] = x_1069;
       float3 const x_589 = float3(x_576[2], x_588[1], x_576[2]);
       int const x_1070 = h_1;
       h_1 = 0;
       h_1 = x_1070;
       top = x_133;
-      tint_array_wrapper const x_1071 = stack;
-      tint_array_wrapper const tint_symbol_29 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+      tint_array<int, 10> const x_1071 = stack;
+      tint_array<int, 10> const tint_symbol_29 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
       stack = tint_symbol_29;
       stack = x_1071;
       int const x_134 = p;
       float2 const x_590 = float2(x_576[0], x_573[1]);
-      int const x_1072 = stack.arr[x_114_save];
-      stack.arr[x_114_save] = 0;
-      stack.arr[x_114_save] = x_1072;
+      int const x_1072 = stack[x_114_save];
+      stack[x_114_save] = 0;
+      stack[x_114_save] = x_1072;
       int const x_136_save = x_133;
-      int const x_1073 = stack.arr[x_114_save];
-      stack.arr[x_114_save] = 0;
-      stack.arr[x_114_save] = x_1073;
-      stack.arr[x_136_save] = as_type<int>((as_type<uint>(x_134) - as_type<uint>(as_type<int>(1u))));
-      int const x_1074 = stack.arr[x_96_save];
-      stack.arr[x_96_save] = 0;
-      stack.arr[x_96_save] = x_1074;
+      int const x_1073 = stack[x_114_save];
+      stack[x_114_save] = 0;
+      stack[x_114_save] = x_1073;
+      stack[x_136_save] = as_type<int>((as_type<uint>(x_134) - as_type<uint>(as_type<int>(1u))));
+      int const x_1074 = stack[x_96_save];
+      stack[x_96_save] = 0;
+      stack[x_96_save] = x_1074;
       float2 const x_591 = float2(x_569[2], x_569[1]);
-      int const x_1075 = stack.arr[x_136_save];
-      stack.arr[x_136_save] = 0;
-      stack.arr[x_136_save] = x_1075;
+      int const x_1075 = stack[x_136_save];
+      stack[x_136_save] = 0;
+      stack[x_136_save] = x_1075;
     }
-    int const x_1076 = stack.arr[x_96_save];
-    stack.arr[x_96_save] = 0;
-    stack.arr[x_96_save] = x_1076;
+    int const x_1076 = stack[x_96_save];
+    stack[x_96_save] = 0;
+    stack[x_96_save] = x_1076;
     float2 const x_592 = float2(float3(1.0f, 2.0f, 3.0f)[0], float3(1.0f, 2.0f, 3.0f)[1]);
     QuicksortObject const x_1077 = *(tint_symbol_83);
-    tint_array_wrapper const tint_symbol_30 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+    tint_array<int, 10> const tint_symbol_30 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     QuicksortObject const tint_symbol_31 = {.numbers=tint_symbol_30};
     *(tint_symbol_83) = tint_symbol_31;
     *(tint_symbol_83) = x_1077;
     int const x_137 = p;
-    int const x_1078 = stack.arr[x_114_save];
-    stack.arr[x_114_save] = 0;
-    stack.arr[x_114_save] = x_1078;
+    int const x_1078 = stack[x_114_save];
+    stack[x_114_save] = 0;
+    stack[x_114_save] = x_1078;
     float3 const x_593 = float3(x_571[2], x_556[0], x_556[1]);
     int const x_1079 = p;
     p = 0;
     p = x_1079;
     float3 const x_594 = float3(x_563[2], x_563[0], x_575[0]);
-    int const x_1080 = stack.arr[x_114_save];
-    stack.arr[x_114_save] = 0;
-    stack.arr[x_114_save] = x_1080;
+    int const x_1080 = stack[x_114_save];
+    stack[x_114_save] = 0;
+    stack[x_114_save] = x_1080;
     int const x_139 = h_1;
     int const x_1081 = top;
     top = 0;
     top = x_1081;
     float3 const x_595 = float3(x_560[2], x_568[0], x_560[0]);
-    int const x_1082 = stack.arr[x_100_save];
-    stack.arr[x_100_save] = 0;
-    stack.arr[x_100_save] = x_1082;
+    int const x_1082 = stack[x_100_save];
+    stack[x_100_save] = 0;
+    stack[x_100_save] = x_1082;
     int const x_1083 = p;
     p = 0;
     p = x_1083;
     if ((as_type<int>((1u + as_type<uint>(x_137))) < x_139)) {
-      int const x_1084 = stack.arr[x_114_save];
-      stack.arr[x_114_save] = 0;
-      stack.arr[x_114_save] = x_1084;
+      int const x_1084 = stack[x_114_save];
+      stack[x_114_save] = 0;
+      stack[x_114_save] = x_1084;
       float2 const x_596 = float2(x_592[1], x_582[0]);
       int const x_1085 = l_1;
       l_1 = 0;
       l_1 = x_1085;
       int const x_143 = top;
-      int const x_1086 = stack.arr[x_114_save];
-      stack.arr[x_114_save] = 0;
-      stack.arr[x_114_save] = x_1086;
+      int const x_1086 = stack[x_114_save];
+      stack[x_114_save] = 0;
+      stack[x_114_save] = x_1086;
       float3 const x_597 = float3(x_562[1], x_560[1], x_560[1]);
       int const x_144 = as_type<int>((as_type<uint>(x_143) + as_type<uint>(1)));
       int const x_1087 = param_5;
       param_5 = 0;
       param_5 = x_1087;
       top = x_144;
-      int const x_1088 = stack.arr[x_114_save];
-      stack.arr[x_114_save] = 0;
-      stack.arr[x_114_save] = x_1088;
+      int const x_1088 = stack[x_114_save];
+      stack[x_114_save] = 0;
+      stack[x_114_save] = x_1088;
       int const x_145 = p;
       int const x_1089 = param_5;
       param_5 = 0;
@@ -733,62 +742,62 @@
       p = 0;
       p = x_1090;
       float3 const x_600 = float3(x_556[0], x_580[0], x_580[0]);
-      int const x_1091 = stack.arr[x_100_save];
-      stack.arr[x_100_save] = 0;
-      stack.arr[x_100_save] = x_1091;
+      int const x_1091 = stack[x_100_save];
+      stack[x_100_save] = 0;
+      stack[x_100_save] = x_1091;
       int const x_147_save = x_144;
-      int const x_1092 = stack.arr[x_110_save];
-      stack.arr[x_110_save] = 0;
-      stack.arr[x_110_save] = x_1092;
+      int const x_1092 = stack[x_110_save];
+      stack[x_110_save] = 0;
+      stack[x_110_save] = x_1092;
       float2 const x_601 = float2(x_563[0], x_563[1]);
-      stack.arr[x_147_save] = as_type<int>((1u + as_type<uint>(x_145)));
-      tint_array_wrapper const x_1093 = stack;
-      tint_array_wrapper const tint_symbol_32 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+      stack[x_147_save] = as_type<int>((1u + as_type<uint>(x_145)));
+      tint_array<int, 10> const x_1093 = stack;
+      tint_array<int, 10> const tint_symbol_32 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
       stack = tint_symbol_32;
       stack = x_1093;
       int const x_148 = top;
-      int const x_1094 = stack.arr[x_114_save];
-      stack.arr[x_114_save] = 0;
-      stack.arr[x_114_save] = x_1094;
+      int const x_1094 = stack[x_114_save];
+      stack[x_114_save] = 0;
+      stack[x_114_save] = x_1094;
       float2 const x_602 = float2(x_565[1], x_599[1]);
-      tint_array_wrapper const x_1095 = stack;
-      tint_array_wrapper const tint_symbol_33 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+      tint_array<int, 10> const x_1095 = stack;
+      tint_array<int, 10> const tint_symbol_33 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
       stack = tint_symbol_33;
       stack = x_1095;
       int const x_149 = as_type<int>((as_type<uint>(x_148) + as_type<uint>(as_type<int>(1u))));
-      int const x_1096 = stack.arr[x_147_save];
-      stack.arr[x_147_save] = 0;
-      stack.arr[x_147_save] = x_1096;
+      int const x_1096 = stack[x_147_save];
+      stack[x_147_save] = 0;
+      stack[x_147_save] = x_1096;
       top = x_149;
       int const x_1097 = param_4;
       param_4 = 0;
       param_4 = x_1097;
       int const x_150 = h_1;
-      int const x_1098 = stack.arr[x_100_save];
-      stack.arr[x_100_save] = 0;
-      stack.arr[x_100_save] = x_1098;
-      int const x_1099 = stack.arr[x_96_save];
-      stack.arr[x_96_save] = 0;
-      stack.arr[x_96_save] = x_1099;
-      stack.arr[x_149] = x_150;
-      int const x_1100 = stack.arr[x_114_save];
-      stack.arr[x_114_save] = 0;
-      stack.arr[x_114_save] = x_1100;
+      int const x_1098 = stack[x_100_save];
+      stack[x_100_save] = 0;
+      stack[x_100_save] = x_1098;
+      int const x_1099 = stack[x_96_save];
+      stack[x_96_save] = 0;
+      stack[x_96_save] = x_1099;
+      stack[x_149] = x_150;
+      int const x_1100 = stack[x_114_save];
+      stack[x_114_save] = 0;
+      stack[x_114_save] = x_1100;
       float3 const x_603 = float3(x_568[1], x_564[0], x_564[0]);
       int const x_1101 = l_1;
       l_1 = 0;
       l_1 = x_1101;
     }
-    int const x_1102 = stack.arr[x_100_save];
-    stack.arr[x_100_save] = 0;
-    stack.arr[x_100_save] = x_1102;
+    int const x_1102 = stack[x_100_save];
+    stack[x_100_save] = 0;
+    stack[x_100_save] = x_1102;
     {
       int const x_1103 = l_1;
       l_1 = 0;
       l_1 = x_1103;
       float2 const x_604 = float2(x_563[2], x_564[0]);
       QuicksortObject const x_1104 = *(tint_symbol_83);
-      tint_array_wrapper const tint_symbol_34 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+      tint_array<int, 10> const tint_symbol_34 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
       QuicksortObject const tint_symbol_35 = {.numbers=tint_symbol_34};
       *(tint_symbol_83) = tint_symbol_35;
       *(tint_symbol_83) = x_1104;
@@ -809,13 +818,13 @@
   uv = x_717;
   i_2 = 0;
   QuicksortObject const x_721 = *(tint_symbol_84);
-  tint_array_wrapper const tint_symbol_36 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+  tint_array<int, 10> const tint_symbol_36 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   QuicksortObject const tint_symbol_37 = {.numbers=tint_symbol_36};
   *(tint_symbol_84) = tint_symbol_37;
   *(tint_symbol_84) = x_721;
   if (true) {
     QuicksortObject const x_722 = *(tint_symbol_84);
-    tint_array_wrapper const tint_symbol_38 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+    tint_array<int, 10> const tint_symbol_38 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     QuicksortObject const tint_symbol_39 = {.numbers=tint_symbol_38};
     *(tint_symbol_84) = tint_symbol_39;
     *(tint_symbol_84) = x_722;
@@ -829,13 +838,13 @@
     color = x_725;
     float2 const x_432 = float2(x_431[1], x_431[1]);
     QuicksortObject const x_726 = *(tint_symbol_84);
-    tint_array_wrapper const tint_symbol_40 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+    tint_array<int, 10> const tint_symbol_40 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     QuicksortObject const tint_symbol_41 = {.numbers=tint_symbol_40};
     *(tint_symbol_84) = tint_symbol_41;
     *(tint_symbol_84) = x_726;
   }
   QuicksortObject const x_756 = *(tint_symbol_84);
-  tint_array_wrapper const tint_symbol_42 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+  tint_array<int, 10> const tint_symbol_42 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   QuicksortObject const tint_symbol_43 = {.numbers=tint_symbol_42};
   *(tint_symbol_84) = tint_symbol_43;
   *(tint_symbol_84) = x_756;
@@ -845,7 +854,7 @@
   i_2 = x_757;
   quicksort_(tint_symbol_84);
   QuicksortObject const x_758 = *(tint_symbol_84);
-  tint_array_wrapper const tint_symbol_44 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+  tint_array<int, 10> const tint_symbol_44 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   QuicksortObject const tint_symbol_45 = {.numbers=tint_symbol_44};
   *(tint_symbol_84) = tint_symbol_45;
   *(tint_symbol_84) = x_758;
@@ -860,7 +869,7 @@
   float2 const x_185 = float2(x_184[0], x_184[1]);
   float3 const x_448 = float3(x_185[1], x_446[1], x_446[1]);
   QuicksortObject const x_761 = *(tint_symbol_84);
-  tint_array_wrapper const tint_symbol_46 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+  tint_array<int, 10> const tint_symbol_46 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   QuicksortObject const tint_symbol_47 = {.numbers=tint_symbol_46};
   *(tint_symbol_84) = tint_symbol_47;
   *(tint_symbol_84) = x_761;
@@ -869,7 +878,7 @@
   uv = x_762;
   float2 const x_191 = (*(tint_symbol_86)).resolution;
   QuicksortObject const x_763 = *(tint_symbol_84);
-  tint_array_wrapper const tint_symbol_48 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+  tint_array<int, 10> const tint_symbol_48 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   QuicksortObject const tint_symbol_49 = {.numbers=tint_symbol_48};
   *(tint_symbol_84) = tint_symbol_49;
   *(tint_symbol_84) = x_763;
@@ -879,7 +888,7 @@
   color = x_764;
   float2 const x_192 = (x_185 / x_191);
   QuicksortObject const x_765 = *(tint_symbol_84);
-  tint_array_wrapper const tint_symbol_50 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+  tint_array<int, 10> const tint_symbol_50 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   QuicksortObject const tint_symbol_51 = {.numbers=tint_symbol_50};
   *(tint_symbol_84) = tint_symbol_51;
   *(tint_symbol_84) = x_765;
@@ -897,22 +906,22 @@
   color = x_768;
   float3 const x_451 = float3(x_185[0], x_185[1], x_446[1]);
   QuicksortObject const x_769 = *(tint_symbol_84);
-  tint_array_wrapper const tint_symbol_52 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+  tint_array<int, 10> const tint_symbol_52 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   QuicksortObject const tint_symbol_53 = {.numbers=tint_symbol_52};
   *(tint_symbol_84) = tint_symbol_53;
   *(tint_symbol_84) = x_769;
-  int const x_770 = (*(tint_symbol_84)).numbers.arr[0u];
-  (*(tint_symbol_84)).numbers.arr[0u] = 0;
-  (*(tint_symbol_84)).numbers.arr[0u] = x_770;
-  int const x_201 = (*(tint_symbol_84)).numbers.arr[0u];
+  int const x_770 = (*(tint_symbol_84)).numbers[0u];
+  (*(tint_symbol_84)).numbers[0u] = 0;
+  (*(tint_symbol_84)).numbers[0u] = x_770;
+  int const x_201 = (*(tint_symbol_84)).numbers[0u];
   QuicksortObject const x_771 = *(tint_symbol_84);
-  tint_array_wrapper const tint_symbol_54 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+  tint_array<int, 10> const tint_symbol_54 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   QuicksortObject const tint_symbol_55 = {.numbers=tint_symbol_54};
   *(tint_symbol_84) = tint_symbol_55;
   *(tint_symbol_84) = x_771;
-  int const x_772 = (*(tint_symbol_84)).numbers.arr[0u];
-  (*(tint_symbol_84)).numbers.arr[0u] = 0;
-  (*(tint_symbol_84)).numbers.arr[0u] = x_772;
+  int const x_772 = (*(tint_symbol_84)).numbers[0u];
+  (*(tint_symbol_84)).numbers[0u] = 0;
+  (*(tint_symbol_84)).numbers[0u] = x_772;
   float const x_206 = color[0];
   float const x_773 = color[0];
   color[0] = 0.0f;
@@ -922,7 +931,7 @@
   i_2 = 0;
   i_2 = x_774;
   QuicksortObject const x_775 = *(tint_symbol_84);
-  tint_array_wrapper const tint_symbol_56 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+  tint_array<int, 10> const tint_symbol_56 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   QuicksortObject const tint_symbol_57 = {.numbers=tint_symbol_56};
   *(tint_symbol_84) = tint_symbol_57;
   *(tint_symbol_84) = x_775;
@@ -941,7 +950,7 @@
   uv[0] = 0.0f;
   uv[0] = x_778;
   QuicksortObject const x_779 = *(tint_symbol_84);
-  tint_array_wrapper const tint_symbol_58 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+  tint_array<int, 10> const tint_symbol_58 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   QuicksortObject const tint_symbol_59 = {.numbers=tint_symbol_58};
   *(tint_symbol_84) = tint_symbol_59;
   *(tint_symbol_84) = x_779;
@@ -949,16 +958,16 @@
     int const x_780 = i_2;
     i_2 = 0;
     i_2 = x_780;
-    int const x_781 = (*(tint_symbol_84)).numbers.arr[0u];
-    (*(tint_symbol_84)).numbers.arr[0u] = 0;
-    (*(tint_symbol_84)).numbers.arr[0u] = x_781;
+    int const x_781 = (*(tint_symbol_84)).numbers[0u];
+    (*(tint_symbol_84)).numbers[0u] = 0;
+    (*(tint_symbol_84)).numbers[0u] = x_781;
     float3 const x_456 = float3(float2(0.0f)[1], x_448[1], x_448[1]);
     float const x_782 = uv[0];
     uv[0] = 0.0f;
     uv[0] = x_782;
-    int const x_216 = (*(tint_symbol_84)).numbers.arr[1];
+    int const x_216 = (*(tint_symbol_84)).numbers[1];
     QuicksortObject const x_783 = *(tint_symbol_84);
-    tint_array_wrapper const tint_symbol_60 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+    tint_array<int, 10> const tint_symbol_60 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     QuicksortObject const tint_symbol_61 = {.numbers=tint_symbol_60};
     *(tint_symbol_84) = tint_symbol_61;
     *(tint_symbol_84) = x_783;
@@ -967,7 +976,7 @@
     uv = float2(0.0f);
     uv = x_784;
     QuicksortObject const x_785 = *(tint_symbol_84);
-    tint_array_wrapper const tint_symbol_62 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+    tint_array<int, 10> const tint_symbol_62 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     QuicksortObject const tint_symbol_63 = {.numbers=tint_symbol_62};
     *(tint_symbol_84) = tint_symbol_63;
     *(tint_symbol_84) = x_785;
@@ -990,9 +999,9 @@
     color[0] = 0.0f;
     color[0] = x_790;
     color[0] = (float(x_216) + x_219);
-    int const x_791 = (*(tint_symbol_84)).numbers.arr[0u];
-    (*(tint_symbol_84)).numbers.arr[0u] = 0;
-    (*(tint_symbol_84)).numbers.arr[0u] = x_791;
+    int const x_791 = (*(tint_symbol_84)).numbers[0u];
+    (*(tint_symbol_84)).numbers[0u] = 0;
+    (*(tint_symbol_84)).numbers[0u] = x_791;
   }
   float const x_792 = uv[0];
   uv[0] = 0.0f;
@@ -1030,24 +1039,24 @@
     float const x_801 = color[0];
     color[0] = 0.0f;
     color[0] = x_801;
-    int const x_230 = (*(tint_symbol_84)).numbers.arr[2u];
+    int const x_230 = (*(tint_symbol_84)).numbers[2u];
     float const x_802 = uv[0];
     uv[0] = 0.0f;
     uv[0] = x_802;
     float const x_803 = color[0];
     color[0] = 0.0f;
     color[0] = x_803;
-    int const x_804 = (*(tint_symbol_84)).numbers.arr[2u];
-    (*(tint_symbol_84)).numbers.arr[2u] = 0;
-    (*(tint_symbol_84)).numbers.arr[2u] = x_804;
+    int const x_804 = (*(tint_symbol_84)).numbers[2u];
+    (*(tint_symbol_84)).numbers[2u] = 0;
+    (*(tint_symbol_84)).numbers[2u] = x_804;
     float2 const x_464 = float2(x_450[1], x_191[0]);
     float const x_805 = color[1];
     color[1] = 0.0f;
     color[1] = x_805;
     float const x_234 = color[1];
-    int const x_806 = (*(tint_symbol_84)).numbers.arr[2u];
-    (*(tint_symbol_84)).numbers.arr[2u] = 0;
-    (*(tint_symbol_84)).numbers.arr[2u] = x_806;
+    int const x_806 = (*(tint_symbol_84)).numbers[2u];
+    (*(tint_symbol_84)).numbers[2u] = 0;
+    (*(tint_symbol_84)).numbers[2u] = x_806;
     float2 const x_465 = float2(x_463[0], x_185[0]);
     float const x_807 = color[0];
     color[0] = 0.0f;
@@ -1082,12 +1091,12 @@
     float const x_815 = color[0];
     color[0] = 0.0f;
     color[0] = x_815;
-    int const x_245 = (*(tint_symbol_84)).numbers.arr[3];
+    int const x_245 = (*(tint_symbol_84)).numbers[3];
     float const x_816 = color[0];
     color[0] = 0.0f;
     color[0] = x_816;
     QuicksortObject const x_817 = *(tint_symbol_84);
-    tint_array_wrapper const tint_symbol_64 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+    tint_array<int, 10> const tint_symbol_64 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     QuicksortObject const tint_symbol_65 = {.numbers=tint_symbol_64};
     *(tint_symbol_84) = tint_symbol_65;
     *(tint_symbol_84) = x_817;
@@ -1106,9 +1115,9 @@
     float const x_821 = color[2];
     color[2] = 0.0f;
     color[2] = x_821;
-    int const x_822 = (*(tint_symbol_84)).numbers.arr[0u];
-    (*(tint_symbol_84)).numbers.arr[0u] = 0;
-    (*(tint_symbol_84)).numbers.arr[0u] = x_822;
+    int const x_822 = (*(tint_symbol_84)).numbers[0u];
+    (*(tint_symbol_84)).numbers[0u] = 0;
+    (*(tint_symbol_84)).numbers[0u] = x_822;
     float2 const x_470 = float2(float2(0.0f)[0], float2(0.0f)[1]);
     float const x_823 = color[2];
     color[2] = 0.0f;
@@ -1123,7 +1132,7 @@
   uv[0] = 0.0f;
   uv[0] = x_825;
   float3 const x_472 = float3(x_454[0], x_454[1], x_454[1]);
-  int const x_254 = (*(tint_symbol_84)).numbers.arr[4];
+  int const x_254 = (*(tint_symbol_84)).numbers[4];
   float const x_826 = uv[0];
   uv[0] = 0.0f;
   uv[0] = x_826;
@@ -1131,9 +1140,9 @@
   color = float3(0.0f);
   color = x_827;
   float3 const x_473 = float3(x_446[1], x_453[0], x_453[0]);
-  int const x_828 = (*(tint_symbol_84)).numbers.arr[4];
-  (*(tint_symbol_84)).numbers.arr[4] = 0;
-  (*(tint_symbol_84)).numbers.arr[4] = x_828;
+  int const x_828 = (*(tint_symbol_84)).numbers[4];
+  (*(tint_symbol_84)).numbers[4] = 0;
+  (*(tint_symbol_84)).numbers[4] = x_828;
   float2 const x_474 = float2(x_191[0], x_184[2]);
   float const x_829 = uv[0];
   uv[0] = 0.0f;
@@ -1167,9 +1176,9 @@
   i_2 = 0;
   i_2 = x_836;
   float3 const x_479 = float3(float2(0.0f)[1], x_454[1], float2(0.0f)[0]);
-  int const x_837 = (*(tint_symbol_84)).numbers.arr[0u];
-  (*(tint_symbol_84)).numbers.arr[0u] = 0;
-  (*(tint_symbol_84)).numbers.arr[0u] = x_837;
+  int const x_837 = (*(tint_symbol_84)).numbers[0u];
+  (*(tint_symbol_84)).numbers[0u] = 0;
+  (*(tint_symbol_84)).numbers[0u] = x_837;
   float const x_838 = color[1];
   color[1] = 0.0f;
   color[1] = x_838;
@@ -1182,7 +1191,7 @@
     float3 const x_840 = color;
     color = float3(0.0f);
     color = x_840;
-    int const x_267 = (*(tint_symbol_84)).numbers.arr[5u];
+    int const x_267 = (*(tint_symbol_84)).numbers[5u];
     float const x_841 = color[0];
     color[0] = 0.0f;
     color[0] = x_841;
@@ -1198,7 +1207,7 @@
     uv[0] = x_844;
     float3 const x_482 = float3(x_455[0], x_475[1], x_455[1]);
     QuicksortObject const x_845 = *(tint_symbol_84);
-    tint_array_wrapper const tint_symbol_66 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+    tint_array<int, 10> const tint_symbol_66 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     QuicksortObject const tint_symbol_67 = {.numbers=tint_symbol_66};
     *(tint_symbol_84) = tint_symbol_67;
     *(tint_symbol_84) = x_845;
@@ -1225,13 +1234,13 @@
   float const x_851 = uv[1];
   uv[1] = 0.0f;
   uv[1] = x_851;
-  int const x_852 = (*(tint_symbol_84)).numbers.arr[4];
-  (*(tint_symbol_84)).numbers.arr[4] = 0;
-  (*(tint_symbol_84)).numbers.arr[4] = x_852;
+  int const x_852 = (*(tint_symbol_84)).numbers[4];
+  (*(tint_symbol_84)).numbers[4] = 0;
+  (*(tint_symbol_84)).numbers[4] = x_852;
   float const x_274 = uv[1];
-  int const x_853 = (*(tint_symbol_84)).numbers.arr[0u];
-  (*(tint_symbol_84)).numbers.arr[0u] = 0;
-  (*(tint_symbol_84)).numbers.arr[0u] = x_853;
+  int const x_853 = (*(tint_symbol_84)).numbers[0u];
+  (*(tint_symbol_84)).numbers[0u] = 0;
+  (*(tint_symbol_84)).numbers[0u] = x_853;
   if ((x_274 > 0.5f)) {
     float const x_854 = uv[0];
     uv[0] = 0.0f;
@@ -1244,16 +1253,16 @@
     float const x_856 = uv[1];
     uv[1] = 0.0f;
     uv[1] = x_856;
-    int const x_280 = (*(tint_symbol_84)).numbers.arr[6u];
+    int const x_280 = (*(tint_symbol_84)).numbers[6u];
     float const x_857 = uv[1];
     uv[1] = 0.0f;
     uv[1] = x_857;
     int const x_858 = i_2;
     i_2 = 0;
     i_2 = x_858;
-    int const x_859 = (*(tint_symbol_84)).numbers.arr[4];
-    (*(tint_symbol_84)).numbers.arr[4] = 0;
-    (*(tint_symbol_84)).numbers.arr[4] = x_859;
+    int const x_859 = (*(tint_symbol_84)).numbers[4];
+    (*(tint_symbol_84)).numbers[4] = 0;
+    (*(tint_symbol_84)).numbers[4] = x_859;
     float2 const x_488 = float2(x_473[2], x_473[1]);
     float const x_283 = color[1];
     float2 const x_860 = uv;
@@ -1263,15 +1272,15 @@
     color[0] = 0.0f;
     color[0] = x_861;
     float2 const x_489 = float2(x_475[1], x_475[0]);
-    int const x_862 = (*(tint_symbol_84)).numbers.arr[6u];
-    (*(tint_symbol_84)).numbers.arr[6u] = 0;
-    (*(tint_symbol_84)).numbers.arr[6u] = x_862;
-    int const x_863 = (*(tint_symbol_84)).numbers.arr[6u];
-    (*(tint_symbol_84)).numbers.arr[6u] = 0;
-    (*(tint_symbol_84)).numbers.arr[6u] = x_863;
+    int const x_862 = (*(tint_symbol_84)).numbers[6u];
+    (*(tint_symbol_84)).numbers[6u] = 0;
+    (*(tint_symbol_84)).numbers[6u] = x_862;
+    int const x_863 = (*(tint_symbol_84)).numbers[6u];
+    (*(tint_symbol_84)).numbers[6u] = 0;
+    (*(tint_symbol_84)).numbers[6u] = x_863;
     float2 const x_490 = float2(x_480[2], x_480[2]);
     QuicksortObject const x_864 = *(tint_symbol_84);
-    tint_array_wrapper const tint_symbol_68 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+    tint_array<int, 10> const tint_symbol_68 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     QuicksortObject const tint_symbol_69 = {.numbers=tint_symbol_68};
     *(tint_symbol_84) = tint_symbol_69;
     *(tint_symbol_84) = x_864;
@@ -1290,7 +1299,7 @@
   color[0] = x_867;
   float const x_287 = uv[1];
   QuicksortObject const x_868 = *(tint_symbol_84);
-  tint_array_wrapper const tint_symbol_70 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+  tint_array<int, 10> const tint_symbol_70 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   QuicksortObject const tint_symbol_71 = {.numbers=tint_symbol_70};
   *(tint_symbol_84) = tint_symbol_71;
   *(tint_symbol_84) = x_868;
@@ -1302,9 +1311,9 @@
   color[1] = 0.0f;
   color[1] = x_870;
   float3 const x_494 = float3(x_191[0], x_191[1], x_191[1]);
-  int const x_871 = (*(tint_symbol_84)).numbers.arr[4];
-  (*(tint_symbol_84)).numbers.arr[4] = 0;
-  (*(tint_symbol_84)).numbers.arr[4] = x_871;
+  int const x_871 = (*(tint_symbol_84)).numbers[4];
+  (*(tint_symbol_84)).numbers[4] = 0;
+  (*(tint_symbol_84)).numbers[4] = x_871;
   if ((x_287 > 0.75f)) {
     float3 const x_872 = color;
     color = float3(0.0f);
@@ -1316,7 +1325,7 @@
     float3 const x_874 = color;
     color = float3(0.0f);
     color = x_874;
-    int const x_293 = (*(tint_symbol_84)).numbers.arr[7];
+    int const x_293 = (*(tint_symbol_84)).numbers[7];
     float const x_875 = uv[0];
     uv[0] = 0.0f;
     uv[0] = x_875;
@@ -1325,9 +1334,9 @@
     color[1] = 0.0f;
     color[1] = x_876;
     float2 const x_497 = float2(x_477[0], x_461[1]);
-    int const x_877 = (*(tint_symbol_84)).numbers.arr[0u];
-    (*(tint_symbol_84)).numbers.arr[0u] = 0;
-    (*(tint_symbol_84)).numbers.arr[0u] = x_877;
+    int const x_877 = (*(tint_symbol_84)).numbers[0u];
+    (*(tint_symbol_84)).numbers[0u] = 0;
+    (*(tint_symbol_84)).numbers[0u] = x_877;
     float const x_878 = color[1];
     color[1] = 0.0f;
     color[1] = x_878;
@@ -1369,14 +1378,14 @@
   float2 const x_888 = uv;
   uv = float2(0.0f);
   uv = x_888;
-  int const x_301 = (*(tint_symbol_84)).numbers.arr[8];
+  int const x_301 = (*(tint_symbol_84)).numbers[8];
   int const x_889 = i_2;
   i_2 = 0;
   i_2 = x_889;
   float2 const x_503 = float2(x_185[0], x_451[2]);
-  int const x_890 = (*(tint_symbol_84)).numbers.arr[8];
-  (*(tint_symbol_84)).numbers.arr[8] = 0;
-  (*(tint_symbol_84)).numbers.arr[8] = x_890;
+  int const x_890 = (*(tint_symbol_84)).numbers[8];
+  (*(tint_symbol_84)).numbers[8] = 0;
+  (*(tint_symbol_84)).numbers[8] = x_890;
   float const x_891 = color[1];
   color[1] = 0.0f;
   color[1] = x_891;
@@ -1393,9 +1402,9 @@
   color[0] = 0.0f;
   color[0] = x_894;
   float2 const x_506 = float2(x_493[0], x_492[0]);
-  int const x_895 = (*(tint_symbol_84)).numbers.arr[4];
-  (*(tint_symbol_84)).numbers.arr[4] = 0;
-  (*(tint_symbol_84)).numbers.arr[4] = x_895;
+  int const x_895 = (*(tint_symbol_84)).numbers[4];
+  (*(tint_symbol_84)).numbers[4] = 0;
+  (*(tint_symbol_84)).numbers[4] = x_895;
   float const x_896 = uv[1];
   uv[1] = 0.0f;
   uv[1] = x_896;
@@ -1444,23 +1453,23 @@
   uv[1] = 0.0f;
   uv[1] = x_908;
   float3 const x_512 = float3(x_455[1], x_455[1], x_455[1]);
-  int const x_909 = (*(tint_symbol_84)).numbers.arr[4];
-  (*(tint_symbol_84)).numbers.arr[4] = 0;
-  (*(tint_symbol_84)).numbers.arr[4] = x_909;
+  int const x_909 = (*(tint_symbol_84)).numbers[4];
+  (*(tint_symbol_84)).numbers[4] = 0;
+  (*(tint_symbol_84)).numbers[4] = x_909;
   if ((fabs((x_308 - x_310)) < 0.25f)) {
     float const x_910 = uv[0];
     uv[0] = 0.0f;
     uv[0] = x_910;
     QuicksortObject const x_911 = *(tint_symbol_84);
-    tint_array_wrapper const tint_symbol_72 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+    tint_array<int, 10> const tint_symbol_72 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     QuicksortObject const tint_symbol_73 = {.numbers=tint_symbol_72};
     *(tint_symbol_84) = tint_symbol_73;
     *(tint_symbol_84) = x_911;
     float3 const x_513 = float3(x_505[2], x_505[0], x_448[0]);
-    int const x_912 = (*(tint_symbol_84)).numbers.arr[8];
-    (*(tint_symbol_84)).numbers.arr[8] = 0;
-    (*(tint_symbol_84)).numbers.arr[8] = x_912;
-    int const x_317 = (*(tint_symbol_84)).numbers.arr[9u];
+    int const x_912 = (*(tint_symbol_84)).numbers[8];
+    (*(tint_symbol_84)).numbers[8] = 0;
+    (*(tint_symbol_84)).numbers[8] = x_912;
+    int const x_317 = (*(tint_symbol_84)).numbers[9u];
     float3 const x_514 = float3(x_474[1], x_474[1], x_474[1]);
     float const x_913 = uv[1];
     uv[1] = 0.0f;
@@ -1506,12 +1515,12 @@
   uv[0] = 0.0f;
   uv[0] = x_923;
   QuicksortObject const x_924 = *(tint_symbol_84);
-  tint_array_wrapper const tint_symbol_74 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+  tint_array<int, 10> const tint_symbol_74 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   QuicksortObject const tint_symbol_75 = {.numbers=tint_symbol_74};
   *(tint_symbol_84) = tint_symbol_75;
   *(tint_symbol_84) = x_924;
   QuicksortObject const x_925 = *(tint_symbol_84);
-  tint_array_wrapper const tint_symbol_76 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+  tint_array<int, 10> const tint_symbol_76 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   QuicksortObject const tint_symbol_77 = {.numbers=tint_symbol_76};
   *(tint_symbol_84) = tint_symbol_77;
   *(tint_symbol_84) = x_925;
@@ -1532,7 +1541,7 @@
   uv[0] = x_929;
   *(tint_symbol_87) = x_330;
   QuicksortObject const x_930 = *(tint_symbol_84);
-  tint_array_wrapper const tint_symbol_78 = {.arr={0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+  tint_array<int, 10> const tint_symbol_78 = tint_array<int, 10>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   QuicksortObject const tint_symbol_79 = {.numbers=tint_symbol_78};
   *(tint_symbol_84) = tint_symbol_79;
   *(tint_symbol_84) = x_930;
diff --git a/test/tint/bug/tint/757.wgsl.expected.msl b/test/tint/bug/tint/757.wgsl.expected.msl
index 35bf540..b0644d0 100644
--- a/test/tint/bug/tint/757.wgsl.expected.msl
+++ b/test/tint/bug/tint/757.wgsl.expected.msl
@@ -1,12 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Constants {
   int level;
 };
 
 struct Result {
-  /* 0x0000 */ float values[1];
+  /* 0x0000 */ tint_array<float, 1> values;
 };
 
 void tint_symbol_inner(uint3 GlobalInvocationID, texture2d_array<float, access::sample> tint_symbol_1, device Result* const tint_symbol_2) {
diff --git a/test/tint/bug/tint/782.wgsl.expected.msl b/test/tint/bug/tint/782.wgsl.expected.msl
index a568ecb..0d4e877 100644
--- a/test/tint/bug/tint/782.wgsl.expected.msl
+++ b/test/tint/bug/tint/782.wgsl.expected.msl
@@ -1,13 +1,22 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  int arr[2];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 void foo() {
-  tint_array_wrapper tint_symbol = {};
-  tint_array_wrapper implict = {};
+  tint_array<int, 2> tint_symbol = {};
+  tint_array<int, 2> implict = {};
   implict = tint_symbol;
 }
 
diff --git a/test/tint/bug/tint/824.wgsl.expected.msl b/test/tint/bug/tint/824.wgsl.expected.msl
index c9a884c..4427773 100644
--- a/test/tint/bug/tint/824.wgsl.expected.msl
+++ b/test/tint/bug/tint/824.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Output {
   float4 Position;
   float4 color;
@@ -11,21 +24,13 @@
   float4 Position [[position]];
 };
 
-struct tint_array_wrapper {
-  float2 arr[4];
-};
-
-struct tint_array_wrapper_1 {
-  float4 arr[4];
-};
-
 Output tint_symbol_inner(uint VertexIndex, uint InstanceIndex) {
-  tint_array_wrapper const zv = {.arr={float2(0.200000003f), float2(0.300000012f), float2(-0.100000001f), float2(1.100000024f)}};
-  float const z = zv.arr[InstanceIndex][0];
+  tint_array<float2, 4> const zv = tint_array<float2, 4>{float2(0.200000003f), float2(0.300000012f), float2(-0.100000001f), float2(1.100000024f)};
+  float const z = zv[InstanceIndex][0];
   Output output = {};
   output.Position = float4(0.5f, 0.5f, z, 1.0f);
-  tint_array_wrapper_1 const colors = {.arr={float4(1.0f, 0.0f, 0.0f, 1.0f), float4(0.0f, 1.0f, 0.0f, 1.0f), float4(0.0f, 0.0f, 1.0f, 1.0f), float4(1.0f)}};
-  output.color = colors.arr[InstanceIndex];
+  tint_array<float4, 4> const colors = tint_array<float4, 4>{float4(1.0f, 0.0f, 0.0f, 1.0f), float4(0.0f, 1.0f, 0.0f, 1.0f), float4(0.0f, 0.0f, 1.0f, 1.0f), float4(1.0f)};
+  output.color = colors[InstanceIndex];
   return output;
 }
 
diff --git a/test/tint/bug/tint/827.wgsl.expected.msl b/test/tint/bug/tint/827.wgsl.expected.msl
index 532283a..4ea4405 100644
--- a/test/tint/bug/tint/827.wgsl.expected.msl
+++ b/test/tint/bug/tint/827.wgsl.expected.msl
@@ -1,8 +1,21 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Result {
-  /* 0x0000 */ float values[1];
+  /* 0x0000 */ tint_array<float, 1> values;
 };
 
 constant uint width = 128u;
diff --git a/test/tint/bug/tint/870.spvasm.expected.msl b/test/tint/bug/tint/870.spvasm.expected.msl
index 05b9e33..9a97441 100644
--- a/test/tint/bug/tint/870.spvasm.expected.msl
+++ b/test/tint/bug/tint/870.spvasm.expected.msl
@@ -1,15 +1,24 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  /* 0x0000 */ int arr[6];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 struct sspp962805860buildInformationS {
   /* 0x0000 */ float4 footprint;
   /* 0x0010 */ float4 offset;
   /* 0x0020 */ int essence;
-  /* 0x0024 */ tint_array_wrapper orientation;
+  /* 0x0024 */ tint_array<int, 6> orientation;
 };
 
 struct x_B4_BuildInformation {
@@ -17,14 +26,14 @@
 };
 
 void main_1(const device x_B4_BuildInformation* const tint_symbol_1) {
-  tint_array_wrapper orientation = {};
-  tint_array_wrapper const x_23 = (*(tint_symbol_1)).passthru.orientation;
-  orientation.arr[0] = x_23.arr[0u];
-  orientation.arr[1] = x_23.arr[1u];
-  orientation.arr[2] = x_23.arr[2u];
-  orientation.arr[3] = x_23.arr[3u];
-  orientation.arr[4] = x_23.arr[4u];
-  orientation.arr[5] = x_23.arr[5u];
+  tint_array<int, 6> orientation = {};
+  tint_array<int, 6> const x_23 = (*(tint_symbol_1)).passthru.orientation;
+  orientation[0] = x_23[0u];
+  orientation[1] = x_23[1u];
+  orientation[2] = x_23[2u];
+  orientation[3] = x_23[3u];
+  orientation[4] = x_23[4u];
+  orientation[5] = x_23[5u];
   return;
 }
 
diff --git a/test/tint/bug/tint/913.wgsl.expected.msl b/test/tint/bug/tint/913.wgsl.expected.msl
index e326a9b..3acacc7 100644
--- a/test/tint/bug/tint/913.wgsl.expected.msl
+++ b/test/tint/bug/tint/913.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Uniforms {
   /* 0x0000 */ uint dstTextureFlipY;
   /* 0x0004 */ uint channelCount;
@@ -10,7 +23,7 @@
 };
 
 struct OutputBuf {
-  /* 0x0000 */ uint result[1];
+  /* 0x0000 */ tint_array<uint, 1> result;
 };
 
 bool aboutEqual(float value, float expect) {
diff --git a/test/tint/bug/tint/914.wgsl.expected.msl b/test/tint/bug/tint/914.wgsl.expected.msl
index c6aad93..b7a253c 100644
--- a/test/tint/bug/tint/914.wgsl.expected.msl
+++ b/test/tint/bug/tint/914.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Uniforms {
   /* 0x0000 */ uint dimAOuter;
   /* 0x0004 */ uint dimInner;
@@ -8,7 +21,7 @@
 };
 
 struct Matrix {
-  /* 0x0000 */ float numbers[1];
+  /* 0x0000 */ tint_array<float, 1> numbers;
 };
 
 float mm_readA(uint row, uint col, const constant Uniforms* const tint_symbol_3, const device Matrix* const tint_symbol_4) {
@@ -44,28 +57,12 @@
 
 constant uint TileInner = 64u;
 
-struct tint_array_wrapper_1 {
-  float arr[64];
-};
-
-struct tint_array_wrapper {
-  tint_array_wrapper_1 arr[64];
-};
-
-struct tint_array_wrapper_2 {
-  float arr[16];
-};
-
-struct tint_array_wrapper_3 {
-  float arr[4];
-};
-
-void tint_symbol_inner(uint3 local_id, uint3 global_id, uint local_invocation_index, threadgroup tint_array_wrapper* const tint_symbol_9, threadgroup tint_array_wrapper* const tint_symbol_10, const constant Uniforms* const tint_symbol_11, const device Matrix* const tint_symbol_12, const device Matrix* const tint_symbol_13, device Matrix* const tint_symbol_14) {
+void tint_symbol_inner(uint3 local_id, uint3 global_id, uint local_invocation_index, threadgroup tint_array<tint_array<float, 64>, 64>* const tint_symbol_9, threadgroup tint_array<tint_array<float, 64>, 64>* const tint_symbol_10, const constant Uniforms* const tint_symbol_11, const device Matrix* const tint_symbol_12, const device Matrix* const tint_symbol_13, device Matrix* const tint_symbol_14) {
   for(uint idx = local_invocation_index; (idx < 4096u); idx = (idx + 256u)) {
     uint const i = (idx / 64u);
     uint const i_1 = (idx % 64u);
-    (*(tint_symbol_9)).arr[i].arr[i_1] = 0.0f;
-    (*(tint_symbol_10)).arr[i].arr[i_1] = 0.0f;
+    (*(tint_symbol_9))[i][i_1] = 0.0f;
+    (*(tint_symbol_10))[i][i_1] = 0.0f;
   }
   threadgroup_barrier(mem_flags::mem_threadgroup);
   uint const tileRow = (local_id[1] * 4u);
@@ -73,11 +70,11 @@
   uint const globalRow = (global_id[1] * 4u);
   uint const globalCol = (global_id[0] * 4u);
   uint const numTiles = ((((*(tint_symbol_11)).dimInner - 1u) / 64u) + 1u);
-  tint_array_wrapper_2 acc = {};
+  tint_array<float, 16> acc = {};
   float ACached = 0.0f;
-  tint_array_wrapper_3 BCached = {};
+  tint_array<float, 4> BCached = {};
   for(uint index = 0u; (index < (4u * 4u)); index = (index + 1u)) {
-    acc.arr[index] = 0.0f;
+    acc[index] = 0.0f;
   }
   uint const ColPerThreadA = (64u / 16u);
   uint const tileColA = (local_id[0] * ColPerThreadA);
@@ -89,7 +86,7 @@
         uint const inputRow = (tileRow + innerRow);
         uint const inputCol = (tileColA + innerCol);
         float const tint_symbol_1 = mm_readA((globalRow + innerRow), ((t * 64u) + inputCol), tint_symbol_11, tint_symbol_12);
-        (*(tint_symbol_9)).arr[inputRow].arr[inputCol] = tint_symbol_1;
+        (*(tint_symbol_9))[inputRow][inputCol] = tint_symbol_1;
       }
     }
     for(uint innerRow = 0u; (innerRow < RowPerThreadB); innerRow = (innerRow + 1u)) {
@@ -97,19 +94,19 @@
         uint const inputRow = (tileRowB + innerRow);
         uint const inputCol = (tileCol + innerCol);
         float const tint_symbol_2 = mm_readB(((t * 64u) + inputRow), (globalCol + innerCol), tint_symbol_11, tint_symbol_13);
-        (*(tint_symbol_10)).arr[innerCol].arr[inputCol] = tint_symbol_2;
+        (*(tint_symbol_10))[innerCol][inputCol] = tint_symbol_2;
       }
     }
     threadgroup_barrier(mem_flags::mem_threadgroup);
     for(uint k = 0u; (k < 64u); k = (k + 1u)) {
       for(uint inner = 0u; (inner < 4u); inner = (inner + 1u)) {
-        BCached.arr[inner] = (*(tint_symbol_10)).arr[k].arr[(tileCol + inner)];
+        BCached[inner] = (*(tint_symbol_10))[k][(tileCol + inner)];
       }
       for(uint innerRow = 0u; (innerRow < 4u); innerRow = (innerRow + 1u)) {
-        ACached = (*(tint_symbol_9)).arr[(tileRow + innerRow)].arr[k];
+        ACached = (*(tint_symbol_9))[(tileRow + innerRow)][k];
         for(uint innerCol = 0u; (innerCol < 4u); innerCol = (innerCol + 1u)) {
           uint const index = ((innerRow * 4u) + innerCol);
-          acc.arr[index] = (acc.arr[index] + (ACached * BCached.arr[innerCol]));
+          acc[index] = (acc[index] + (ACached * BCached[innerCol]));
         }
       }
     }
@@ -118,14 +115,14 @@
   for(uint innerRow = 0u; (innerRow < 4u); innerRow = (innerRow + 1u)) {
     for(uint innerCol = 0u; (innerCol < 4u); innerCol = (innerCol + 1u)) {
       uint const index = ((innerRow * 4u) + innerCol);
-      mm_write((globalRow + innerRow), (globalCol + innerCol), acc.arr[index], tint_symbol_11, tint_symbol_14);
+      mm_write((globalRow + innerRow), (globalCol + innerCol), acc[index], tint_symbol_11, tint_symbol_14);
     }
   }
 }
 
 kernel void tint_symbol(const constant Uniforms* tint_symbol_17 [[buffer(0)]], const device Matrix* tint_symbol_18 [[buffer(2)]], const device Matrix* tint_symbol_19 [[buffer(3)]], device Matrix* tint_symbol_20 [[buffer(1)]], uint3 local_id [[thread_position_in_threadgroup]], uint3 global_id [[thread_position_in_grid]], uint local_invocation_index [[thread_index_in_threadgroup]]) {
-  threadgroup tint_array_wrapper tint_symbol_15;
-  threadgroup tint_array_wrapper tint_symbol_16;
+  threadgroup tint_array<tint_array<float, 64>, 64> tint_symbol_15;
+  threadgroup tint_array<tint_array<float, 64>, 64> tint_symbol_16;
   tint_symbol_inner(local_id, global_id, local_invocation_index, &(tint_symbol_15), &(tint_symbol_16), tint_symbol_17, tint_symbol_18, tint_symbol_19, tint_symbol_20);
   return;
 }
diff --git a/test/tint/bug/tint/922.wgsl.expected.msl b/test/tint/bug/tint/922.wgsl.expected.msl
index 584f9e4..56d2fe7 100644
--- a/test/tint/bug/tint/922.wgsl.expected.msl
+++ b/test/tint/bug/tint/922.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Mat4x4_ {
   /* 0x0000 */ float4 mx;
   /* 0x0010 */ float4 my;
@@ -23,21 +36,13 @@
   /* 0x0000 */ Mat4x4_ u_Projection;
 };
 
-struct tint_array_wrapper {
-  /* 0x0000 */ Mat4x2_ arr[1];
-};
-
 struct ub_MaterialParams {
-  /* 0x0000 */ tint_array_wrapper u_TexMtx;
+  /* 0x0000 */ tint_array<Mat4x2_, 1> u_TexMtx;
   /* 0x0020 */ float4 u_Misc0_;
 };
 
-struct tint_array_wrapper_1 {
-  /* 0x0000 */ Mat4x3_ arr[32];
-};
-
 struct ub_PacketParams {
-  /* 0x0000 */ tint_array_wrapper_1 u_PosMtx;
+  /* 0x0000 */ tint_array<Mat4x3_, 32> u_PosMtx;
 };
 
 struct VertexOutput {
@@ -222,7 +227,7 @@
   Mat4x3_ t_PosMtx = {};
   float2 t_TexSpaceCoord = 0.0f;
   float const x_e15 = *(tint_symbol_5);
-  Mat4x3_ const x_e18 = (*(tint_symbol_6)).u_PosMtx.arr[int(x_e15)];
+  Mat4x3_ const x_e18 = (*(tint_symbol_6)).u_PosMtx[int(x_e15)];
   t_PosMtx = x_e18;
   Mat4x3_ const x_e23 = t_PosMtx;
   Mat4x4_ const x_e24 = x_Mat4x4_1(x_e23);
@@ -247,7 +252,7 @@
   if ((x_e52[0] == 2.0f)) {
     {
       float3 const x_e59 = *(tint_symbol_13);
-      Mat4x2_ const x_e64 = (*(tint_symbol_12)).u_TexMtx.arr[0];
+      Mat4x2_ const x_e64 = (*(tint_symbol_12)).u_TexMtx[0];
       float3 const x_e65 = *(tint_symbol_13);
       float2 const x_e68 = Mul2(x_e64, float4(x_e65, 1.0f));
       *(tint_symbol_14) = float2(x_e68).xy;
@@ -256,7 +261,7 @@
   } else {
     {
       float2 const x_e73 = *(tint_symbol_15);
-      Mat4x2_ const x_e79 = (*(tint_symbol_12)).u_TexMtx.arr[0];
+      Mat4x2_ const x_e79 = (*(tint_symbol_12)).u_TexMtx[0];
       float2 const x_e80 = *(tint_symbol_15);
       float2 const x_e84 = Mul2(x_e79, float4(x_e80, 1.0f, 1.0f));
       *(tint_symbol_14) = float2(x_e84).xy;
diff --git a/test/tint/bug/tint/942.wgsl.expected.msl b/test/tint/bug/tint/942.wgsl.expected.msl
index 6693f72..cca7a67 100644
--- a/test/tint/bug/tint/942.wgsl.expected.msl
+++ b/test/tint/bug/tint/942.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Params {
   /* 0x0000 */ uint filterDim;
   /* 0x0004 */ uint blockDim;
@@ -10,19 +23,11 @@
   /* 0x0000 */ uint value;
 };
 
-struct tint_array_wrapper_1 {
-  float3 arr[256];
-};
-
-struct tint_array_wrapper {
-  tint_array_wrapper_1 arr[4];
-};
-
-void tint_symbol_inner(uint3 WorkGroupID, uint3 LocalInvocationID, uint local_invocation_index, threadgroup tint_array_wrapper* const tint_symbol_1, const constant Params* const tint_symbol_2, texture2d<float, access::sample> tint_symbol_3, const constant Flip* const tint_symbol_4, sampler tint_symbol_5, texture2d<float, access::write> tint_symbol_6) {
+void tint_symbol_inner(uint3 WorkGroupID, uint3 LocalInvocationID, uint local_invocation_index, threadgroup tint_array<tint_array<float3, 256>, 4>* const tint_symbol_1, const constant Params* const tint_symbol_2, texture2d<float, access::sample> tint_symbol_3, const constant Flip* const tint_symbol_4, sampler tint_symbol_5, texture2d<float, access::write> tint_symbol_6) {
   for(uint idx = local_invocation_index; (idx < 1024u); idx = (idx + 64u)) {
     uint const i_1 = (idx / 256u);
     uint const i_2 = (idx % 256u);
-    (*(tint_symbol_1)).arr[i_1].arr[i_2] = float3(0.0f);
+    (*(tint_symbol_1))[i_1][i_2] = float3(0.0f);
   }
   threadgroup_barrier(mem_flags::mem_threadgroup);
   uint const filterOffset = (((*(tint_symbol_2)).filterDim - 1u) / 2u);
@@ -34,7 +39,7 @@
       if (((*(tint_symbol_4)).value != 0u)) {
         loadIndex = int2(loadIndex).yx;
       }
-      (*(tint_symbol_1)).arr[r].arr[((4u * LocalInvocationID[0]) + c)] = float4(tint_symbol_3.sample(tint_symbol_5, ((float2(loadIndex) + float2(0.25f)) / float2(dims)), level(0.0f))).rgb;
+      (*(tint_symbol_1))[r][((4u * LocalInvocationID[0]) + c)] = float4(tint_symbol_3.sample(tint_symbol_5, ((float2(loadIndex) + float2(0.25f)) / float2(dims)), level(0.0f))).rgb;
     }
   }
   threadgroup_barrier(mem_flags::mem_threadgroup);
@@ -49,7 +54,7 @@
         float3 acc = float3(0.0f);
         for(uint f = 0u; (f < (*(tint_symbol_2)).filterDim); f = (f + 1u)) {
           uint i = ((center + f) - filterOffset);
-          acc = (acc + ((1.0f / float((*(tint_symbol_2)).filterDim)) * (*(tint_symbol_1)).arr[r].arr[i]));
+          acc = (acc + ((1.0f / float((*(tint_symbol_2)).filterDim)) * (*(tint_symbol_1))[r][i]));
         }
         tint_symbol_6.write(float4(acc, 1.0f), uint2(writeIndex));
       }
@@ -58,7 +63,7 @@
 }
 
 kernel void tint_symbol(const constant Params* tint_symbol_8 [[buffer(0)]], texture2d<float, access::sample> tint_symbol_9 [[texture(0)]], const constant Flip* tint_symbol_10 [[buffer(1)]], sampler tint_symbol_11 [[sampler(0)]], texture2d<float, access::write> tint_symbol_12 [[texture(1)]], uint3 WorkGroupID [[threadgroup_position_in_grid]], uint3 LocalInvocationID [[thread_position_in_threadgroup]], uint local_invocation_index [[thread_index_in_threadgroup]]) {
-  threadgroup tint_array_wrapper tint_symbol_7;
+  threadgroup tint_array<tint_array<float3, 256>, 4> tint_symbol_7;
   tint_symbol_inner(WorkGroupID, LocalInvocationID, local_invocation_index, &(tint_symbol_7), tint_symbol_8, tint_symbol_9, tint_symbol_10, tint_symbol_11, tint_symbol_12);
   return;
 }
diff --git a/test/tint/bug/tint/943.spvasm.expected.msl b/test/tint/bug/tint/943.spvasm.expected.msl
index 6093a20..48563e2 100644
--- a/test/tint/bug/tint/943.spvasm.expected.msl
+++ b/test/tint/bug/tint/943.spvasm.expected.msl
@@ -4,44 +4,41 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Uniforms {
   /* 0x0000 */ float tint_symbol;
-  /* 0x0004 */ int8_t tint_pad[12];
+  /* 0x0004 */ tint_array<int8_t, 12> tint_pad;
   /* 0x0010 */ packed_int3 aShape;
-  /* 0x001c */ int8_t tint_pad_1[4];
+  /* 0x001c */ tint_array<int8_t, 4> tint_pad_1;
   /* 0x0020 */ packed_int3 bShape;
-  /* 0x002c */ int8_t tint_pad_2[4];
+  /* 0x002c */ tint_array<int8_t, 4> tint_pad_2;
   /* 0x0030 */ packed_int3 outShape;
-  /* 0x003c */ int8_t tint_pad_3[4];
+  /* 0x003c */ tint_array<int8_t, 4> tint_pad_3;
   /* 0x0040 */ int2 outShapeStrides;
 };
 
 struct ssbOut {
-  /* 0x0000 */ float result[1];
+  /* 0x0000 */ tint_array<float, 1> result;
 };
 
 struct ssbA {
-  /* 0x0000 */ float A[1];
+  /* 0x0000 */ tint_array<float, 1> A;
 };
 
 struct ssbB {
-  /* 0x0000 */ float B[1];
-};
-
-struct tint_array_wrapper_1 {
-  float arr[64];
-};
-
-struct tint_array_wrapper {
-  tint_array_wrapper_1 arr[64];
-};
-
-struct tint_array_wrapper_3 {
-  float arr[1];
-};
-
-struct tint_array_wrapper_2 {
-  tint_array_wrapper_3 arr[64];
+  /* 0x0000 */ tint_array<float, 1> B;
 };
 
 bool coordsInBounds_vi2_vi2_(thread int2* const coord, thread int2* const shape) {
@@ -175,11 +172,7 @@
   return;
 }
 
-struct tint_array_wrapper_4 {
-  tint_array_wrapper_3 arr[1];
-};
-
-void mm_matMul_i1_i1_i1_(thread int* const dimAOuter, thread int* const dimInner, thread int* const dimBOuter, thread uint3* const tint_symbol_19, thread uint3* const tint_symbol_20, const constant Uniforms* const tint_symbol_21, thread int* const tint_symbol_22, thread int* const tint_symbol_23, thread int* const tint_symbol_24, const device ssbA* const tint_symbol_25, threadgroup tint_array_wrapper* const tint_symbol_26, thread int* const tint_symbol_27, const device ssbB* const tint_symbol_28, threadgroup tint_array_wrapper_2* const tint_symbol_29, device ssbOut* const tint_symbol_30) {
+void mm_matMul_i1_i1_i1_(thread int* const dimAOuter, thread int* const dimInner, thread int* const dimBOuter, thread uint3* const tint_symbol_19, thread uint3* const tint_symbol_20, const constant Uniforms* const tint_symbol_21, thread int* const tint_symbol_22, thread int* const tint_symbol_23, thread int* const tint_symbol_24, const device ssbA* const tint_symbol_25, threadgroup tint_array<tint_array<float, 64>, 64>* const tint_symbol_26, thread int* const tint_symbol_27, const device ssbB* const tint_symbol_28, threadgroup tint_array<tint_array<float, 1>, 64>* const tint_symbol_29, device ssbOut* const tint_symbol_30) {
   int tileRow = 0;
   int tileCol = 0;
   int globalRow = 0;
@@ -187,7 +180,7 @@
   int numTiles = 0;
   int innerRow = 0;
   int innerCol = 0;
-  tint_array_wrapper_4 acc = {};
+  tint_array<tint_array<float, 1>, 1> acc = {};
   int tileColA = 0;
   int tileRowB = 0;
   int t = 0;
@@ -205,7 +198,7 @@
   int param_6 = 0;
   int k = 0;
   int inner = 0;
-  tint_array_wrapper_3 BCached = {};
+  tint_array<float, 1> BCached = {};
   int innerRow_3 = 0;
   float ACached = 0.0f;
   int innerCol_3 = 0;
@@ -240,7 +233,7 @@
       }
       int const x_177 = innerRow;
       int const x_178 = innerCol;
-      acc.arr[x_177].arr[x_178] = 0.0f;
+      acc[x_177][x_178] = 0.0f;
       {
         int const x_181 = innerCol;
         innerCol = as_type<int>((as_type<uint>(x_181) + as_type<uint>(1)));
@@ -292,7 +285,7 @@
         param_3 = as_type<int>((as_type<uint>(x_235) + as_type<uint>(x_236)));
         param_4 = as_type<int>((as_type<uint>(as_type<int>((as_type<uint>(x_238) * as_type<uint>(64)))) + as_type<uint>(x_240)));
         float const x_244 = mm_readA_i1_i1_(&(param_3), &(param_4), tint_symbol_21, tint_symbol_22, tint_symbol_23, tint_symbol_24, tint_symbol_25);
-        (*(tint_symbol_26)).arr[x_233].arr[x_234] = x_244;
+        (*(tint_symbol_26))[x_233][x_234] = x_244;
         {
           int const x_247 = innerCol_1;
           innerCol_1 = as_type<int>((as_type<uint>(x_247) + as_type<uint>(1)));
@@ -332,7 +325,7 @@
         param_5 = as_type<int>((as_type<uint>(as_type<int>((as_type<uint>(x_280) * as_type<uint>(64)))) + as_type<uint>(x_282)));
         param_6 = as_type<int>((as_type<uint>(x_284) + as_type<uint>(x_285)));
         float const x_289 = mm_readB_i1_i1_(&(param_5), &(param_6), tint_symbol_21, tint_symbol_23, tint_symbol_27, tint_symbol_24, tint_symbol_28);
-        (*(tint_symbol_29)).arr[x_278].arr[x_279] = x_289;
+        (*(tint_symbol_29))[x_278][x_279] = x_289;
         {
           int const x_291 = innerCol_2;
           innerCol_2 = as_type<int>((as_type<uint>(x_291) + as_type<uint>(1)));
@@ -362,8 +355,8 @@
         int const x_315 = k;
         int const x_316 = tileCol;
         int const x_317 = inner;
-        float const x_320 = (*(tint_symbol_29)).arr[x_315].arr[as_type<int>((as_type<uint>(x_316) + as_type<uint>(x_317)))];
-        BCached.arr[x_314] = x_320;
+        float const x_320 = (*(tint_symbol_29))[x_315][as_type<int>((as_type<uint>(x_316) + as_type<uint>(x_317)))];
+        BCached[x_314] = x_320;
         {
           int const x_322 = inner;
           inner = as_type<int>((as_type<uint>(x_322) + as_type<uint>(1)));
@@ -379,7 +372,7 @@
         int const x_333 = tileRow;
         int const x_334 = innerRow_3;
         int const x_336 = k;
-        float const x_338 = (*(tint_symbol_26)).arr[as_type<int>((as_type<uint>(x_333) + as_type<uint>(x_334)))].arr[x_336];
+        float const x_338 = (*(tint_symbol_26))[as_type<int>((as_type<uint>(x_333) + as_type<uint>(x_334)))][x_336];
         ACached = x_338;
         innerCol_3 = 0;
         while (true) {
@@ -392,9 +385,9 @@
           int const x_348 = innerCol_3;
           float const x_349 = ACached;
           int const x_350 = innerCol_3;
-          float const x_352 = BCached.arr[x_350];
-          float const x_355 = acc.arr[x_347].arr[x_348];
-          acc.arr[x_347].arr[x_348] = (x_355 + (x_349 * x_352));
+          float const x_352 = BCached[x_350];
+          float const x_355 = acc[x_347][x_348];
+          acc[x_347][x_348] = (x_355 + (x_349 * x_352));
           {
             int const x_358 = innerCol_3;
             innerCol_3 = as_type<int>((as_type<uint>(x_358) + as_type<uint>(1)));
@@ -454,7 +447,7 @@
         int const x_404 = innerCol_4;
         param_7 = as_type<int>((as_type<uint>(x_397) + as_type<uint>(x_398)));
         param_8 = as_type<int>((as_type<uint>(x_400) + as_type<uint>(x_401)));
-        float const x_409 = acc.arr[x_403].arr[x_404];
+        float const x_409 = acc[x_403][x_404];
         param_9 = x_409;
         mm_write_i1_i1_f1_(&(param_7), &(param_8), &(param_9), tint_symbol_24, tint_symbol_21, tint_symbol_30);
       }
@@ -471,7 +464,7 @@
   return;
 }
 
-void main_1(const constant Uniforms* const tint_symbol_31, thread int* const tint_symbol_32, thread int* const tint_symbol_33, thread int* const tint_symbol_34, thread uint3* const tint_symbol_35, thread int* const tint_symbol_36, thread uint3* const tint_symbol_37, const device ssbA* const tint_symbol_38, threadgroup tint_array_wrapper* const tint_symbol_39, const device ssbB* const tint_symbol_40, threadgroup tint_array_wrapper_2* const tint_symbol_41, device ssbOut* const tint_symbol_42) {
+void main_1(const constant Uniforms* const tint_symbol_31, thread int* const tint_symbol_32, thread int* const tint_symbol_33, thread int* const tint_symbol_34, thread uint3* const tint_symbol_35, thread int* const tint_symbol_36, thread uint3* const tint_symbol_37, const device ssbA* const tint_symbol_38, threadgroup tint_array<tint_array<float, 64>, 64>* const tint_symbol_39, const device ssbB* const tint_symbol_40, threadgroup tint_array<tint_array<float, 1>, 64>* const tint_symbol_41, device ssbOut* const tint_symbol_42) {
   int param_18 = 0;
   int param_19 = 0;
   int param_20 = 0;
@@ -493,16 +486,16 @@
   return;
 }
 
-void tint_symbol_1_inner(uint3 gl_LocalInvocationID_param, uint3 gl_GlobalInvocationID_param, uint local_invocation_index, threadgroup tint_array_wrapper_2* const tint_symbol_43, threadgroup tint_array_wrapper* const tint_symbol_44, thread uint3* const tint_symbol_45, thread uint3* const tint_symbol_46, const constant Uniforms* const tint_symbol_47, thread int* const tint_symbol_48, thread int* const tint_symbol_49, thread int* const tint_symbol_50, thread int* const tint_symbol_51, const device ssbA* const tint_symbol_52, const device ssbB* const tint_symbol_53, device ssbOut* const tint_symbol_54) {
+void tint_symbol_1_inner(uint3 gl_LocalInvocationID_param, uint3 gl_GlobalInvocationID_param, uint local_invocation_index, threadgroup tint_array<tint_array<float, 1>, 64>* const tint_symbol_43, threadgroup tint_array<tint_array<float, 64>, 64>* const tint_symbol_44, thread uint3* const tint_symbol_45, thread uint3* const tint_symbol_46, const constant Uniforms* const tint_symbol_47, thread int* const tint_symbol_48, thread int* const tint_symbol_49, thread int* const tint_symbol_50, thread int* const tint_symbol_51, const device ssbA* const tint_symbol_52, const device ssbB* const tint_symbol_53, device ssbOut* const tint_symbol_54) {
   {
     uint const i_1 = local_invocation_index;
     uint const i_2 = (local_invocation_index % 1u);
-    (*(tint_symbol_43)).arr[i_1].arr[i_2] = 0.0f;
+    (*(tint_symbol_43))[i_1][i_2] = 0.0f;
   }
   for(uint idx = local_invocation_index; (idx < 4096u); idx = (idx + 64u)) {
     uint const i = (idx / 64u);
     uint const i_1 = (idx % 64u);
-    (*(tint_symbol_44)).arr[i].arr[i_1] = 0.0f;
+    (*(tint_symbol_44))[i][i_1] = 0.0f;
   }
   threadgroup_barrier(mem_flags::mem_threadgroup);
   *(tint_symbol_45) = gl_LocalInvocationID_param;
@@ -511,8 +504,8 @@
 }
 
 kernel void tint_symbol_1(const constant Uniforms* tint_symbol_59 [[buffer(0)]], const device ssbA* tint_symbol_64 [[buffer(2)]], const device ssbB* tint_symbol_65 [[buffer(3)]], device ssbOut* tint_symbol_66 [[buffer(1)]], uint3 gl_LocalInvocationID_param [[thread_position_in_threadgroup]], uint3 gl_GlobalInvocationID_param [[thread_position_in_grid]], uint local_invocation_index [[thread_index_in_threadgroup]]) {
-  threadgroup tint_array_wrapper_2 tint_symbol_55;
-  threadgroup tint_array_wrapper tint_symbol_56;
+  threadgroup tint_array<tint_array<float, 1>, 64> tint_symbol_55;
+  threadgroup tint_array<tint_array<float, 64>, 64> tint_symbol_56;
   thread uint3 tint_symbol_57 = 0u;
   thread uint3 tint_symbol_58 = 0u;
   thread int tint_symbol_60 = 0;
diff --git a/test/tint/bug/tint/948.wgsl.expected.msl b/test/tint/bug/tint/948.wgsl.expected.msl
index c03a21b..c21d89f 100644
--- a/test/tint/bug/tint/948.wgsl.expected.msl
+++ b/test/tint/bug/tint/948.wgsl.expected.msl
@@ -14,6 +14,18 @@
 
 using namespace metal;
 
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 template<typename T, int N, int M>
 inline vec<T, M> operator*(matrix<T, N, M> lhs, packed_vec<T, N> rhs) {
   return lhs * vec<T, N>(rhs);
@@ -27,7 +39,7 @@
 struct LeftOver {
   /* 0x0000 */ float time;
   /* 0x0004 */ uint padding;
-  /* 0x0008 */ int8_t tint_pad[8];
+  /* 0x0008 */ tint_array<int8_t, 8> tint_pad;
   /* 0x0010 */ float4x4 worldViewProjection;
   /* 0x0050 */ float2 outputSize;
   /* 0x0058 */ float2 stageSize;
@@ -35,7 +47,7 @@
   /* 0x0068 */ float stageScale;
   /* 0x006c */ float spriteCount;
   /* 0x0070 */ packed_float3 colorMul;
-  /* 0x007c */ int8_t tint_pad_1[4];
+  /* 0x007c */ tint_array<int8_t, 4> tint_pad_1;
 };
 
 float4x4 getFrameData_f1_(thread float* const frameID, const constant LeftOver* const tint_symbol_5, texture2d<float, access::sample> tint_symbol_6, sampler tint_symbol_7) {
diff --git a/test/tint/bug/tint/949.wgsl.expected.msl b/test/tint/bug/tint/949.wgsl.expected.msl
index 5aab1bd..9b8daf4 100644
--- a/test/tint/bug/tint/949.wgsl.expected.msl
+++ b/test/tint/bug/tint/949.wgsl.expected.msl
@@ -14,6 +14,18 @@
 
 using namespace metal;
 
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 template<typename T, int N, int M>
 inline vec<T, M> operator*(matrix<T, N, M> lhs, packed_vec<T, N> rhs) {
   return lhs * vec<T, N>(rhs);
@@ -34,7 +46,7 @@
   /* 0x0040 */ float4x4 u_ViewProjection;
   /* 0x0080 */ float u_bumpStrength;
   /* 0x0084 */ uint padding;
-  /* 0x0088 */ int8_t tint_pad[8];
+  /* 0x0088 */ tint_array<int8_t, 8> tint_pad;
   /* 0x0090 */ packed_float3 u_cameraPosition;
   /* 0x009c */ float u_parallaxScale;
   /* 0x00a0 */ float textureInfoName;
@@ -50,7 +62,7 @@
   /* 0x003c */ uint padding_2;
   /* 0x0040 */ float4 shadowsInfo;
   /* 0x0050 */ float2 depthValues;
-  /* 0x0058 */ int8_t tint_pad_1[8];
+  /* 0x0058 */ tint_array<int8_t, 8> tint_pad_1;
 };
 
 float3x3 cotangent_frame_vf3_vf3_vf2_vf2_(thread float3* const normal_1, thread float3* const p, thread float2* const uv, thread float2* const tangentSpaceParams) {
diff --git a/test/tint/bug/tint/951.spvasm.expected.msl b/test/tint/bug/tint/951.spvasm.expected.msl
index 80a05a7..0e5fdff 100644
--- a/test/tint/bug/tint/951.spvasm.expected.msl
+++ b/test/tint/bug/tint/951.spvasm.expected.msl
@@ -1,12 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct ssbOut {
-  /* 0x0000 */ float result[1];
+  /* 0x0000 */ tint_array<float, 1> result;
 };
 
 struct ssbA {
-  /* 0x0000 */ float A[1];
+  /* 0x0000 */ tint_array<float, 1> A;
 };
 
 struct Uniforms {
diff --git a/test/tint/bug/tint/977.spvasm.expected.msl b/test/tint/bug/tint/977.spvasm.expected.msl
index 16062d0..83b2b6c 100644
--- a/test/tint/bug/tint/977.spvasm.expected.msl
+++ b/test/tint/bug/tint/977.spvasm.expected.msl
@@ -1,16 +1,29 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct ResultMatrix {
-  /* 0x0000 */ float numbers[1];
+  /* 0x0000 */ tint_array<float, 1> numbers;
 };
 
 struct FirstMatrix {
-  float numbers[1];
+  tint_array<float, 1> numbers;
 };
 
 struct SecondMatrix {
-  float numbers[1];
+  tint_array<float, 1> numbers;
 };
 
 struct Uniforms {
diff --git a/test/tint/bug/tint/993.wgsl.expected.msl b/test/tint/bug/tint/993.wgsl.expected.msl
index 98f5117..7b82d95 100644
--- a/test/tint/bug/tint/993.wgsl.expected.msl
+++ b/test/tint/bug/tint/993.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Constants {
   /* 0x0000 */ uint zero;
 };
@@ -9,16 +22,12 @@
   /* 0x0000 */ uint value;
 };
 
-struct tint_array_wrapper {
-  /* 0x0000 */ atomic_int arr[3];
-};
-
 struct TestData {
-  /* 0x0000 */ tint_array_wrapper data;
+  /* 0x0000 */ tint_array<atomic_int, 3> data;
 };
 
 int runTest(device TestData* const tint_symbol_3, const constant Constants* const tint_symbol_4) {
-  return atomic_load_explicit(&((*(tint_symbol_3)).data.arr[(0u + uint((*(tint_symbol_4)).zero))]), memory_order_relaxed);
+  return atomic_load_explicit(&((*(tint_symbol_3)).data[(0u + uint((*(tint_symbol_4)).zero))]), memory_order_relaxed);
 }
 
 kernel void tint_symbol(device TestData* tint_symbol_5 [[buffer(2)]], const constant Constants* tint_symbol_6 [[buffer(0)]], device Result* tint_symbol_7 [[buffer(1)]]) {
diff --git a/test/tint/bug/tint/998.wgsl.expected.msl b/test/tint/bug/tint/998.wgsl.expected.msl
index 740838e..c9dd773 100644
--- a/test/tint/bug/tint/998.wgsl.expected.msl
+++ b/test/tint/bug/tint/998.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Constants {
   /* 0x0000 */ uint zero;
 };
@@ -9,17 +22,13 @@
   uint value;
 };
 
-struct tint_array_wrapper {
-  uint arr[3];
-};
-
 struct S {
-  tint_array_wrapper data;
+  tint_array<uint, 3> data;
 };
 
 kernel void tint_symbol(const constant Constants* tint_symbol_2 [[buffer(0)]]) {
   thread S tint_symbol_1 = {};
-  tint_symbol_1.data.arr[(*(tint_symbol_2)).zero] = 0u;
+  tint_symbol_1.data[(*(tint_symbol_2)).zero] = 0u;
   return;
 }
 
diff --git a/test/tint/builtins/arrayLength/complex_via_let.wgsl.expected.msl b/test/tint/builtins/arrayLength/complex_via_let.wgsl.expected.msl
index 537a453..f92477a 100644
--- a/test/tint/builtins/arrayLength/complex_via_let.wgsl.expected.msl
+++ b/test/tint/builtins/arrayLength/complex_via_let.wgsl.expected.msl
@@ -1,12 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol_1 {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 struct S {
-  int a[1];
+  tint_array<int, 1> a;
 };
 
 kernel void tint_symbol(const constant tint_symbol_1* tint_symbol_3 [[buffer(30)]]) {
diff --git a/test/tint/builtins/arrayLength/complex_via_let_no_struct.wgsl.expected.msl b/test/tint/builtins/arrayLength/complex_via_let_no_struct.wgsl.expected.msl
index 0ab65d1..3fb5295 100644
--- a/test/tint/builtins/arrayLength/complex_via_let_no_struct.wgsl.expected.msl
+++ b/test/tint/builtins/arrayLength/complex_via_let_no_struct.wgsl.expected.msl
@@ -1,8 +1,21 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol_1 {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 kernel void tint_symbol(const constant tint_symbol_1* tint_symbol_3 [[buffer(30)]]) {
diff --git a/test/tint/builtins/arrayLength/deprecated.wgsl.expected.msl b/test/tint/builtins/arrayLength/deprecated.wgsl.expected.msl
index bf8e5b0..bc4e7ca 100644
--- a/test/tint/builtins/arrayLength/deprecated.wgsl.expected.msl
+++ b/test/tint/builtins/arrayLength/deprecated.wgsl.expected.msl
@@ -1,12 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol_1 {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 struct S {
-  int a[1];
+  tint_array<int, 1> a;
 };
 
 kernel void tint_symbol(const constant tint_symbol_1* tint_symbol_3 [[buffer(30)]]) {
diff --git a/test/tint/builtins/arrayLength/simple.wgsl.expected.msl b/test/tint/builtins/arrayLength/simple.wgsl.expected.msl
index 537a453..f92477a 100644
--- a/test/tint/builtins/arrayLength/simple.wgsl.expected.msl
+++ b/test/tint/builtins/arrayLength/simple.wgsl.expected.msl
@@ -1,12 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol_1 {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 struct S {
-  int a[1];
+  tint_array<int, 1> a;
 };
 
 kernel void tint_symbol(const constant tint_symbol_1* tint_symbol_3 [[buffer(30)]]) {
diff --git a/test/tint/builtins/arrayLength/simple_no_struct.wgsl.expected.msl b/test/tint/builtins/arrayLength/simple_no_struct.wgsl.expected.msl
index 0ab65d1..3fb5295 100644
--- a/test/tint/builtins/arrayLength/simple_no_struct.wgsl.expected.msl
+++ b/test/tint/builtins/arrayLength/simple_no_struct.wgsl.expected.msl
@@ -1,8 +1,21 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol_1 {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 kernel void tint_symbol(const constant tint_symbol_1* tint_symbol_3 [[buffer(30)]]) {
diff --git a/test/tint/builtins/arrayLength/via_let.wgsl.expected.msl b/test/tint/builtins/arrayLength/via_let.wgsl.expected.msl
index 537a453..f92477a 100644
--- a/test/tint/builtins/arrayLength/via_let.wgsl.expected.msl
+++ b/test/tint/builtins/arrayLength/via_let.wgsl.expected.msl
@@ -1,12 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol_1 {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 struct S {
-  int a[1];
+  tint_array<int, 1> a;
 };
 
 kernel void tint_symbol(const constant tint_symbol_1* tint_symbol_3 [[buffer(30)]]) {
diff --git a/test/tint/builtins/arrayLength/via_let_complex.wgsl.expected.msl b/test/tint/builtins/arrayLength/via_let_complex.wgsl.expected.msl
index 537a453..f92477a 100644
--- a/test/tint/builtins/arrayLength/via_let_complex.wgsl.expected.msl
+++ b/test/tint/builtins/arrayLength/via_let_complex.wgsl.expected.msl
@@ -1,12 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol_1 {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 struct S {
-  int a[1];
+  tint_array<int, 1> a;
 };
 
 kernel void tint_symbol(const constant tint_symbol_1* tint_symbol_3 [[buffer(30)]]) {
diff --git a/test/tint/builtins/arrayLength/via_let_complex_no_struct.wgsl.expected.msl b/test/tint/builtins/arrayLength/via_let_complex_no_struct.wgsl.expected.msl
index 0ab65d1..3fb5295 100644
--- a/test/tint/builtins/arrayLength/via_let_complex_no_struct.wgsl.expected.msl
+++ b/test/tint/builtins/arrayLength/via_let_complex_no_struct.wgsl.expected.msl
@@ -1,8 +1,21 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol_1 {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 kernel void tint_symbol(const constant tint_symbol_1* tint_symbol_3 [[buffer(30)]]) {
diff --git a/test/tint/builtins/arrayLength/via_let_no_struct.wgsl.expected.msl b/test/tint/builtins/arrayLength/via_let_no_struct.wgsl.expected.msl
index 0ab65d1..3fb5295 100644
--- a/test/tint/builtins/arrayLength/via_let_no_struct.wgsl.expected.msl
+++ b/test/tint/builtins/arrayLength/via_let_no_struct.wgsl.expected.msl
@@ -1,8 +1,21 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol_1 {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 kernel void tint_symbol(const constant tint_symbol_1* tint_symbol_3 [[buffer(30)]]) {
diff --git a/test/tint/builtins/gen/literal/arrayLength/1588cd.wgsl.expected.msl b/test/tint/builtins/gen/literal/arrayLength/1588cd.wgsl.expected.msl
index 1f8ac29..8f9b209 100644
--- a/test/tint/builtins/gen/literal/arrayLength/1588cd.wgsl.expected.msl
+++ b/test/tint/builtins/gen/literal/arrayLength/1588cd.wgsl.expected.msl
@@ -1,12 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol_1 {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 struct SB_RO {
-  int arg_0[1];
+  tint_array<int, 1> arg_0;
 };
 
 void arrayLength_1588cd(const constant tint_symbol_1* const tint_symbol_3) {
diff --git a/test/tint/builtins/gen/literal/arrayLength/61b1c7.wgsl.expected.msl b/test/tint/builtins/gen/literal/arrayLength/61b1c7.wgsl.expected.msl
index 9fa02f3..a0b7187 100644
--- a/test/tint/builtins/gen/literal/arrayLength/61b1c7.wgsl.expected.msl
+++ b/test/tint/builtins/gen/literal/arrayLength/61b1c7.wgsl.expected.msl
@@ -1,12 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol_1 {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 struct SB_RW {
-  int arg_0[1];
+  tint_array<int, 1> arg_0;
 };
 
 void arrayLength_61b1c7(const constant tint_symbol_1* const tint_symbol_3) {
diff --git a/test/tint/builtins/gen/literal/arrayLength/a0f5ca.wgsl.expected.msl b/test/tint/builtins/gen/literal/arrayLength/a0f5ca.wgsl.expected.msl
index f1b7785..b0aba71 100644
--- a/test/tint/builtins/gen/literal/arrayLength/a0f5ca.wgsl.expected.msl
+++ b/test/tint/builtins/gen/literal/arrayLength/a0f5ca.wgsl.expected.msl
@@ -1,12 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol_1 {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 struct SB_RO {
-  float arg_0[1];
+  tint_array<float, 1> arg_0;
 };
 
 void arrayLength_a0f5ca(const constant tint_symbol_1* const tint_symbol_3) {
diff --git a/test/tint/builtins/gen/literal/arrayLength/cdd123.wgsl.expected.msl b/test/tint/builtins/gen/literal/arrayLength/cdd123.wgsl.expected.msl
index 22cfa6c..b7fe50b 100644
--- a/test/tint/builtins/gen/literal/arrayLength/cdd123.wgsl.expected.msl
+++ b/test/tint/builtins/gen/literal/arrayLength/cdd123.wgsl.expected.msl
@@ -1,12 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol_1 {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 struct SB_RW {
-  float arg_0[1];
+  tint_array<float, 1> arg_0;
 };
 
 void arrayLength_cdd123(const constant tint_symbol_1* const tint_symbol_3) {
diff --git a/test/tint/builtins/gen/literal/arrayLength/cfca0a.wgsl.expected.msl b/test/tint/builtins/gen/literal/arrayLength/cfca0a.wgsl.expected.msl
index d64c1e0..f62106e 100644
--- a/test/tint/builtins/gen/literal/arrayLength/cfca0a.wgsl.expected.msl
+++ b/test/tint/builtins/gen/literal/arrayLength/cfca0a.wgsl.expected.msl
@@ -1,12 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol_1 {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 struct SB_RO {
-  uint arg_0[1];
+  tint_array<uint, 1> arg_0;
 };
 
 void arrayLength_cfca0a(const constant tint_symbol_1* const tint_symbol_3) {
diff --git a/test/tint/builtins/gen/literal/arrayLength/eb510f.wgsl.expected.msl b/test/tint/builtins/gen/literal/arrayLength/eb510f.wgsl.expected.msl
index 4412853..05c3bc9 100644
--- a/test/tint/builtins/gen/literal/arrayLength/eb510f.wgsl.expected.msl
+++ b/test/tint/builtins/gen/literal/arrayLength/eb510f.wgsl.expected.msl
@@ -1,12 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol_1 {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 struct SB_RW {
-  uint arg_0[1];
+  tint_array<uint, 1> arg_0;
 };
 
 void arrayLength_eb510f(const constant tint_symbol_1* const tint_symbol_3) {
diff --git a/test/tint/builtins/gen/literal/textureLoad/8acf41.wgsl.expected.msl b/test/tint/builtins/gen/literal/textureLoad/8acf41.wgsl.expected.msl
index 04f882d..617b8df 100644
--- a/test/tint/builtins/gen/literal/textureLoad/8acf41.wgsl.expected.msl
+++ b/test/tint/builtins/gen/literal/textureLoad/8acf41.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct GammaTransferParams {
   /* 0x0000 */ float G;
   /* 0x0004 */ float A;
@@ -15,7 +28,7 @@
 struct ExternalTextureParams {
   /* 0x0000 */ uint numPlanes;
   /* 0x0004 */ uint doYuvToRgbConversionOnly;
-  /* 0x0008 */ int8_t tint_pad[8];
+  /* 0x0008 */ tint_array<int8_t, 8> tint_pad;
   /* 0x0010 */ float3x4 yuvToRgbConversionMatrix;
   /* 0x0040 */ GammaTransferParams gammaDecodeParams;
   /* 0x0060 */ GammaTransferParams gammaEncodeParams;
diff --git a/test/tint/builtins/gen/literal/textureSampleLevel/979816.wgsl.expected.msl b/test/tint/builtins/gen/literal/textureSampleLevel/979816.wgsl.expected.msl
index fa5b6c3..5b65152 100644
--- a/test/tint/builtins/gen/literal/textureSampleLevel/979816.wgsl.expected.msl
+++ b/test/tint/builtins/gen/literal/textureSampleLevel/979816.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct GammaTransferParams {
   /* 0x0000 */ float G;
   /* 0x0004 */ float A;
@@ -15,7 +28,7 @@
 struct ExternalTextureParams {
   /* 0x0000 */ uint numPlanes;
   /* 0x0004 */ uint doYuvToRgbConversionOnly;
-  /* 0x0008 */ int8_t tint_pad[8];
+  /* 0x0008 */ tint_array<int8_t, 8> tint_pad;
   /* 0x0010 */ float3x4 yuvToRgbConversionMatrix;
   /* 0x0040 */ GammaTransferParams gammaDecodeParams;
   /* 0x0060 */ GammaTransferParams gammaEncodeParams;
diff --git a/test/tint/builtins/gen/var/arrayLength/1588cd.wgsl.expected.msl b/test/tint/builtins/gen/var/arrayLength/1588cd.wgsl.expected.msl
index 1f8ac29..8f9b209 100644
--- a/test/tint/builtins/gen/var/arrayLength/1588cd.wgsl.expected.msl
+++ b/test/tint/builtins/gen/var/arrayLength/1588cd.wgsl.expected.msl
@@ -1,12 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol_1 {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 struct SB_RO {
-  int arg_0[1];
+  tint_array<int, 1> arg_0;
 };
 
 void arrayLength_1588cd(const constant tint_symbol_1* const tint_symbol_3) {
diff --git a/test/tint/builtins/gen/var/arrayLength/61b1c7.wgsl.expected.msl b/test/tint/builtins/gen/var/arrayLength/61b1c7.wgsl.expected.msl
index 9fa02f3..a0b7187 100644
--- a/test/tint/builtins/gen/var/arrayLength/61b1c7.wgsl.expected.msl
+++ b/test/tint/builtins/gen/var/arrayLength/61b1c7.wgsl.expected.msl
@@ -1,12 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol_1 {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 struct SB_RW {
-  int arg_0[1];
+  tint_array<int, 1> arg_0;
 };
 
 void arrayLength_61b1c7(const constant tint_symbol_1* const tint_symbol_3) {
diff --git a/test/tint/builtins/gen/var/arrayLength/a0f5ca.wgsl.expected.msl b/test/tint/builtins/gen/var/arrayLength/a0f5ca.wgsl.expected.msl
index f1b7785..b0aba71 100644
--- a/test/tint/builtins/gen/var/arrayLength/a0f5ca.wgsl.expected.msl
+++ b/test/tint/builtins/gen/var/arrayLength/a0f5ca.wgsl.expected.msl
@@ -1,12 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol_1 {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 struct SB_RO {
-  float arg_0[1];
+  tint_array<float, 1> arg_0;
 };
 
 void arrayLength_a0f5ca(const constant tint_symbol_1* const tint_symbol_3) {
diff --git a/test/tint/builtins/gen/var/arrayLength/cdd123.wgsl.expected.msl b/test/tint/builtins/gen/var/arrayLength/cdd123.wgsl.expected.msl
index 22cfa6c..b7fe50b 100644
--- a/test/tint/builtins/gen/var/arrayLength/cdd123.wgsl.expected.msl
+++ b/test/tint/builtins/gen/var/arrayLength/cdd123.wgsl.expected.msl
@@ -1,12 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol_1 {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 struct SB_RW {
-  float arg_0[1];
+  tint_array<float, 1> arg_0;
 };
 
 void arrayLength_cdd123(const constant tint_symbol_1* const tint_symbol_3) {
diff --git a/test/tint/builtins/gen/var/arrayLength/cfca0a.wgsl.expected.msl b/test/tint/builtins/gen/var/arrayLength/cfca0a.wgsl.expected.msl
index d64c1e0..f62106e 100644
--- a/test/tint/builtins/gen/var/arrayLength/cfca0a.wgsl.expected.msl
+++ b/test/tint/builtins/gen/var/arrayLength/cfca0a.wgsl.expected.msl
@@ -1,12 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol_1 {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 struct SB_RO {
-  uint arg_0[1];
+  tint_array<uint, 1> arg_0;
 };
 
 void arrayLength_cfca0a(const constant tint_symbol_1* const tint_symbol_3) {
diff --git a/test/tint/builtins/gen/var/arrayLength/eb510f.wgsl.expected.msl b/test/tint/builtins/gen/var/arrayLength/eb510f.wgsl.expected.msl
index 4412853..05c3bc9 100644
--- a/test/tint/builtins/gen/var/arrayLength/eb510f.wgsl.expected.msl
+++ b/test/tint/builtins/gen/var/arrayLength/eb510f.wgsl.expected.msl
@@ -1,12 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol_1 {
-  /* 0x0000 */ uint4 buffer_size[1];
+  /* 0x0000 */ tint_array<uint4, 1> buffer_size;
 };
 
 struct SB_RW {
-  uint arg_0[1];
+  tint_array<uint, 1> arg_0;
 };
 
 void arrayLength_eb510f(const constant tint_symbol_1* const tint_symbol_3) {
diff --git a/test/tint/builtins/gen/var/textureLoad/8acf41.wgsl.expected.msl b/test/tint/builtins/gen/var/textureLoad/8acf41.wgsl.expected.msl
index 5f8b2c3..d6fff77 100644
--- a/test/tint/builtins/gen/var/textureLoad/8acf41.wgsl.expected.msl
+++ b/test/tint/builtins/gen/var/textureLoad/8acf41.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct GammaTransferParams {
   /* 0x0000 */ float G;
   /* 0x0004 */ float A;
@@ -15,7 +28,7 @@
 struct ExternalTextureParams {
   /* 0x0000 */ uint numPlanes;
   /* 0x0004 */ uint doYuvToRgbConversionOnly;
-  /* 0x0008 */ int8_t tint_pad[8];
+  /* 0x0008 */ tint_array<int8_t, 8> tint_pad;
   /* 0x0010 */ float3x4 yuvToRgbConversionMatrix;
   /* 0x0040 */ GammaTransferParams gammaDecodeParams;
   /* 0x0060 */ GammaTransferParams gammaEncodeParams;
diff --git a/test/tint/builtins/gen/var/textureSampleLevel/979816.wgsl.expected.msl b/test/tint/builtins/gen/var/textureSampleLevel/979816.wgsl.expected.msl
index a84758c..9622213 100644
--- a/test/tint/builtins/gen/var/textureSampleLevel/979816.wgsl.expected.msl
+++ b/test/tint/builtins/gen/var/textureSampleLevel/979816.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct GammaTransferParams {
   /* 0x0000 */ float G;
   /* 0x0004 */ float A;
@@ -15,7 +28,7 @@
 struct ExternalTextureParams {
   /* 0x0000 */ uint numPlanes;
   /* 0x0004 */ uint doYuvToRgbConversionOnly;
-  /* 0x0008 */ int8_t tint_pad[8];
+  /* 0x0008 */ tint_array<int8_t, 8> tint_pad;
   /* 0x0010 */ float3x4 yuvToRgbConversionMatrix;
   /* 0x0040 */ GammaTransferParams gammaDecodeParams;
   /* 0x0060 */ GammaTransferParams gammaEncodeParams;
diff --git a/test/tint/builtins/textureLoad/texture_external_param.wgsl.expected.msl b/test/tint/builtins/textureLoad/texture_external_param.wgsl.expected.msl
index a7d71cc..2afc801 100644
--- a/test/tint/builtins/textureLoad/texture_external_param.wgsl.expected.msl
+++ b/test/tint/builtins/textureLoad/texture_external_param.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct GammaTransferParams {
   /* 0x0000 */ float G;
   /* 0x0004 */ float A;
@@ -15,7 +28,7 @@
 struct ExternalTextureParams {
   /* 0x0000 */ uint numPlanes;
   /* 0x0004 */ uint doYuvToRgbConversionOnly;
-  /* 0x0008 */ int8_t tint_pad[8];
+  /* 0x0008 */ tint_array<int8_t, 8> tint_pad;
   /* 0x0010 */ float3x4 yuvToRgbConversionMatrix;
   /* 0x0040 */ GammaTransferParams gammaDecodeParams;
   /* 0x0060 */ GammaTransferParams gammaEncodeParams;
diff --git a/test/tint/expressions/binary/mul/mat3x2-vec3/f32.wgsl.expected.msl b/test/tint/expressions/binary/mul/mat3x2-vec3/f32.wgsl.expected.msl
index 31add65..e210c3c 100644
--- a/test/tint/expressions/binary/mul/mat3x2-vec3/f32.wgsl.expected.msl
+++ b/test/tint/expressions/binary/mul/mat3x2-vec3/f32.wgsl.expected.msl
@@ -2,6 +2,18 @@
 
 using namespace metal;
 
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 template<typename T, int N, int M>
 inline vec<T, M> operator*(matrix<T, N, M> lhs, packed_vec<T, N> rhs) {
   return lhs * vec<T, N>(rhs);
@@ -14,9 +26,9 @@
 
 struct S {
   /* 0x0000 */ float3x2 tint_symbol;
-  /* 0x0018 */ int8_t tint_pad[8];
+  /* 0x0018 */ tint_array<int8_t, 8> tint_pad;
   /* 0x0020 */ packed_float3 vector;
-  /* 0x002c */ int8_t tint_pad_1[4];
+  /* 0x002c */ tint_array<int8_t, 4> tint_pad_1;
 };
 
 fragment void tint_symbol_1(const constant S* tint_symbol_2 [[buffer(0)]]) {
diff --git a/test/tint/expressions/binary/mul/mat3x3-vec3/f32.wgsl.expected.msl b/test/tint/expressions/binary/mul/mat3x3-vec3/f32.wgsl.expected.msl
index 40ec533..8f4ff55 100644
--- a/test/tint/expressions/binary/mul/mat3x3-vec3/f32.wgsl.expected.msl
+++ b/test/tint/expressions/binary/mul/mat3x3-vec3/f32.wgsl.expected.msl
@@ -12,10 +12,22 @@
   return vec<T, M>(lhs) * rhs;
 }
 
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct S {
   /* 0x0000 */ float3x3 tint_symbol;
   /* 0x0030 */ packed_float3 vector;
-  /* 0x003c */ int8_t tint_pad[4];
+  /* 0x003c */ tint_array<int8_t, 4> tint_pad;
 };
 
 fragment void tint_symbol_1(const constant S* tint_symbol_2 [[buffer(0)]]) {
diff --git a/test/tint/expressions/binary/mul/vec3-mat3x3/f32.wgsl.expected.msl b/test/tint/expressions/binary/mul/vec3-mat3x3/f32.wgsl.expected.msl
index a5da864..d9fc80b 100644
--- a/test/tint/expressions/binary/mul/vec3-mat3x3/f32.wgsl.expected.msl
+++ b/test/tint/expressions/binary/mul/vec3-mat3x3/f32.wgsl.expected.msl
@@ -12,10 +12,22 @@
   return vec<T, M>(lhs) * rhs;
 }
 
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct S {
   /* 0x0000 */ float3x3 tint_symbol;
   /* 0x0030 */ packed_float3 vector;
-  /* 0x003c */ int8_t tint_pad[4];
+  /* 0x003c */ tint_array<int8_t, 4> tint_pad;
 };
 
 fragment void tint_symbol_1(const constant S* tint_symbol_2 [[buffer(0)]]) {
diff --git a/test/tint/expressions/binary/mul/vec3-mat4x3/f32.wgsl.expected.msl b/test/tint/expressions/binary/mul/vec3-mat4x3/f32.wgsl.expected.msl
index 36412ba..3f66659 100644
--- a/test/tint/expressions/binary/mul/vec3-mat4x3/f32.wgsl.expected.msl
+++ b/test/tint/expressions/binary/mul/vec3-mat4x3/f32.wgsl.expected.msl
@@ -12,10 +12,22 @@
   return vec<T, M>(lhs) * rhs;
 }
 
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct S {
   /* 0x0000 */ float4x3 tint_symbol;
   /* 0x0040 */ packed_float3 vector;
-  /* 0x004c */ int8_t tint_pad[4];
+  /* 0x004c */ tint_array<int8_t, 4> tint_pad;
 };
 
 fragment void tint_symbol_1(const constant S* tint_symbol_2 [[buffer(0)]]) {
diff --git a/test/tint/expressions/index/let/let/literal/array.wgsl.expected.msl b/test/tint/expressions/index/let/let/literal/array.wgsl.expected.msl
index 6a9487f..c5f5865 100644
--- a/test/tint/expressions/index/let/let/literal/array.wgsl.expected.msl
+++ b/test/tint/expressions/index/let/let/literal/array.wgsl.expected.msl
@@ -1,13 +1,22 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  int arr[8];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 int f() {
-  tint_array_wrapper const a = {.arr={1, 2, 3, 4, 5, 6, 7, 8}};
+  tint_array<int, 8> const a = tint_array<int, 8>{1, 2, 3, 4, 5, 6, 7, 8};
   int const i = 1;
-  return a.arr[1];
+  return 2;
 }
 
diff --git a/test/tint/expressions/index/let/let/param/array.wgsl.expected.msl b/test/tint/expressions/index/let/let/param/array.wgsl.expected.msl
index 7b941e4..11906e9 100644
--- a/test/tint/expressions/index/let/let/param/array.wgsl.expected.msl
+++ b/test/tint/expressions/index/let/let/param/array.wgsl.expected.msl
@@ -1,13 +1,22 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  int arr[8];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 int f(int x) {
-  tint_array_wrapper const a = {.arr={1, 2, 3, 4, 5, 6, 7, 8}};
+  tint_array<int, 8> const a = tint_array<int, 8>{1, 2, 3, 4, 5, 6, 7, 8};
   int const i = x;
-  return a.arr[i];
+  return a[i];
 }
 
diff --git a/test/tint/expressions/index/let/literal/array.wgsl.expected.msl b/test/tint/expressions/index/let/literal/array.wgsl.expected.msl
index d4d28b1..e0a4066 100644
--- a/test/tint/expressions/index/let/literal/array.wgsl.expected.msl
+++ b/test/tint/expressions/index/let/literal/array.wgsl.expected.msl
@@ -1,12 +1,21 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  int arr[8];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 int f() {
-  tint_array_wrapper const a = {.arr={1, 2, 3, 4, 5, 6, 7, 8}};
-  return a.arr[1];
+  tint_array<int, 8> const a = tint_array<int, 8>{1, 2, 3, 4, 5, 6, 7, 8};
+  return 2;
 }
 
diff --git a/test/tint/expressions/index/let/param/array.wgsl.expected.msl b/test/tint/expressions/index/let/param/array.wgsl.expected.msl
index eac5c42..d3bbdae 100644
--- a/test/tint/expressions/index/let/param/array.wgsl.expected.msl
+++ b/test/tint/expressions/index/let/param/array.wgsl.expected.msl
@@ -1,12 +1,21 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  int arr[8];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 int f(int i) {
-  tint_array_wrapper const a = {.arr={1, 2, 3, 4, 5, 6, 7, 8}};
-  return a.arr[i];
+  tint_array<int, 8> const a = tint_array<int, 8>{1, 2, 3, 4, 5, 6, 7, 8};
+  return a[i];
 }
 
diff --git a/test/tint/expressions/index/let/var/literal/array.wgsl.expected.msl b/test/tint/expressions/index/let/var/literal/array.wgsl.expected.msl
index 30f40e9..74fc40d 100644
--- a/test/tint/expressions/index/let/var/literal/array.wgsl.expected.msl
+++ b/test/tint/expressions/index/let/var/literal/array.wgsl.expected.msl
@@ -1,13 +1,22 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  int arr[8];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 int f() {
-  tint_array_wrapper a = {.arr={1, 2, 3, 4, 5, 6, 7, 8}};
+  tint_array<int, 8> a = tint_array<int, 8>{1, 2, 3, 4, 5, 6, 7, 8};
   int const i = 1;
-  return a.arr[1];
+  return a[1];
 }
 
diff --git a/test/tint/expressions/index/var/let/literal/array.wgsl.expected.msl b/test/tint/expressions/index/var/let/literal/array.wgsl.expected.msl
index 30f40e9..74fc40d 100644
--- a/test/tint/expressions/index/var/let/literal/array.wgsl.expected.msl
+++ b/test/tint/expressions/index/var/let/literal/array.wgsl.expected.msl
@@ -1,13 +1,22 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  int arr[8];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 int f() {
-  tint_array_wrapper a = {.arr={1, 2, 3, 4, 5, 6, 7, 8}};
+  tint_array<int, 8> a = tint_array<int, 8>{1, 2, 3, 4, 5, 6, 7, 8};
   int const i = 1;
-  return a.arr[1];
+  return a[1];
 }
 
diff --git a/test/tint/expressions/index/var/let/param/array.wgsl.expected.msl b/test/tint/expressions/index/var/let/param/array.wgsl.expected.msl
index b1e207d..970f617 100644
--- a/test/tint/expressions/index/var/let/param/array.wgsl.expected.msl
+++ b/test/tint/expressions/index/var/let/param/array.wgsl.expected.msl
@@ -1,13 +1,22 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  int arr[8];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 int f(int x) {
-  tint_array_wrapper a = {.arr={1, 2, 3, 4, 5, 6, 7, 8}};
+  tint_array<int, 8> a = tint_array<int, 8>{1, 2, 3, 4, 5, 6, 7, 8};
   int const i = x;
-  return a.arr[i];
+  return a[i];
 }
 
diff --git a/test/tint/expressions/index/var/literal/array.wgsl.expected.msl b/test/tint/expressions/index/var/literal/array.wgsl.expected.msl
index ae2d9ca..5cc2f07 100644
--- a/test/tint/expressions/index/var/literal/array.wgsl.expected.msl
+++ b/test/tint/expressions/index/var/literal/array.wgsl.expected.msl
@@ -1,12 +1,21 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  int arr[8];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 int f() {
-  tint_array_wrapper a = {.arr={1, 2, 3, 4, 5, 6, 7, 8}};
-  return a.arr[1];
+  tint_array<int, 8> a = tint_array<int, 8>{1, 2, 3, 4, 5, 6, 7, 8};
+  return a[1];
 }
 
diff --git a/test/tint/expressions/index/var/param/array.wgsl.expected.msl b/test/tint/expressions/index/var/param/array.wgsl.expected.msl
index 9d03b80..918a63b 100644
--- a/test/tint/expressions/index/var/param/array.wgsl.expected.msl
+++ b/test/tint/expressions/index/var/param/array.wgsl.expected.msl
@@ -1,12 +1,21 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  int arr[8];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 int f(int i) {
-  tint_array_wrapper a = {.arr={1, 2, 3, 4, 5, 6, 7, 8}};
-  return a.arr[i];
+  tint_array<int, 8> a = tint_array<int, 8>{1, 2, 3, 4, 5, 6, 7, 8};
+  return a[i];
 }
 
diff --git a/test/tint/expressions/swizzle/read/packed_vec3/f32.wgsl.expected.msl b/test/tint/expressions/swizzle/read/packed_vec3/f32.wgsl.expected.msl
index 9f40be1..e2bbca3 100644
--- a/test/tint/expressions/swizzle/read/packed_vec3/f32.wgsl.expected.msl
+++ b/test/tint/expressions/swizzle/read/packed_vec3/f32.wgsl.expected.msl
@@ -12,9 +12,21 @@
   return vec<T, M>(lhs) * rhs;
 }
 
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct S {
   /* 0x0000 */ packed_float3 v;
-  /* 0x000c */ int8_t tint_pad[4];
+  /* 0x000c */ tint_array<int8_t, 4> tint_pad;
 };
 
 void f(const constant S* const tint_symbol) {
diff --git a/test/tint/expressions/swizzle/read/packed_vec3/i32.wgsl.expected.msl b/test/tint/expressions/swizzle/read/packed_vec3/i32.wgsl.expected.msl
index d396d1f..7a93a59 100644
--- a/test/tint/expressions/swizzle/read/packed_vec3/i32.wgsl.expected.msl
+++ b/test/tint/expressions/swizzle/read/packed_vec3/i32.wgsl.expected.msl
@@ -1,9 +1,22 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct S {
   /* 0x0000 */ packed_int3 v;
-  /* 0x000c */ int8_t tint_pad[4];
+  /* 0x000c */ tint_array<int8_t, 4> tint_pad;
 };
 
 void f(const constant S* const tint_symbol) {
diff --git a/test/tint/expressions/swizzle/read/packed_vec3/u32.wgsl.expected.msl b/test/tint/expressions/swizzle/read/packed_vec3/u32.wgsl.expected.msl
index 8f6a4a6..7444f75 100644
--- a/test/tint/expressions/swizzle/read/packed_vec3/u32.wgsl.expected.msl
+++ b/test/tint/expressions/swizzle/read/packed_vec3/u32.wgsl.expected.msl
@@ -1,9 +1,22 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct S {
   /* 0x0000 */ packed_uint3 v;
-  /* 0x000c */ int8_t tint_pad[4];
+  /* 0x000c */ tint_array<int8_t, 4> tint_pad;
 };
 
 void f(const constant S* const tint_symbol) {
diff --git a/test/tint/expressions/swizzle/write/packed_vec3/f32.wgsl.expected.msl b/test/tint/expressions/swizzle/write/packed_vec3/f32.wgsl.expected.msl
index 2e63ec7..7599cbc 100644
--- a/test/tint/expressions/swizzle/write/packed_vec3/f32.wgsl.expected.msl
+++ b/test/tint/expressions/swizzle/write/packed_vec3/f32.wgsl.expected.msl
@@ -12,9 +12,21 @@
   return vec<T, M>(lhs) * rhs;
 }
 
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct S {
   /* 0x0000 */ packed_float3 v;
-  /* 0x000c */ int8_t tint_pad[4];
+  /* 0x000c */ tint_array<int8_t, 4> tint_pad;
 };
 
 void f(device S* const tint_symbol) {
diff --git a/test/tint/expressions/swizzle/write/packed_vec3/i32.wgsl.expected.msl b/test/tint/expressions/swizzle/write/packed_vec3/i32.wgsl.expected.msl
index 2bcecc1..06cb457 100644
--- a/test/tint/expressions/swizzle/write/packed_vec3/i32.wgsl.expected.msl
+++ b/test/tint/expressions/swizzle/write/packed_vec3/i32.wgsl.expected.msl
@@ -1,9 +1,22 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct S {
   /* 0x0000 */ packed_int3 v;
-  /* 0x000c */ int8_t tint_pad[4];
+  /* 0x000c */ tint_array<int8_t, 4> tint_pad;
 };
 
 void f(device S* const tint_symbol) {
diff --git a/test/tint/expressions/swizzle/write/packed_vec3/u32.wgsl.expected.msl b/test/tint/expressions/swizzle/write/packed_vec3/u32.wgsl.expected.msl
index b55912d..e8e4904 100644
--- a/test/tint/expressions/swizzle/write/packed_vec3/u32.wgsl.expected.msl
+++ b/test/tint/expressions/swizzle/write/packed_vec3/u32.wgsl.expected.msl
@@ -1,9 +1,22 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct S {
   /* 0x0000 */ packed_uint3 v;
-  /* 0x000c */ int8_t tint_pad[4];
+  /* 0x000c */ tint_array<int8_t, 4> tint_pad;
 };
 
 void f(device S* const tint_symbol) {
diff --git a/test/tint/expressions/zero_init/array/bool.wgsl.expected.msl b/test/tint/expressions/zero_init/array/bool.wgsl.expected.msl
index ead82c7..8b8b451 100644
--- a/test/tint/expressions/zero_init/array/bool.wgsl.expected.msl
+++ b/test/tint/expressions/zero_init/array/bool.wgsl.expected.msl
@@ -1,11 +1,20 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  bool arr[4];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 void f() {
-  tint_array_wrapper v = {.arr={}};
+  tint_array<bool, 4> v = tint_array<bool, 4>{};
 }
 
diff --git a/test/tint/expressions/zero_init/array/f32.wgsl.expected.msl b/test/tint/expressions/zero_init/array/f32.wgsl.expected.msl
index 7a8df6c..60e142d 100644
--- a/test/tint/expressions/zero_init/array/f32.wgsl.expected.msl
+++ b/test/tint/expressions/zero_init/array/f32.wgsl.expected.msl
@@ -1,11 +1,20 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  float arr[4];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 void f() {
-  tint_array_wrapper v = {.arr={}};
+  tint_array<float, 4> v = tint_array<float, 4>{};
 }
 
diff --git a/test/tint/expressions/zero_init/array/i32.wgsl.expected.msl b/test/tint/expressions/zero_init/array/i32.wgsl.expected.msl
index e8cd266..42ecab7 100644
--- a/test/tint/expressions/zero_init/array/i32.wgsl.expected.msl
+++ b/test/tint/expressions/zero_init/array/i32.wgsl.expected.msl
@@ -1,11 +1,20 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  int arr[4];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 void f() {
-  tint_array_wrapper v = {.arr={}};
+  tint_array<int, 4> v = tint_array<int, 4>{};
 }
 
diff --git a/test/tint/expressions/zero_init/array/struct.wgsl.expected.msl b/test/tint/expressions/zero_init/array/struct.wgsl.expected.msl
index 79d7fd7..5101891 100644
--- a/test/tint/expressions/zero_init/array/struct.wgsl.expected.msl
+++ b/test/tint/expressions/zero_init/array/struct.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct S {
   int i;
   uint u;
@@ -8,11 +21,7 @@
   bool b;
 };
 
-struct tint_array_wrapper {
-  S arr[4];
-};
-
 void f() {
-  tint_array_wrapper v = {.arr={}};
+  tint_array<S, 4> v = tint_array<S, 4>{};
 }
 
diff --git a/test/tint/expressions/zero_init/array/u32.wgsl.expected.msl b/test/tint/expressions/zero_init/array/u32.wgsl.expected.msl
index 83dceb0..b2f7135 100644
--- a/test/tint/expressions/zero_init/array/u32.wgsl.expected.msl
+++ b/test/tint/expressions/zero_init/array/u32.wgsl.expected.msl
@@ -1,11 +1,20 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  uint arr[4];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 void f() {
-  tint_array_wrapper v = {.arr={}};
+  tint_array<uint, 4> v = tint_array<uint, 4>{};
 }
 
diff --git a/test/tint/expressions/zero_init/struct/array.wgsl.expected.msl b/test/tint/expressions/zero_init/struct/array.wgsl.expected.msl
index 2600536..ec53d10 100644
--- a/test/tint/expressions/zero_init/struct/array.wgsl.expected.msl
+++ b/test/tint/expressions/zero_init/struct/array.wgsl.expected.msl
@@ -1,12 +1,21 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  float arr[4];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 struct S {
-  tint_array_wrapper a;
+  tint_array<float, 4> a;
 };
 
 void f() {
diff --git a/test/tint/layout/storage/mat2x2/stride/16.spvasm.expected.msl b/test/tint/layout/storage/mat2x2/stride/16.spvasm.expected.msl
index abb676b..860459d 100644
--- a/test/tint/layout/storage/mat2x2/stride/16.spvasm.expected.msl
+++ b/test/tint/layout/storage/mat2x2/stride/16.spvasm.expected.msl
@@ -1,33 +1,42 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct strided_arr {
-  /* 0x0000 */ float2 el;
-  /* 0x0008 */ int8_t tint_pad[8];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
-struct tint_array_wrapper {
-  /* 0x0000 */ strided_arr arr[2];
+struct strided_arr {
+  /* 0x0000 */ float2 el;
+  /* 0x0008 */ tint_array<int8_t, 8> tint_pad;
 };
 
 struct SSBO {
-  /* 0x0000 */ tint_array_wrapper m;
+  /* 0x0000 */ tint_array<strided_arr, 2> m;
 };
 
-float2x2 arr_to_mat2x2_stride_16(tint_array_wrapper arr) {
-  return float2x2(arr.arr[0u].el, arr.arr[1u].el);
+float2x2 arr_to_mat2x2_stride_16(tint_array<strided_arr, 2> arr) {
+  return float2x2(arr[0u].el, arr[1u].el);
 }
 
-tint_array_wrapper mat2x2_stride_16_to_arr(float2x2 m) {
+tint_array<strided_arr, 2> mat2x2_stride_16_to_arr(float2x2 m) {
   strided_arr const tint_symbol_1 = {.el=m[0u]};
   strided_arr const tint_symbol_2 = {.el=m[1u]};
-  tint_array_wrapper const tint_symbol_3 = {.arr={tint_symbol_1, tint_symbol_2}};
+  tint_array<strided_arr, 2> const tint_symbol_3 = tint_array<strided_arr, 2>{tint_symbol_1, tint_symbol_2};
   return tint_symbol_3;
 }
 
 void f_1(device SSBO* const tint_symbol_4) {
   float2x2 const x_15 = arr_to_mat2x2_stride_16((*(tint_symbol_4)).m);
-  tint_array_wrapper const tint_symbol = mat2x2_stride_16_to_arr(x_15);
+  tint_array<strided_arr, 2> const tint_symbol = mat2x2_stride_16_to_arr(x_15);
   (*(tint_symbol_4)).m = tint_symbol;
   return;
 }
diff --git a/test/tint/let/global/global.wgsl.expected.msl b/test/tint/let/global/global.wgsl.expected.msl
index 72d5df7..d4ded52 100644
--- a/test/tint/let/global/global.wgsl.expected.msl
+++ b/test/tint/let/global/global.wgsl.expected.msl
@@ -1,12 +1,21 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct MyStruct {
-  float f1;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
-struct tint_array_wrapper {
-  float arr[10];
+struct MyStruct {
+  float f1;
 };
 
 constant int v1 = 1;
@@ -25,7 +34,7 @@
 
 constant MyStruct v8 = {};
 
-constant tint_array_wrapper v9 = {.arr={}};
+constant tint_array<float, 10> v9 = tint_array<float, 10>{};
 
 struct tint_symbol_1 {
   float4 value [[color(0)]];
diff --git a/test/tint/let/inferred/function.wgsl.expected.msl b/test/tint/let/inferred/function.wgsl.expected.msl
index 9d93fcf..604c979 100644
--- a/test/tint/let/inferred/function.wgsl.expected.msl
+++ b/test/tint/let/inferred/function.wgsl.expected.msl
@@ -1,12 +1,21 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct MyStruct {
-  float f1;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
-struct tint_array_wrapper {
-  float arr[10];
+struct MyStruct {
+  float f1;
 };
 
 int ret_i32() {
@@ -26,8 +35,8 @@
   return tint_symbol_2;
 }
 
-tint_array_wrapper ret_MyArray() {
-  tint_array_wrapper const tint_symbol_3 = {.arr={}};
+tint_array<float, 10> ret_MyArray() {
+  tint_array<float, 10> const tint_symbol_3 = tint_array<float, 10>{};
   return tint_symbol_3;
 }
 
@@ -40,13 +49,13 @@
   float3 const v6 = float3(1.0f);
   float3x3 const v7 = float3x3(float3(1.0f), float3(1.0f), float3(1.0f));
   MyStruct const v8 = {.f1=1.0f};
-  tint_array_wrapper const v9 = {.arr={}};
+  tint_array<float, 10> const v9 = tint_array<float, 10>{};
   int const v10 = ret_i32();
   uint const v11 = ret_u32();
   float const v12 = ret_f32();
   MyStruct const v13 = ret_MyStruct();
   MyStruct const v14 = ret_MyStruct();
-  tint_array_wrapper const v15 = ret_MyArray();
+  tint_array<float, 10> const v15 = ret_MyArray();
 }
 
 struct tint_symbol_1 {
diff --git a/test/tint/out_of_order_decls/array/alias.wgsl.expected.msl b/test/tint/out_of_order_decls/array/alias.wgsl.expected.msl
index e345a6d..3baeea6 100644
--- a/test/tint/out_of_order_decls/array/alias.wgsl.expected.msl
+++ b/test/tint/out_of_order_decls/array/alias.wgsl.expected.msl
@@ -1,13 +1,22 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  int arr[4];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 fragment void f() {
-  thread tint_array_wrapper tint_symbol = {};
-  tint_symbol.arr[0] = 1;
+  thread tint_array<int, 4> tint_symbol = {};
+  tint_symbol[0] = 1;
   return;
 }
 
diff --git a/test/tint/out_of_order_decls/array/struct.wgsl.expected.msl b/test/tint/out_of_order_decls/array/struct.wgsl.expected.msl
index 4e91e2e..98b6893 100644
--- a/test/tint/out_of_order_decls/array/struct.wgsl.expected.msl
+++ b/test/tint/out_of_order_decls/array/struct.wgsl.expected.msl
@@ -1,18 +1,27 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct S {
   int m;
 };
 
-struct tint_array_wrapper {
-  S arr[4];
-};
-
 fragment void f() {
-  thread tint_array_wrapper tint_symbol_1 = {};
+  thread tint_array<S, 4> tint_symbol_1 = {};
   S const tint_symbol = {.m=1};
-  tint_symbol_1.arr[0] = tint_symbol;
+  tint_symbol_1[0] = tint_symbol;
   return;
 }
 
diff --git a/test/tint/samples/compute_boids.wgsl.expected.msl b/test/tint/samples/compute_boids.wgsl.expected.msl
index cf438ab..a3cb32a 100644
--- a/test/tint/samples/compute_boids.wgsl.expected.msl
+++ b/test/tint/samples/compute_boids.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct tint_symbol_1 {
   float2 a_particlePos [[attribute(0)]];
   float2 a_particleVel [[attribute(1)]];
@@ -54,12 +67,8 @@
   /* 0x0018 */ float rule3Scale;
 };
 
-struct tint_array_wrapper {
-  /* 0x0000 */ Particle arr[5];
-};
-
 struct Particles {
-  /* 0x0000 */ tint_array_wrapper particles;
+  /* 0x0000 */ tint_array<Particle, 5> particles;
 };
 
 void comp_main_inner(uint3 gl_GlobalInvocationID, device Particles* const tint_symbol_4, const constant SimParams* const tint_symbol_5, device Particles* const tint_symbol_6) {
@@ -67,8 +76,8 @@
   if ((index >= 5u)) {
     return;
   }
-  float2 vPos = (*(tint_symbol_4)).particles.arr[index].pos;
-  float2 vVel = (*(tint_symbol_4)).particles.arr[index].vel;
+  float2 vPos = (*(tint_symbol_4)).particles[index].pos;
+  float2 vVel = (*(tint_symbol_4)).particles[index].vel;
   float2 cMass = float2(0.0f);
   float2 cVel = float2(0.0f);
   float2 colVel = float2(0.0f);
@@ -80,8 +89,8 @@
     if ((i == index)) {
       continue;
     }
-    pos = float2((*(tint_symbol_4)).particles.arr[i].pos).xy;
-    vel = float2((*(tint_symbol_4)).particles.arr[i].vel).xy;
+    pos = float2((*(tint_symbol_4)).particles[i].pos).xy;
+    vel = float2((*(tint_symbol_4)).particles[i].vel).xy;
     if ((distance(pos, vPos) < (*(tint_symbol_5)).rule1Distance)) {
       cMass = (cMass + pos);
       cMassCount = as_type<int>((as_type<uint>(cMassCount) + as_type<uint>(1)));
@@ -115,8 +124,8 @@
   if ((vPos[1] > 1.0f)) {
     vPos[1] = -1.0f;
   }
-  (*(tint_symbol_6)).particles.arr[index].pos = vPos;
-  (*(tint_symbol_6)).particles.arr[index].vel = vVel;
+  (*(tint_symbol_6)).particles[index].pos = vPos;
+  (*(tint_symbol_6)).particles[index].vel = vVel;
 }
 
 kernel void comp_main(device Particles* tint_symbol_7 [[buffer(1)]], const constant SimParams* tint_symbol_8 [[buffer(0)]], device Particles* tint_symbol_9 [[buffer(2)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) {
diff --git a/test/tint/samples/triangle.wgsl.expected.msl b/test/tint/samples/triangle.wgsl.expected.msl
index 51fb847..664ec4e 100644
--- a/test/tint/samples/triangle.wgsl.expected.msl
+++ b/test/tint/samples/triangle.wgsl.expected.msl
@@ -1,18 +1,27 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  float2 arr[3];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
-constant tint_array_wrapper pos = {.arr={float2(0.0f, 0.5f), float2(-0.5f), float2(0.5f, -0.5f)}};
+constant tint_array<float2, 3> pos = tint_array<float2, 3>{float2(0.0f, 0.5f), float2(-0.5f), float2(0.5f, -0.5f)};
 
 struct tint_symbol {
   float4 value [[position]];
 };
 
 float4 vtx_main_inner(uint VertexIndex) {
-  return float4(pos.arr[VertexIndex], 0.0f, 1.0f);
+  return float4(pos[VertexIndex], 0.0f, 1.0f);
 }
 
 vertex tint_symbol vtx_main(uint VertexIndex [[vertex_id]]) {
diff --git a/test/tint/shader_io/shared_struct_storage_buffer.wgsl.expected.msl b/test/tint/shader_io/shared_struct_storage_buffer.wgsl.expected.msl
index 7b765f6..b086e52 100644
--- a/test/tint/shader_io/shared_struct_storage_buffer.wgsl.expected.msl
+++ b/test/tint/shader_io/shared_struct_storage_buffer.wgsl.expected.msl
@@ -1,12 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct S {
   /* 0x0000 */ float f;
   /* 0x0004 */ uint u;
-  /* 0x0008 */ int8_t tint_pad[120];
+  /* 0x0008 */ tint_array<int8_t, 120> tint_pad;
   /* 0x0080 */ float4 v;
-  /* 0x0090 */ int8_t tint_pad_1[112];
+  /* 0x0090 */ tint_array<int8_t, 112> tint_pad_1;
 };
 
 struct tint_symbol_1 {
diff --git a/test/tint/statements/assign/indexed_assign_to_array_in_struct/in_for_loop_body.wgsl.expected.msl b/test/tint/statements/assign/indexed_assign_to_array_in_struct/in_for_loop_body.wgsl.expected.msl
index acf0cc6..939ff14 100644
--- a/test/tint/statements/assign/indexed_assign_to_array_in_struct/in_for_loop_body.wgsl.expected.msl
+++ b/test/tint/statements/assign/indexed_assign_to_array_in_struct/in_for_loop_body.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Uniforms {
   /* 0x0000 */ uint i;
 };
@@ -9,19 +22,15 @@
   int v;
 };
 
-struct tint_array_wrapper {
-  InnerS arr[8];
-};
-
 struct OuterS {
-  tint_array_wrapper a1;
+  tint_array<InnerS, 8> a1;
 };
 
 kernel void tint_symbol(const constant Uniforms* tint_symbol_1 [[buffer(0)]]) {
   InnerS v = {};
   OuterS s1 = {};
   for(int i = 0; (i < 4); i = as_type<int>((as_type<uint>(i) + as_type<uint>(1)))) {
-    s1.a1.arr[(*(tint_symbol_1)).i] = v;
+    s1.a1[(*(tint_symbol_1)).i] = v;
   }
   return;
 }
diff --git a/test/tint/statements/assign/indexed_assign_to_array_in_struct/in_for_loop_continuing.wgsl.expected.msl b/test/tint/statements/assign/indexed_assign_to_array_in_struct/in_for_loop_continuing.wgsl.expected.msl
index 60c282a..76e27e9 100644
--- a/test/tint/statements/assign/indexed_assign_to_array_in_struct/in_for_loop_continuing.wgsl.expected.msl
+++ b/test/tint/statements/assign/indexed_assign_to_array_in_struct/in_for_loop_continuing.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Uniforms {
   /* 0x0000 */ uint i;
 };
@@ -9,18 +22,14 @@
   int v;
 };
 
-struct tint_array_wrapper {
-  InnerS arr[8];
-};
-
 struct OuterS {
-  tint_array_wrapper a1;
+  tint_array<InnerS, 8> a1;
 };
 
 kernel void tint_symbol(const constant Uniforms* tint_symbol_1 [[buffer(0)]]) {
   InnerS v = {};
   OuterS s1 = {};
-  for(int i = 0; (i < 4); s1.a1.arr[(*(tint_symbol_1)).i] = v) {
+  for(int i = 0; (i < 4); s1.a1[(*(tint_symbol_1)).i] = v) {
     i = as_type<int>((as_type<uint>(i) + as_type<uint>(1)));
   }
   return;
diff --git a/test/tint/statements/assign/indexed_assign_to_array_in_struct/in_for_loop_init.wgsl.expected.msl b/test/tint/statements/assign/indexed_assign_to_array_in_struct/in_for_loop_init.wgsl.expected.msl
index 8f33141..82e2101 100644
--- a/test/tint/statements/assign/indexed_assign_to_array_in_struct/in_for_loop_init.wgsl.expected.msl
+++ b/test/tint/statements/assign/indexed_assign_to_array_in_struct/in_for_loop_init.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Uniforms {
   /* 0x0000 */ uint i;
 };
@@ -9,19 +22,15 @@
   int v;
 };
 
-struct tint_array_wrapper {
-  InnerS arr[8];
-};
-
 struct OuterS {
-  tint_array_wrapper a1;
+  tint_array<InnerS, 8> a1;
 };
 
 kernel void tint_symbol(const constant Uniforms* tint_symbol_1 [[buffer(0)]]) {
   InnerS v = {};
   OuterS s1 = {};
   int i = 0;
-  for(s1.a1.arr[(*(tint_symbol_1)).i] = v; (i < 4); i = as_type<int>((as_type<uint>(i) + as_type<uint>(1)))) {
+  for(s1.a1[(*(tint_symbol_1)).i] = v; (i < 4); i = as_type<int>((as_type<uint>(i) + as_type<uint>(1)))) {
   }
   return;
 }
diff --git a/test/tint/statements/assign/indexed_assign_to_array_in_struct/indexing_with_side_effect_func.wgsl.expected.msl b/test/tint/statements/assign/indexed_assign_to_array_in_struct/indexing_with_side_effect_func.wgsl.expected.msl
index 7652337..76c3cca 100644
--- a/test/tint/statements/assign/indexed_assign_to_array_in_struct/indexing_with_side_effect_func.wgsl.expected.msl
+++ b/test/tint/statements/assign/indexed_assign_to_array_in_struct/indexing_with_side_effect_func.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Uniforms {
   /* 0x0000 */ uint i;
   /* 0x0004 */ uint j;
@@ -10,20 +23,12 @@
   int v;
 };
 
-struct tint_array_wrapper {
-  InnerS arr[8];
-};
-
 struct S1 {
-  tint_array_wrapper a2;
-};
-
-struct tint_array_wrapper_1 {
-  S1 arr[8];
+  tint_array<InnerS, 8> a2;
 };
 
 struct OuterS {
-  tint_array_wrapper_1 a1;
+  tint_array<S1, 8> a1;
 };
 
 uint getNextIndex(thread uint* const tint_symbol_3) {
@@ -37,7 +42,7 @@
   OuterS s = {};
   InnerS const tint_symbol_1 = v;
   uint const tint_symbol_2 = getNextIndex(&(tint_symbol_4));
-  s.a1.arr[tint_symbol_2].a2.arr[(*(tint_symbol_5)).j] = tint_symbol_1;
+  s.a1[tint_symbol_2].a2[(*(tint_symbol_5)).j] = tint_symbol_1;
   return;
 }
 
diff --git a/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_array.wgsl.expected.msl b/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_array.wgsl.expected.msl
index 17a3a5e..6e93e31 100644
--- a/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_array.wgsl.expected.msl
+++ b/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_array.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Uniforms {
   /* 0x0000 */ uint i;
 };
@@ -9,18 +22,14 @@
   int v;
 };
 
-struct tint_array_wrapper {
-  InnerS arr[8];
-};
-
 struct OuterS {
-  tint_array_wrapper a1;
+  tint_array<InnerS, 8> a1;
 };
 
 kernel void tint_symbol(const constant Uniforms* tint_symbol_1 [[buffer(0)]]) {
   InnerS v = {};
   OuterS s1 = {};
-  s1.a1.arr[(*(tint_symbol_1)).i] = v;
+  s1.a1[(*(tint_symbol_1)).i] = v;
   return;
 }
 
diff --git a/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_array_array.wgsl.expected.msl b/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_array_array.wgsl.expected.msl
index 12691cd..05ae52c 100644
--- a/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_array_array.wgsl.expected.msl
+++ b/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_array_array.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Uniforms {
   /* 0x0000 */ uint i;
   /* 0x0004 */ uint j;
@@ -10,22 +23,14 @@
   int v;
 };
 
-struct tint_array_wrapper_1 {
-  InnerS arr[8];
-};
-
-struct tint_array_wrapper {
-  tint_array_wrapper_1 arr[8];
-};
-
 struct OuterS {
-  tint_array_wrapper a1;
+  tint_array<tint_array<InnerS, 8>, 8> a1;
 };
 
 kernel void tint_symbol(const constant Uniforms* tint_symbol_1 [[buffer(0)]]) {
   InnerS v = {};
   OuterS s1 = {};
-  s1.a1.arr[(*(tint_symbol_1)).i].arr[(*(tint_symbol_1)).j] = v;
+  s1.a1[(*(tint_symbol_1)).i][(*(tint_symbol_1)).j] = v;
   return;
 }
 
diff --git a/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_array_struct.wgsl.expected.msl b/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_array_struct.wgsl.expected.msl
index 7686939..9ad4660 100644
--- a/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_array_struct.wgsl.expected.msl
+++ b/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_array_struct.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Uniforms {
   /* 0x0000 */ uint i;
 };
@@ -13,18 +26,14 @@
   InnerS s2;
 };
 
-struct tint_array_wrapper {
-  S1 arr[8];
-};
-
 struct OuterS {
-  tint_array_wrapper a1;
+  tint_array<S1, 8> a1;
 };
 
 kernel void tint_symbol(const constant Uniforms* tint_symbol_1 [[buffer(0)]]) {
   InnerS v = {};
   OuterS s1 = {};
-  s1.a1.arr[(*(tint_symbol_1)).i].s2 = v;
+  s1.a1[(*(tint_symbol_1)).i].s2 = v;
   return;
 }
 
diff --git a/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_array_struct_array.wgsl.expected.msl b/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_array_struct_array.wgsl.expected.msl
index a638c3f..c92d7c4 100644
--- a/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_array_struct_array.wgsl.expected.msl
+++ b/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_array_struct_array.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Uniforms {
   /* 0x0000 */ uint i;
   /* 0x0004 */ uint j;
@@ -10,26 +23,18 @@
   int v;
 };
 
-struct tint_array_wrapper {
-  InnerS arr[8];
-};
-
 struct S1 {
-  tint_array_wrapper a2;
-};
-
-struct tint_array_wrapper_1 {
-  S1 arr[8];
+  tint_array<InnerS, 8> a2;
 };
 
 struct OuterS {
-  tint_array_wrapper_1 a1;
+  tint_array<S1, 8> a1;
 };
 
 kernel void tint_symbol(const constant Uniforms* tint_symbol_1 [[buffer(0)]]) {
   InnerS v = {};
   OuterS s = {};
-  s.a1.arr[(*(tint_symbol_1)).i].a2.arr[(*(tint_symbol_1)).j] = v;
+  s.a1[(*(tint_symbol_1)).i].a2[(*(tint_symbol_1)).j] = v;
   return;
 }
 
diff --git a/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_dynamic_array.wgsl.expected.msl b/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_dynamic_array.wgsl.expected.msl
index 6d0fc32..ac9d29d 100644
--- a/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_dynamic_array.wgsl.expected.msl
+++ b/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_dynamic_array.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Uniforms {
   /* 0x0000 */ uint i;
 };
@@ -10,7 +23,7 @@
 };
 
 struct OuterS {
-  /* 0x0000 */ InnerS a1[1];
+  /* 0x0000 */ tint_array<InnerS, 1> a1;
 };
 
 kernel void tint_symbol(device OuterS* tint_symbol_1 [[buffer(1)]], const constant Uniforms* tint_symbol_2 [[buffer(0)]]) {
diff --git a/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_dynamic_array_struct_array.wgsl.expected.msl b/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_dynamic_array_struct_array.wgsl.expected.msl
index 2b23084..b9abd21 100644
--- a/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_dynamic_array_struct_array.wgsl.expected.msl
+++ b/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_dynamic_array_struct_array.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Uniforms {
   /* 0x0000 */ uint i;
   /* 0x0004 */ uint j;
@@ -10,21 +23,17 @@
   /* 0x0000 */ int v;
 };
 
-struct tint_array_wrapper {
-  /* 0x0000 */ InnerS arr[8];
-};
-
 struct S1 {
-  /* 0x0000 */ tint_array_wrapper a2;
+  /* 0x0000 */ tint_array<InnerS, 8> a2;
 };
 
 struct OuterS {
-  /* 0x0000 */ S1 a1[1];
+  /* 0x0000 */ tint_array<S1, 1> a1;
 };
 
 kernel void tint_symbol(device OuterS* tint_symbol_1 [[buffer(1)]], const constant Uniforms* tint_symbol_2 [[buffer(0)]]) {
   InnerS v = {};
-  (*(tint_symbol_1)).a1[(*(tint_symbol_2)).i].a2.arr[(*(tint_symbol_2)).j] = v;
+  (*(tint_symbol_1)).a1[(*(tint_symbol_2)).i].a2[(*(tint_symbol_2)).j] = v;
   return;
 }
 
diff --git a/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_multiple_arrays.wgsl.expected.msl b/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_multiple_arrays.wgsl.expected.msl
index 3a268a0..6bdd4e5 100644
--- a/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_multiple_arrays.wgsl.expected.msl
+++ b/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_multiple_arrays.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Uniforms {
   /* 0x0000 */ uint i;
 };
@@ -9,20 +22,16 @@
   int v;
 };
 
-struct tint_array_wrapper {
-  InnerS arr[8];
-};
-
 struct OuterS {
-  tint_array_wrapper a1;
-  tint_array_wrapper a2;
+  tint_array<InnerS, 8> a1;
+  tint_array<InnerS, 8> a2;
 };
 
 kernel void tint_symbol(const constant Uniforms* tint_symbol_1 [[buffer(0)]]) {
   InnerS v = {};
   OuterS s1 = {};
-  s1.a1.arr[(*(tint_symbol_1)).i] = v;
-  s1.a2.arr[(*(tint_symbol_1)).i] = v;
+  s1.a1[(*(tint_symbol_1)).i] = v;
+  s1.a2[(*(tint_symbol_1)).i] = v;
   return;
 }
 
diff --git a/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_struct_array.wgsl.expected.msl b/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_struct_array.wgsl.expected.msl
index 2d164b2..375695c 100644
--- a/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_struct_array.wgsl.expected.msl
+++ b/test/tint/statements/assign/indexed_assign_to_array_in_struct/struct_struct_array.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Uniforms {
   /* 0x0000 */ uint i;
 };
@@ -9,12 +22,8 @@
   int v;
 };
 
-struct tint_array_wrapper {
-  InnerS arr[8];
-};
-
 struct S1 {
-  tint_array_wrapper a;
+  tint_array<InnerS, 8> a;
 };
 
 struct OuterS {
@@ -24,7 +33,7 @@
 kernel void tint_symbol(const constant Uniforms* tint_symbol_1 [[buffer(0)]]) {
   InnerS v = {};
   OuterS s1 = {};
-  s1.s2.a.arr[(*(tint_symbol_1)).i] = v;
+  s1.s2.a[(*(tint_symbol_1)).i] = v;
   return;
 }
 
diff --git a/test/tint/statements/assign/indexed_assign_to_array_in_struct/vector_assign.wgsl.expected.msl b/test/tint/statements/assign/indexed_assign_to_array_in_struct/vector_assign.wgsl.expected.msl
index 766be46..bccd603 100644
--- a/test/tint/statements/assign/indexed_assign_to_array_in_struct/vector_assign.wgsl.expected.msl
+++ b/test/tint/statements/assign/indexed_assign_to_array_in_struct/vector_assign.wgsl.expected.msl
@@ -1,16 +1,25 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Uniforms {
   /* 0x0000 */ uint i;
 };
 
-struct tint_array_wrapper {
-  uint arr[8];
-};
-
 struct OuterS {
-  tint_array_wrapper a1;
+  tint_array<uint, 8> a1;
 };
 
 uint f(uint i) {
@@ -20,8 +29,8 @@
 kernel void tint_symbol(const constant Uniforms* tint_symbol_2 [[buffer(0)]]) {
   OuterS s1 = {};
   float3 v = 0.0f;
-  v[s1.a1.arr[(*(tint_symbol_2)).i]] = 1.0f;
-  uint const tint_symbol_1 = f(s1.a1.arr[(*(tint_symbol_2)).i]);
+  v[s1.a1[(*(tint_symbol_2)).i]] = 1.0f;
+  uint const tint_symbol_1 = f(s1.a1[(*(tint_symbol_2)).i]);
   v[tint_symbol_1] = 1.0f;
   return;
 }
diff --git a/test/tint/statements/assign/indexed_assign_to_array_in_struct/via_pointer.wgsl.expected.msl b/test/tint/statements/assign/indexed_assign_to_array_in_struct/via_pointer.wgsl.expected.msl
index 2f71f6e..573c567 100644
--- a/test/tint/statements/assign/indexed_assign_to_array_in_struct/via_pointer.wgsl.expected.msl
+++ b/test/tint/statements/assign/indexed_assign_to_array_in_struct/via_pointer.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Uniforms {
   /* 0x0000 */ uint i;
 };
@@ -9,19 +22,15 @@
   int v;
 };
 
-struct tint_array_wrapper {
-  InnerS arr[8];
-};
-
 struct OuterS {
-  tint_array_wrapper a1;
+  tint_array<InnerS, 8> a1;
 };
 
 kernel void tint_symbol(const constant Uniforms* tint_symbol_1 [[buffer(0)]]) {
   InnerS v = {};
   OuterS s1 = {};
   uint const p_save = (*(tint_symbol_1)).i;
-  s1.a1.arr[p_save] = v;
+  s1.a1[p_save] = v;
   return;
 }
 
diff --git a/test/tint/statements/assign/indexed_assign_to_array_in_struct/via_pointer_arg.wgsl.expected.msl b/test/tint/statements/assign/indexed_assign_to_array_in_struct/via_pointer_arg.wgsl.expected.msl
index 6ceb36f..9d025d3 100644
--- a/test/tint/statements/assign/indexed_assign_to_array_in_struct/via_pointer_arg.wgsl.expected.msl
+++ b/test/tint/statements/assign/indexed_assign_to_array_in_struct/via_pointer_arg.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct Uniforms {
   /* 0x0000 */ uint i;
 };
@@ -9,17 +22,13 @@
   int v;
 };
 
-struct tint_array_wrapper {
-  InnerS arr[8];
-};
-
 struct OuterS {
-  tint_array_wrapper a1;
+  tint_array<InnerS, 8> a1;
 };
 
 void f(thread OuterS* const p, const constant Uniforms* const tint_symbol_1) {
   InnerS v = {};
-  (*(p)).a1.arr[(*(tint_symbol_1)).i] = v;
+  (*(p)).a1[(*(tint_symbol_1)).i] = v;
 }
 
 kernel void tint_symbol(const constant Uniforms* tint_symbol_2 [[buffer(0)]]) {
diff --git a/test/tint/statements/assign/phony/addr_of_non_constructable.wgsl.expected.msl b/test/tint/statements/assign/phony/addr_of_non_constructable.wgsl.expected.msl
index 2f29265..90a2b8d 100644
--- a/test/tint/statements/assign/phony/addr_of_non_constructable.wgsl.expected.msl
+++ b/test/tint/statements/assign/phony/addr_of_non_constructable.wgsl.expected.msl
@@ -1,8 +1,21 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct S {
-  int arr[1];
+  tint_array<int, 1> arr;
 };
 
 kernel void tint_symbol() {
diff --git a/test/tint/statements/assign/phony/addr_of_runtime_array.wgsl.expected.msl b/test/tint/statements/assign/phony/addr_of_runtime_array.wgsl.expected.msl
index 2f29265..90a2b8d 100644
--- a/test/tint/statements/assign/phony/addr_of_runtime_array.wgsl.expected.msl
+++ b/test/tint/statements/assign/phony/addr_of_runtime_array.wgsl.expected.msl
@@ -1,8 +1,21 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct S {
-  int arr[1];
+  tint_array<int, 1> arr;
 };
 
 kernel void tint_symbol() {
diff --git a/test/tint/statements/compound_assign/complex_lhs.wgsl.expected.msl b/test/tint/statements/compound_assign/complex_lhs.wgsl.expected.msl
index 6a6d978..26fb806 100644
--- a/test/tint/statements/compound_assign/complex_lhs.wgsl.expected.msl
+++ b/test/tint/statements/compound_assign/complex_lhs.wgsl.expected.msl
@@ -1,12 +1,21 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  int4 arr[4];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 struct S {
-  tint_array_wrapper a;
+  tint_array<int4, 4> a;
 };
 
 int foo(thread int* const tint_symbol_4) {
@@ -24,6 +33,6 @@
   int const tint_symbol_3 = foo(tint_symbol_6);
   int const tint_symbol_1_save = tint_symbol_3;
   int const tint_symbol_2 = bar(tint_symbol_6);
-  x.a.arr[tint_symbol_1_save][tint_symbol_2] = as_type<int>((as_type<uint>(x.a.arr[tint_symbol_1_save][tint_symbol_2]) + as_type<uint>(5)));
+  x.a[tint_symbol_1_save][tint_symbol_2] = as_type<int>((as_type<uint>(x.a[tint_symbol_1_save][tint_symbol_2]) + as_type<uint>(5)));
 }
 
diff --git a/test/tint/statements/compound_assign/for_loop.wgsl.expected.msl b/test/tint/statements/compound_assign/for_loop.wgsl.expected.msl
index 4fb8c50..36309e9 100644
--- a/test/tint/statements/compound_assign/for_loop.wgsl.expected.msl
+++ b/test/tint/statements/compound_assign/for_loop.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct S {
   int a;
   float4 b;
@@ -22,19 +35,15 @@
   return 1;
 }
 
-struct tint_array_wrapper {
-  float arr[4];
-};
-
 void foo(thread uint* const tint_symbol_8) {
-  tint_array_wrapper a = {.arr={}};
+  tint_array<float, 4> a = tint_array<float, 4>{};
   int const tint_symbol_2 = idx1(tint_symbol_8);
   int const tint_symbol_save = tint_symbol_2;
   {
-    a.arr[tint_symbol_save] = (a.arr[tint_symbol_save] * 2.0f);
+    a[tint_symbol_save] = (a[tint_symbol_save] * 2.0f);
     while (true) {
       int const tint_symbol_3 = idx2(tint_symbol_8);
-      if (!((a.arr[tint_symbol_3] < 10.0f))) {
+      if (!((a[tint_symbol_3] < 10.0f))) {
         break;
       }
       {
@@ -42,7 +51,7 @@
       {
         int const tint_symbol_4 = idx3(tint_symbol_8);
         int const tint_symbol_1_save = tint_symbol_4;
-        a.arr[tint_symbol_1_save] = (a.arr[tint_symbol_1_save] + 1.0f);
+        a[tint_symbol_1_save] = (a[tint_symbol_1_save] + 1.0f);
       }
     }
   }
diff --git a/test/tint/statements/decrement/array_element.wgsl.expected.msl b/test/tint/statements/decrement/array_element.wgsl.expected.msl
index 03752fb..56896d1 100644
--- a/test/tint/statements/decrement/array_element.wgsl.expected.msl
+++ b/test/tint/statements/decrement/array_element.wgsl.expected.msl
@@ -1,7 +1,20 @@
 #include <metal_stdlib>
 
 using namespace metal;
-void tint_symbol(device uint (*const tint_symbol_2)[1]) {
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
+void tint_symbol(device tint_array<uint, 1>* const tint_symbol_2) {
   (*(tint_symbol_2))[1] = ((*(tint_symbol_2))[1] - 1u);
 }
 
diff --git a/test/tint/statements/decrement/complex.wgsl.expected.msl b/test/tint/statements/decrement/complex.wgsl.expected.msl
index e7e4a6c..ece89e4 100644
--- a/test/tint/statements/decrement/complex.wgsl.expected.msl
+++ b/test/tint/statements/decrement/complex.wgsl.expected.msl
@@ -1,12 +1,21 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  /* 0x0000 */ int4 arr[4];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 struct S {
-  /* 0x0000 */ tint_array_wrapper a;
+  /* 0x0000 */ tint_array<int4, 4> a;
 };
 
 int idx1(thread uint* const tint_symbol_10) {
@@ -39,14 +48,14 @@
   return 2;
 }
 
-void tint_symbol_1(thread uint* const tint_symbol_16, device S (*const tint_symbol_17)[1]) {
+void tint_symbol_1(thread uint* const tint_symbol_16, device tint_array<S, 1>* const tint_symbol_17) {
   int const tint_symbol_6 = idx1(tint_symbol_16);
   int const tint_symbol_7 = idx2(tint_symbol_16);
   int const tint_symbol_2_save = tint_symbol_6;
   int const tint_symbol_2_save_1 = tint_symbol_7;
   int const tint_symbol_3 = idx3(tint_symbol_16);
   {
-    (*(tint_symbol_17))[tint_symbol_2_save].a.arr[tint_symbol_2_save_1][tint_symbol_3] = as_type<int>((as_type<uint>((*(tint_symbol_17))[tint_symbol_2_save].a.arr[tint_symbol_2_save_1][tint_symbol_3]) - as_type<uint>(1)));
+    (*(tint_symbol_17))[tint_symbol_2_save].a[tint_symbol_2_save_1][tint_symbol_3] = as_type<int>((as_type<uint>((*(tint_symbol_17))[tint_symbol_2_save].a[tint_symbol_2_save_1][tint_symbol_3]) - as_type<uint>(1)));
     while (true) {
       if (!((*(tint_symbol_16) < 10u))) {
         break;
@@ -59,7 +68,7 @@
         int const tint_symbol_4_save = tint_symbol_8;
         int const tint_symbol_4_save_1 = tint_symbol_9;
         int const tint_symbol_5 = idx6(tint_symbol_16);
-        (*(tint_symbol_17))[tint_symbol_4_save].a.arr[tint_symbol_4_save_1][tint_symbol_5] = as_type<int>((as_type<uint>((*(tint_symbol_17))[tint_symbol_4_save].a.arr[tint_symbol_4_save_1][tint_symbol_5]) - as_type<uint>(1)));
+        (*(tint_symbol_17))[tint_symbol_4_save].a[tint_symbol_4_save_1][tint_symbol_5] = as_type<int>((as_type<uint>((*(tint_symbol_17))[tint_symbol_4_save].a[tint_symbol_4_save_1][tint_symbol_5]) - as_type<uint>(1)));
       }
     }
   }
diff --git a/test/tint/statements/for/condition/array_ctor.wgsl.expected.msl b/test/tint/statements/for/condition/array_ctor.wgsl.expected.msl
index 5aefd59..ffacf97 100644
--- a/test/tint/statements/for/condition/array_ctor.wgsl.expected.msl
+++ b/test/tint/statements/for/condition/array_ctor.wgsl.expected.msl
@@ -1,15 +1,24 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  int arr[1];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 void f() {
   int i = 0;
   while (true) {
-    tint_array_wrapper const tint_symbol = {.arr={1}};
-    if (!((i < tint_symbol.arr[0]))) {
+    tint_array<int, 1> const tint_symbol = tint_array<int, 1>{1};
+    if (!((i < 1))) {
       break;
     }
     {
diff --git a/test/tint/statements/for/continuing/array_ctor.wgsl.expected.msl b/test/tint/statements/for/continuing/array_ctor.wgsl.expected.msl
index ac6ffc2..e596e14 100644
--- a/test/tint/statements/for/continuing/array_ctor.wgsl.expected.msl
+++ b/test/tint/statements/for/continuing/array_ctor.wgsl.expected.msl
@@ -1,8 +1,17 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  int arr[1];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 void f() {
@@ -14,8 +23,8 @@
     {
     }
     {
-      tint_array_wrapper const tint_symbol = {.arr={1}};
-      i = as_type<int>((as_type<uint>(i) + as_type<uint>(tint_symbol.arr[0])));
+      tint_array<int, 1> const tint_symbol = tint_array<int, 1>{1};
+      i = as_type<int>((as_type<uint>(i) + as_type<uint>(1)));
     }
   }
 }
diff --git a/test/tint/statements/for/initializer/array_ctor.wgsl.expected.msl b/test/tint/statements/for/initializer/array_ctor.wgsl.expected.msl
index aa3d065..f729fc7 100644
--- a/test/tint/statements/for/initializer/array_ctor.wgsl.expected.msl
+++ b/test/tint/statements/for/initializer/array_ctor.wgsl.expected.msl
@@ -1,13 +1,22 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  int arr[1];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 void f() {
-  tint_array_wrapper const tint_symbol = {.arr={1}};
-  for(int i = tint_symbol.arr[0]; false; ) {
+  tint_array<int, 1> const tint_symbol = tint_array<int, 1>{1};
+  for(int i = 1; false; ) {
   }
 }
 
diff --git a/test/tint/statements/increment/array_element.wgsl.expected.msl b/test/tint/statements/increment/array_element.wgsl.expected.msl
index 0347508..4d41bf9 100644
--- a/test/tint/statements/increment/array_element.wgsl.expected.msl
+++ b/test/tint/statements/increment/array_element.wgsl.expected.msl
@@ -1,7 +1,20 @@
 #include <metal_stdlib>
 
 using namespace metal;
-void tint_symbol(device uint (*const tint_symbol_2)[1]) {
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
+void tint_symbol(device tint_array<uint, 1>* const tint_symbol_2) {
   (*(tint_symbol_2))[1] = ((*(tint_symbol_2))[1] + 1u);
 }
 
diff --git a/test/tint/statements/increment/complex.wgsl.expected.msl b/test/tint/statements/increment/complex.wgsl.expected.msl
index fbc0f9c..3f31988 100644
--- a/test/tint/statements/increment/complex.wgsl.expected.msl
+++ b/test/tint/statements/increment/complex.wgsl.expected.msl
@@ -1,12 +1,21 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct tint_array_wrapper {
-  /* 0x0000 */ int4 arr[4];
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
 struct S {
-  /* 0x0000 */ tint_array_wrapper a;
+  /* 0x0000 */ tint_array<int4, 4> a;
 };
 
 int idx1(thread uint* const tint_symbol_10) {
@@ -39,14 +48,14 @@
   return 2;
 }
 
-void tint_symbol_1(thread uint* const tint_symbol_16, device S (*const tint_symbol_17)[1]) {
+void tint_symbol_1(thread uint* const tint_symbol_16, device tint_array<S, 1>* const tint_symbol_17) {
   int const tint_symbol_6 = idx1(tint_symbol_16);
   int const tint_symbol_7 = idx2(tint_symbol_16);
   int const tint_symbol_2_save = tint_symbol_6;
   int const tint_symbol_2_save_1 = tint_symbol_7;
   int const tint_symbol_3 = idx3(tint_symbol_16);
   {
-    (*(tint_symbol_17))[tint_symbol_2_save].a.arr[tint_symbol_2_save_1][tint_symbol_3] = as_type<int>((as_type<uint>((*(tint_symbol_17))[tint_symbol_2_save].a.arr[tint_symbol_2_save_1][tint_symbol_3]) + as_type<uint>(1)));
+    (*(tint_symbol_17))[tint_symbol_2_save].a[tint_symbol_2_save_1][tint_symbol_3] = as_type<int>((as_type<uint>((*(tint_symbol_17))[tint_symbol_2_save].a[tint_symbol_2_save_1][tint_symbol_3]) + as_type<uint>(1)));
     while (true) {
       if (!((*(tint_symbol_16) < 10u))) {
         break;
@@ -59,7 +68,7 @@
         int const tint_symbol_4_save = tint_symbol_8;
         int const tint_symbol_4_save_1 = tint_symbol_9;
         int const tint_symbol_5 = idx6(tint_symbol_16);
-        (*(tint_symbol_17))[tint_symbol_4_save].a.arr[tint_symbol_4_save_1][tint_symbol_5] = as_type<int>((as_type<uint>((*(tint_symbol_17))[tint_symbol_4_save].a.arr[tint_symbol_4_save_1][tint_symbol_5]) + as_type<uint>(1)));
+        (*(tint_symbol_17))[tint_symbol_4_save].a[tint_symbol_4_save_1][tint_symbol_5] = as_type<int>((as_type<uint>((*(tint_symbol_17))[tint_symbol_4_save].a[tint_symbol_4_save_1][tint_symbol_5]) + as_type<uint>(1)));
       }
     }
   }
diff --git a/test/tint/struct/type_constructor.wgsl.expected.msl b/test/tint/struct/type_constructor.wgsl.expected.msl
index d0449b8..6e691c0 100644
--- a/test/tint/struct/type_constructor.wgsl.expected.msl
+++ b/test/tint/struct/type_constructor.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct S1 {
   int a;
   int b;
@@ -19,16 +32,8 @@
   S2 i;
 };
 
-struct tint_array_wrapper {
-  int arr[2];
-};
-
 struct T {
-  tint_array_wrapper a;
-};
-
-struct tint_array_wrapper_1 {
-  T arr[2];
+  tint_array<int, 2> a;
 };
 
 kernel void tint_symbol() {
@@ -58,15 +63,15 @@
   S1 const tint_symbol_12 = {.a=2, .b=42, .c=as_type<int>((as_type<uint>(42) + as_type<uint>(1))), .d=nested_nonempty.i.f.d};
   S2 const tint_symbol_13 = {.e=1, .f=tint_symbol_12};
   S1 const subexpr_nested_nonempty_with_expr = tint_symbol_13.f;
-  tint_array_wrapper_1 const aosoa_empty = {.arr={}};
-  tint_array_wrapper const tint_symbol_14 = {.arr={1, 2}};
+  tint_array<T, 2> const aosoa_empty = tint_array<T, 2>{};
+  tint_array<int, 2> const tint_symbol_14 = tint_array<int, 2>{1, 2};
   T const tint_symbol_15 = {.a=tint_symbol_14};
-  tint_array_wrapper const tint_symbol_16 = {.arr={3, 4}};
+  tint_array<int, 2> const tint_symbol_16 = tint_array<int, 2>{3, 4};
   T const tint_symbol_17 = {.a=tint_symbol_16};
-  tint_array_wrapper_1 const aosoa_nonempty = {.arr={tint_symbol_15, tint_symbol_17}};
-  tint_array_wrapper const tint_symbol_18 = {.arr={1, as_type<int>((as_type<uint>(aosoa_nonempty.arr[0].a.arr[0]) + as_type<uint>(1)))}};
+  tint_array<T, 2> const aosoa_nonempty = tint_array<T, 2>{tint_symbol_15, tint_symbol_17};
+  tint_array<int, 2> const tint_symbol_18 = tint_array<int, 2>{1, as_type<int>((as_type<uint>(aosoa_nonempty[0].a[0]) + as_type<uint>(1)))};
   T const tint_symbol_19 = {.a=tint_symbol_18};
-  tint_array_wrapper_1 const aosoa_nonempty_with_expr = {.arr={tint_symbol_19, aosoa_nonempty.arr[1]}};
+  tint_array<T, 2> const aosoa_nonempty_with_expr = tint_array<T, 2>{tint_symbol_19, aosoa_nonempty[1]};
   return;
 }
 
diff --git a/test/tint/types/function_scope_declarations.wgsl.expected.msl b/test/tint/types/function_scope_declarations.wgsl.expected.msl
index 799d28a..a7cd381 100644
--- a/test/tint/types/function_scope_declarations.wgsl.expected.msl
+++ b/test/tint/types/function_scope_declarations.wgsl.expected.msl
@@ -1,12 +1,21 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct S {
-  float a;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
-struct tint_array_wrapper {
-  float arr[4];
+struct S {
+  float a;
 };
 
 kernel void tint_symbol() {
@@ -26,8 +35,8 @@
   float4 const v4f32_let = float4(0.0f);
   float2x3 m2x3_var = float2x3(float3(0.0f), float3(0.0f));
   float3x4 const m3x4_let = float3x4(float4(0.0f), float4(0.0f), float4(0.0f));
-  tint_array_wrapper arr_var = {.arr={}};
-  tint_array_wrapper const arr_let = {.arr={}};
+  tint_array<float, 4> arr_var = tint_array<float, 4>{};
+  tint_array<float, 4> const arr_let = tint_array<float, 4>{};
   S struct_var = {};
   S const struct_let = {};
   return;
diff --git a/test/tint/types/module_scope_let.wgsl.expected.msl b/test/tint/types/module_scope_let.wgsl.expected.msl
index 33ba095..a48f863 100644
--- a/test/tint/types/module_scope_let.wgsl.expected.msl
+++ b/test/tint/types/module_scope_let.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct S {
   float a;
 };
@@ -21,11 +34,7 @@
 
 constant float3x4 m3x4_let = float3x4(float4(0.0f), float4(0.0f), float4(0.0f));
 
-struct tint_array_wrapper {
-  float arr[4];
-};
-
-constant tint_array_wrapper arr_let = {.arr={}};
+constant tint_array<float, 4> arr_let = tint_array<float, 4>{};
 
 constant S struct_let = {};
 
diff --git a/test/tint/types/module_scope_var.wgsl.expected.msl b/test/tint/types/module_scope_var.wgsl.expected.msl
index b4cb6cd..50d0091 100644
--- a/test/tint/types/module_scope_var.wgsl.expected.msl
+++ b/test/tint/types/module_scope_var.wgsl.expected.msl
@@ -1,12 +1,21 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct S {
-  float a;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
-struct tint_array_wrapper {
-  float arr[4];
+struct S {
+  float a;
 };
 
 kernel void tint_symbol() {
@@ -18,7 +27,7 @@
   thread uint3 tint_symbol_8 = 0u;
   thread float4 tint_symbol_9 = 0.0f;
   thread float2x3 tint_symbol_10 = float2x3(0.0f);
-  thread tint_array_wrapper tint_symbol_11 = {};
+  thread tint_array<float, 4> tint_symbol_11 = {};
   thread S tint_symbol_12 = {};
   tint_symbol_3 = false;
   tint_symbol_4 = 0;
@@ -28,7 +37,7 @@
   tint_symbol_8 = uint3(0u);
   tint_symbol_9 = float4(0.0f);
   tint_symbol_10 = float2x3(float3(0.0f), float3(0.0f));
-  tint_array_wrapper const tint_symbol_1 = {.arr={}};
+  tint_array<float, 4> const tint_symbol_1 = tint_array<float, 4>{};
   tint_symbol_11 = tint_symbol_1;
   S const tint_symbol_2 = {};
   tint_symbol_12 = tint_symbol_2;
diff --git a/test/tint/types/module_scope_var_initializers.wgsl.expected.msl b/test/tint/types/module_scope_var_initializers.wgsl.expected.msl
index 815f9fb..e56e653 100644
--- a/test/tint/types/module_scope_var_initializers.wgsl.expected.msl
+++ b/test/tint/types/module_scope_var_initializers.wgsl.expected.msl
@@ -1,12 +1,21 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct S {
-  float a;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
-struct tint_array_wrapper {
-  float arr[4];
+struct S {
+  float a;
 };
 
 kernel void tint_symbol() {
@@ -18,7 +27,7 @@
   thread uint3 tint_symbol_8 = uint3(0u);
   thread float4 tint_symbol_9 = float4(0.0f);
   thread float2x3 tint_symbol_10 = float2x3(float3(0.0f), float3(0.0f));
-  thread tint_array_wrapper tint_symbol_11 = {.arr={}};
+  thread tint_array<float, 4> tint_symbol_11 = tint_array<float, 4>{};
   thread S tint_symbol_12 = {};
   tint_symbol_3 = false;
   tint_symbol_4 = 0;
@@ -28,7 +37,7 @@
   tint_symbol_8 = uint3(0u);
   tint_symbol_9 = float4(0.0f);
   tint_symbol_10 = float2x3(float3(0.0f), float3(0.0f));
-  tint_array_wrapper const tint_symbol_1 = {.arr={}};
+  tint_array<float, 4> const tint_symbol_1 = tint_array<float, 4>{};
   tint_symbol_11 = tint_symbol_1;
   S const tint_symbol_2 = {};
   tint_symbol_12 = tint_symbol_2;
diff --git a/test/tint/types/parameters.wgsl.expected.msl b/test/tint/types/parameters.wgsl.expected.msl
index 3c1db25..54ad948 100644
--- a/test/tint/types/parameters.wgsl.expected.msl
+++ b/test/tint/types/parameters.wgsl.expected.msl
@@ -1,15 +1,24 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct S {
   float a;
 };
 
-struct tint_array_wrapper {
-  float arr[4];
-};
-
-void foo(bool param_bool, int param_i32, uint param_u32, float param_f32, int2 param_v2i32, uint3 param_v3u32, float4 param_v4f32, float2x3 param_m2x3, tint_array_wrapper param_arr, S param_struct, thread float* const param_ptr_f32, thread float4* const param_ptr_vec, thread tint_array_wrapper* const param_ptr_arr) {
+void foo(bool param_bool, int param_i32, uint param_u32, float param_f32, int2 param_v2i32, uint3 param_v3u32, float4 param_v4f32, float2x3 param_m2x3, tint_array<float, 4> param_arr, S param_struct, thread float* const param_ptr_f32, thread float4* const param_ptr_vec, thread tint_array<float, 4>* const param_ptr_arr) {
 }
 
 kernel void tint_symbol() {
diff --git a/test/tint/types/return_types.wgsl.expected.msl b/test/tint/types/return_types.wgsl.expected.msl
index 769204e..a0cf7b6 100644
--- a/test/tint/types/return_types.wgsl.expected.msl
+++ b/test/tint/types/return_types.wgsl.expected.msl
@@ -1,6 +1,19 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
 struct S {
   float a;
 };
@@ -37,12 +50,8 @@
   return float2x3(float3(0.0f), float3(0.0f));
 }
 
-struct tint_array_wrapper {
-  float arr[4];
-};
-
-tint_array_wrapper ret_arr() {
-  tint_array_wrapper const tint_symbol_1 = {.arr={}};
+tint_array<float, 4> ret_arr() {
+  tint_array<float, 4> const tint_symbol_1 = tint_array<float, 4>{};
   return tint_symbol_1;
 }
 
diff --git a/test/tint/types/struct_members.wgsl.expected.msl b/test/tint/types/struct_members.wgsl.expected.msl
index a43d5e0..03c6b97 100644
--- a/test/tint/types/struct_members.wgsl.expected.msl
+++ b/test/tint/types/struct_members.wgsl.expected.msl
@@ -1,12 +1,21 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct S_inner {
-  float a;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
-struct tint_array_wrapper {
-  float arr[4];
+struct S_inner {
+  float a;
 };
 
 struct S {
@@ -18,7 +27,7 @@
   uint3 member_v3u32;
   float4 member_v4f32;
   float2x3 member_m2x3;
-  tint_array_wrapper member_arr;
+  tint_array<float, 4> member_arr;
   S_inner member_struct;
 };
 
diff --git a/test/tint/var/inferred/function.wgsl.expected.msl b/test/tint/var/inferred/function.wgsl.expected.msl
index 8eebc86..8809684 100644
--- a/test/tint/var/inferred/function.wgsl.expected.msl
+++ b/test/tint/var/inferred/function.wgsl.expected.msl
@@ -1,12 +1,21 @@
 #include <metal_stdlib>
 
 using namespace metal;
-struct MyStruct {
-  float f1;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
 };
 
-struct tint_array_wrapper {
-  float arr[10];
+struct MyStruct {
+  float f1;
 };
 
 int ret_i32() {
@@ -26,8 +35,8 @@
   return tint_symbol_2;
 }
 
-tint_array_wrapper ret_MyArray() {
-  tint_array_wrapper const tint_symbol_3 = {.arr={}};
+tint_array<float, 10> ret_MyArray() {
+  tint_array<float, 10> const tint_symbol_3 = tint_array<float, 10>{};
   return tint_symbol_3;
 }
 
@@ -40,13 +49,13 @@
   float3 v6 = float3(1.0f);
   float3x3 v7 = float3x3(v6, v6, v6);
   MyStruct v8 = {.f1=1.0f};
-  tint_array_wrapper v9 = {.arr={}};
+  tint_array<float, 10> v9 = tint_array<float, 10>{};
   int v10 = ret_i32();
   uint v11 = ret_u32();
   float v12 = ret_f32();
   MyStruct v13 = ret_MyStruct();
   MyStruct v14 = ret_MyStruct();
-  tint_array_wrapper v15 = ret_MyArray();
+  tint_array<float, 10> v15 = ret_MyArray();
 }
 
 struct tint_symbol_1 {
diff --git a/test/tint/var/initialization/function/array.wgsl b/test/tint/var/initialization/function/array.wgsl
deleted file mode 100644
index 7b33431..0000000
--- a/test/tint/var/initialization/function/array.wgsl
+++ /dev/null
@@ -1,5 +0,0 @@
-@compute @workgroup_size(1)
-fn main() {
-    var v : array<i32, 3>;
-    _ = v;
-}
diff --git a/test/tint/var/initialization/function/array.wgsl.expected.glsl b/test/tint/var/initialization/function/array.wgsl.expected.glsl
deleted file mode 100644
index 645b82c..0000000
--- a/test/tint/var/initialization/function/array.wgsl.expected.glsl
+++ /dev/null
@@ -1,11 +0,0 @@
-#version 310 es
-
-void tint_symbol() {
-  int v[3] = int[3](0, 0, 0);
-}
-
-layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
-void main() {
-  tint_symbol();
-  return;
-}
diff --git a/test/tint/var/initialization/function/array.wgsl.expected.hlsl b/test/tint/var/initialization/function/array.wgsl.expected.hlsl
deleted file mode 100644
index 7f979a0..0000000
--- a/test/tint/var/initialization/function/array.wgsl.expected.hlsl
+++ /dev/null
@@ -1,5 +0,0 @@
-[numthreads(1, 1, 1)]
-void main() {
-  int v[3] = (int[3])0;
-  return;
-}
diff --git a/test/tint/var/initialization/function/array.wgsl.expected.msl b/test/tint/var/initialization/function/array.wgsl.expected.msl
deleted file mode 100644
index f441184..0000000
--- a/test/tint/var/initialization/function/array.wgsl.expected.msl
+++ /dev/null
@@ -1,12 +0,0 @@
-#include <metal_stdlib>
-
-using namespace metal;
-struct tint_array_wrapper {
-  int arr[3];
-};
-
-kernel void tint_symbol() {
-  tint_array_wrapper v = {};
-  return;
-}
-
diff --git a/test/tint/var/initialization/function/array.wgsl.expected.wgsl b/test/tint/var/initialization/function/array.wgsl.expected.wgsl
deleted file mode 100644
index fc5ed43..0000000
--- a/test/tint/var/initialization/function/array.wgsl.expected.wgsl
+++ /dev/null
@@ -1,5 +0,0 @@
-@compute @workgroup_size(1)
-fn main() {
-  var v : array<i32, 3>;
-  _ = v;
-}
diff --git a/test/tint/var/initialization/function/array/array_i32.wgsl b/test/tint/var/initialization/function/array/array_i32.wgsl
new file mode 100644
index 0000000..80db613
--- /dev/null
+++ b/test/tint/var/initialization/function/array/array_i32.wgsl
@@ -0,0 +1,5 @@
+@compute @workgroup_size(1)
+fn main() {
+    var zero : array<array<i32, 3>, 2>;
+    var init : array<array<i32, 3>, 2> = array<array<i32, 3>, 2>(array<i32, 3>(1, 2, 3), array<i32, 3>(4, 5, 6));
+}
diff --git a/test/tint/var/initialization/function/array/array_i32.wgsl.expected.glsl b/test/tint/var/initialization/function/array/array_i32.wgsl.expected.glsl
new file mode 100644
index 0000000..132ba50
--- /dev/null
+++ b/test/tint/var/initialization/function/array/array_i32.wgsl.expected.glsl
@@ -0,0 +1,14 @@
+#version 310 es
+
+void tint_symbol() {
+  int zero[2][3] = int[2][3](int[3](0, 0, 0), int[3](0, 0, 0));
+  int tint_symbol_1[3] = int[3](1, 2, 3);
+  int tint_symbol_2[3] = int[3](4, 5, 6);
+  int init[2][3] = int[2][3](tint_symbol_1, tint_symbol_2);
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  tint_symbol();
+  return;
+}
diff --git a/test/tint/var/initialization/function/array/array_i32.wgsl.expected.hlsl b/test/tint/var/initialization/function/array/array_i32.wgsl.expected.hlsl
new file mode 100644
index 0000000..88d2dd3
--- /dev/null
+++ b/test/tint/var/initialization/function/array/array_i32.wgsl.expected.hlsl
@@ -0,0 +1,8 @@
+[numthreads(1, 1, 1)]
+void main() {
+  int zero[2][3] = (int[2][3])0;
+  const int tint_symbol[3] = {1, 2, 3};
+  const int tint_symbol_1[3] = {4, 5, 6};
+  int init[2][3] = {tint_symbol, tint_symbol_1};
+  return;
+}
diff --git a/test/tint/var/initialization/function/array/array_i32.wgsl.expected.msl b/test/tint/var/initialization/function/array/array_i32.wgsl.expected.msl
new file mode 100644
index 0000000..7ad097e
--- /dev/null
+++ b/test/tint/var/initialization/function/array/array_i32.wgsl.expected.msl
@@ -0,0 +1,24 @@
+#include <metal_stdlib>
+
+using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
+kernel void tint_symbol() {
+  tint_array<tint_array<int, 3>, 2> zero = {};
+  tint_array<int, 3> const tint_symbol_1 = tint_array<int, 3>{1, 2, 3};
+  tint_array<int, 3> const tint_symbol_2 = tint_array<int, 3>{4, 5, 6};
+  tint_array<tint_array<int, 3>, 2> init = tint_array<tint_array<int, 3>, 2>{tint_symbol_1, tint_symbol_2};
+  return;
+}
+
diff --git a/test/tint/var/initialization/function/array/array_i32.wgsl.expected.spvasm b/test/tint/var/initialization/function/array/array_i32.wgsl.expected.spvasm
new file mode 100644
index 0000000..12e87cc
--- /dev/null
+++ b/test/tint/var/initialization/function/array/array_i32.wgsl.expected.spvasm
@@ -0,0 +1,40 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 24
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpName %main "main"
+               OpName %zero "zero"
+               OpName %init "init"
+               OpDecorate %_arr_int_uint_3 ArrayStride 4
+               OpDecorate %_arr__arr_int_uint_3_uint_2 ArrayStride 12
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+       %uint = OpTypeInt 32 0
+     %uint_3 = OpConstant %uint 3
+%_arr_int_uint_3 = OpTypeArray %int %uint_3
+     %uint_2 = OpConstant %uint 2
+%_arr__arr_int_uint_3_uint_2 = OpTypeArray %_arr_int_uint_3 %uint_2
+%_ptr_Function__arr__arr_int_uint_3_uint_2 = OpTypePointer Function %_arr__arr_int_uint_3_uint_2
+         %13 = OpConstantNull %_arr__arr_int_uint_3_uint_2
+      %int_1 = OpConstant %int 1
+      %int_2 = OpConstant %int 2
+      %int_3 = OpConstant %int 3
+         %17 = OpConstantComposite %_arr_int_uint_3 %int_1 %int_2 %int_3
+      %int_4 = OpConstant %int 4
+      %int_5 = OpConstant %int 5
+      %int_6 = OpConstant %int 6
+         %21 = OpConstantComposite %_arr_int_uint_3 %int_4 %int_5 %int_6
+         %22 = OpConstantComposite %_arr__arr_int_uint_3_uint_2 %17 %21
+       %main = OpFunction %void None %1
+          %4 = OpLabel
+       %zero = OpVariable %_ptr_Function__arr__arr_int_uint_3_uint_2 Function %13
+       %init = OpVariable %_ptr_Function__arr__arr_int_uint_3_uint_2 Function %13
+               OpStore %init %22
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/var/initialization/function/array/array_i32.wgsl.expected.wgsl b/test/tint/var/initialization/function/array/array_i32.wgsl.expected.wgsl
new file mode 100644
index 0000000..9bf3787
--- /dev/null
+++ b/test/tint/var/initialization/function/array/array_i32.wgsl.expected.wgsl
@@ -0,0 +1,5 @@
+@compute @workgroup_size(1)
+fn main() {
+  var zero : array<array<i32, 3>, 2>;
+  var init : array<array<i32, 3>, 2> = array<array<i32, 3>, 2>(array<i32, 3>(1, 2, 3), array<i32, 3>(4, 5, 6));
+}
diff --git a/test/tint/var/initialization/function/array/i32.wgsl b/test/tint/var/initialization/function/array/i32.wgsl
new file mode 100644
index 0000000..5e83d39
--- /dev/null
+++ b/test/tint/var/initialization/function/array/i32.wgsl
@@ -0,0 +1,5 @@
+@compute @workgroup_size(1)
+fn main() {
+    var zero : array<i32, 3>;
+    var init : array<i32, 3> = array<i32, 3>(1, 2, 3);
+}
diff --git a/test/tint/var/initialization/private/array.wgsl.expected.glsl b/test/tint/var/initialization/function/array/i32.wgsl.expected.glsl
similarity index 69%
rename from test/tint/var/initialization/private/array.wgsl.expected.glsl
rename to test/tint/var/initialization/function/array/i32.wgsl.expected.glsl
index 7bbaf79..8cc2174 100644
--- a/test/tint/var/initialization/private/array.wgsl.expected.glsl
+++ b/test/tint/var/initialization/function/array/i32.wgsl.expected.glsl
@@ -1,7 +1,8 @@
 #version 310 es
 
-int v[3] = int[3](0, 0, 0);
 void tint_symbol() {
+  int zero[3] = int[3](0, 0, 0);
+  int init[3] = int[3](1, 2, 3);
 }
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
diff --git a/test/tint/var/initialization/function/array/i32.wgsl.expected.hlsl b/test/tint/var/initialization/function/array/i32.wgsl.expected.hlsl
new file mode 100644
index 0000000..93ed2c5
--- /dev/null
+++ b/test/tint/var/initialization/function/array/i32.wgsl.expected.hlsl
@@ -0,0 +1,6 @@
+[numthreads(1, 1, 1)]
+void main() {
+  int zero[3] = (int[3])0;
+  int init[3] = {1, 2, 3};
+  return;
+}
diff --git a/test/tint/var/initialization/function/array/i32.wgsl.expected.msl b/test/tint/var/initialization/function/array/i32.wgsl.expected.msl
new file mode 100644
index 0000000..e171bf3
--- /dev/null
+++ b/test/tint/var/initialization/function/array/i32.wgsl.expected.msl
@@ -0,0 +1,22 @@
+#include <metal_stdlib>
+
+using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
+kernel void tint_symbol() {
+  tint_array<int, 3> zero = {};
+  tint_array<int, 3> init = tint_array<int, 3>{1, 2, 3};
+  return;
+}
+
diff --git a/test/tint/var/initialization/function/array.wgsl.expected.spvasm b/test/tint/var/initialization/function/array/i32.wgsl.expected.spvasm
similarity index 65%
rename from test/tint/var/initialization/function/array.wgsl.expected.spvasm
rename to test/tint/var/initialization/function/array/i32.wgsl.expected.spvasm
index 2112361..e61c1ab 100644
--- a/test/tint/var/initialization/function/array.wgsl.expected.spvasm
+++ b/test/tint/var/initialization/function/array/i32.wgsl.expected.spvasm
@@ -1,14 +1,15 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 0
-; Bound: 12
+; Bound: 17
 ; Schema: 0
                OpCapability Shader
                OpMemoryModel Logical GLSL450
                OpEntryPoint GLCompute %main "main"
                OpExecutionMode %main LocalSize 1 1 1
                OpName %main "main"
-               OpName %v "v"
+               OpName %zero "zero"
+               OpName %init "init"
                OpDecorate %_arr_int_uint_3 ArrayStride 4
        %void = OpTypeVoid
           %1 = OpTypeFunction %void
@@ -18,8 +19,14 @@
 %_arr_int_uint_3 = OpTypeArray %int %uint_3
 %_ptr_Function__arr_int_uint_3 = OpTypePointer Function %_arr_int_uint_3
          %11 = OpConstantNull %_arr_int_uint_3
+      %int_1 = OpConstant %int 1
+      %int_2 = OpConstant %int 2
+      %int_3 = OpConstant %int 3
+         %15 = OpConstantComposite %_arr_int_uint_3 %int_1 %int_2 %int_3
        %main = OpFunction %void None %1
           %4 = OpLabel
-          %v = OpVariable %_ptr_Function__arr_int_uint_3 Function %11
+       %zero = OpVariable %_ptr_Function__arr_int_uint_3 Function %11
+       %init = OpVariable %_ptr_Function__arr_int_uint_3 Function %11
+               OpStore %init %15
                OpReturn
                OpFunctionEnd
diff --git a/test/tint/var/initialization/function/array/i32.wgsl.expected.wgsl b/test/tint/var/initialization/function/array/i32.wgsl.expected.wgsl
new file mode 100644
index 0000000..70f7f20
--- /dev/null
+++ b/test/tint/var/initialization/function/array/i32.wgsl.expected.wgsl
@@ -0,0 +1,5 @@
+@compute @workgroup_size(1)
+fn main() {
+  var zero : array<i32, 3>;
+  var init : array<i32, 3> = array<i32, 3>(1, 2, 3);
+}
diff --git a/test/tint/var/initialization/private/array.wgsl b/test/tint/var/initialization/private/array.wgsl
deleted file mode 100644
index 266e4f2..0000000
--- a/test/tint/var/initialization/private/array.wgsl
+++ /dev/null
@@ -1,6 +0,0 @@
-var<private> v : array<i32, 3>;
-
-@compute @workgroup_size(1)
-fn main() {
-    _ = v;
-}
diff --git a/test/tint/var/initialization/private/array.wgsl.expected.hlsl b/test/tint/var/initialization/private/array.wgsl.expected.hlsl
deleted file mode 100644
index 5cedea9..0000000
--- a/test/tint/var/initialization/private/array.wgsl.expected.hlsl
+++ /dev/null
@@ -1,6 +0,0 @@
-static int v[3] = (int[3])0;
-
-[numthreads(1, 1, 1)]
-void main() {
-  return;
-}
diff --git a/test/tint/var/initialization/private/array.wgsl.expected.msl b/test/tint/var/initialization/private/array.wgsl.expected.msl
deleted file mode 100644
index e80edf9..0000000
--- a/test/tint/var/initialization/private/array.wgsl.expected.msl
+++ /dev/null
@@ -1,11 +0,0 @@
-#include <metal_stdlib>
-
-using namespace metal;
-struct tint_array_wrapper {
-  int arr[3];
-};
-
-kernel void tint_symbol() {
-  return;
-}
-
diff --git a/test/tint/var/initialization/private/array.wgsl.expected.spvasm b/test/tint/var/initialization/private/array.wgsl.expected.spvasm
deleted file mode 100644
index 90c0ea5..0000000
--- a/test/tint/var/initialization/private/array.wgsl.expected.spvasm
+++ /dev/null
@@ -1,25 +0,0 @@
-; SPIR-V
-; Version: 1.3
-; Generator: Google Tint Compiler; 0
-; Bound: 12
-; Schema: 0
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main"
-               OpExecutionMode %main LocalSize 1 1 1
-               OpName %v "v"
-               OpName %main "main"
-               OpDecorate %_arr_int_uint_3 ArrayStride 4
-        %int = OpTypeInt 32 1
-       %uint = OpTypeInt 32 0
-     %uint_3 = OpConstant %uint 3
-%_arr_int_uint_3 = OpTypeArray %int %uint_3
-%_ptr_Private__arr_int_uint_3 = OpTypePointer Private %_arr_int_uint_3
-          %7 = OpConstantNull %_arr_int_uint_3
-          %v = OpVariable %_ptr_Private__arr_int_uint_3 Private %7
-       %void = OpTypeVoid
-          %8 = OpTypeFunction %void
-       %main = OpFunction %void None %8
-         %11 = OpLabel
-               OpReturn
-               OpFunctionEnd
diff --git a/test/tint/var/initialization/private/array.wgsl.expected.wgsl b/test/tint/var/initialization/private/array.wgsl.expected.wgsl
deleted file mode 100644
index 478777e..0000000
--- a/test/tint/var/initialization/private/array.wgsl.expected.wgsl
+++ /dev/null
@@ -1,6 +0,0 @@
-var<private> v : array<i32, 3>;
-
-@compute @workgroup_size(1)
-fn main() {
-  _ = v;
-}
diff --git a/test/tint/var/initialization/private/array/array_i32.wgsl b/test/tint/var/initialization/private/array/array_i32.wgsl
new file mode 100644
index 0000000..91547ae
--- /dev/null
+++ b/test/tint/var/initialization/private/array/array_i32.wgsl
@@ -0,0 +1,8 @@
+var<private> zero : array<array<i32, 3>, 2>;
+var<private> init : array<array<i32, 3>, 2> = array<array<i32, 3>, 2>(array<i32, 3>(1, 2, 3), array<i32, 3>(4, 5, 6));
+
+@compute @workgroup_size(1)
+fn main() {
+    var v0 = zero;
+    var v1 = init;
+}
diff --git a/test/tint/var/initialization/private/array/array_i32.wgsl.expected.glsl b/test/tint/var/initialization/private/array/array_i32.wgsl.expected.glsl
new file mode 100644
index 0000000..9ca3db0
--- /dev/null
+++ b/test/tint/var/initialization/private/array/array_i32.wgsl.expected.glsl
@@ -0,0 +1,14 @@
+#version 310 es
+
+int zero[2][3] = int[2][3](int[3](0, 0, 0), int[3](0, 0, 0));
+int init[2][3] = int[2][3](int[3](1, 2, 3), int[3](4, 5, 6));
+void tint_symbol() {
+  int v0[2][3] = zero;
+  int v1[2][3] = init;
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  tint_symbol();
+  return;
+}
diff --git a/test/tint/var/initialization/private/array/array_i32.wgsl.expected.hlsl b/test/tint/var/initialization/private/array/array_i32.wgsl.expected.hlsl
new file mode 100644
index 0000000..aa728ab
--- /dev/null
+++ b/test/tint/var/initialization/private/array/array_i32.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+static int zero[2][3] = (int[2][3])0;
+static int init[2][3] = {{1, 2, 3}, {4, 5, 6}};
+
+[numthreads(1, 1, 1)]
+void main() {
+  int v0[2][3] = zero;
+  int v1[2][3] = init;
+  return;
+}
diff --git a/test/tint/var/initialization/private/array/array_i32.wgsl.expected.msl b/test/tint/var/initialization/private/array/array_i32.wgsl.expected.msl
new file mode 100644
index 0000000..33ba833
--- /dev/null
+++ b/test/tint/var/initialization/private/array/array_i32.wgsl.expected.msl
@@ -0,0 +1,24 @@
+#include <metal_stdlib>
+
+using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
+kernel void tint_symbol() {
+  thread tint_array<tint_array<int, 3>, 2> tint_symbol_1 = {};
+  thread tint_array<tint_array<int, 3>, 2> tint_symbol_2 = tint_array<tint_array<int, 3>, 2>{tint_array<int, 3>{1, 2, 3}, tint_array<int, 3>{4, 5, 6}};
+  tint_array<tint_array<int, 3>, 2> v0 = tint_symbol_1;
+  tint_array<tint_array<int, 3>, 2> v1 = tint_symbol_2;
+  return;
+}
+
diff --git a/test/tint/var/initialization/private/array/array_i32.wgsl.expected.spvasm b/test/tint/var/initialization/private/array/array_i32.wgsl.expected.spvasm
new file mode 100644
index 0000000..81e5d8b
--- /dev/null
+++ b/test/tint/var/initialization/private/array/array_i32.wgsl.expected.spvasm
@@ -0,0 +1,48 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 29
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpName %zero "zero"
+               OpName %init "init"
+               OpName %main "main"
+               OpName %v0 "v0"
+               OpName %v1 "v1"
+               OpDecorate %_arr_int_uint_3 ArrayStride 4
+               OpDecorate %_arr__arr_int_uint_3_uint_2 ArrayStride 12
+        %int = OpTypeInt 32 1
+       %uint = OpTypeInt 32 0
+     %uint_3 = OpConstant %uint 3
+%_arr_int_uint_3 = OpTypeArray %int %uint_3
+     %uint_2 = OpConstant %uint 2
+%_arr__arr_int_uint_3_uint_2 = OpTypeArray %_arr_int_uint_3 %uint_2
+%_ptr_Private__arr__arr_int_uint_3_uint_2 = OpTypePointer Private %_arr__arr_int_uint_3_uint_2
+          %9 = OpConstantNull %_arr__arr_int_uint_3_uint_2
+       %zero = OpVariable %_ptr_Private__arr__arr_int_uint_3_uint_2 Private %9
+      %int_1 = OpConstant %int 1
+      %int_2 = OpConstant %int 2
+      %int_3 = OpConstant %int 3
+         %13 = OpConstantComposite %_arr_int_uint_3 %int_1 %int_2 %int_3
+      %int_4 = OpConstant %int 4
+      %int_5 = OpConstant %int 5
+      %int_6 = OpConstant %int 6
+         %17 = OpConstantComposite %_arr_int_uint_3 %int_4 %int_5 %int_6
+         %18 = OpConstantComposite %_arr__arr_int_uint_3_uint_2 %13 %17
+       %init = OpVariable %_ptr_Private__arr__arr_int_uint_3_uint_2 Private %18
+       %void = OpTypeVoid
+         %20 = OpTypeFunction %void
+%_ptr_Function__arr__arr_int_uint_3_uint_2 = OpTypePointer Function %_arr__arr_int_uint_3_uint_2
+       %main = OpFunction %void None %20
+         %23 = OpLabel
+         %v0 = OpVariable %_ptr_Function__arr__arr_int_uint_3_uint_2 Function %9
+         %v1 = OpVariable %_ptr_Function__arr__arr_int_uint_3_uint_2 Function %9
+         %24 = OpLoad %_arr__arr_int_uint_3_uint_2 %zero
+               OpStore %v0 %24
+         %27 = OpLoad %_arr__arr_int_uint_3_uint_2 %init
+               OpStore %v1 %27
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/var/initialization/private/array/array_i32.wgsl.expected.wgsl b/test/tint/var/initialization/private/array/array_i32.wgsl.expected.wgsl
new file mode 100644
index 0000000..4480574
--- /dev/null
+++ b/test/tint/var/initialization/private/array/array_i32.wgsl.expected.wgsl
@@ -0,0 +1,9 @@
+var<private> zero : array<array<i32, 3>, 2>;
+
+var<private> init : array<array<i32, 3>, 2> = array<array<i32, 3>, 2>(array<i32, 3>(1, 2, 3), array<i32, 3>(4, 5, 6));
+
+@compute @workgroup_size(1)
+fn main() {
+  var v0 = zero;
+  var v1 = init;
+}
diff --git a/test/tint/var/initialization/private/array/i32.wgsl b/test/tint/var/initialization/private/array/i32.wgsl
new file mode 100644
index 0000000..c17920a
--- /dev/null
+++ b/test/tint/var/initialization/private/array/i32.wgsl
@@ -0,0 +1,8 @@
+var<private> zero : array<i32, 3>;
+var<private> init : array<i32, 3> = array<i32, 3>(1, 2, 3);
+
+@compute @workgroup_size(1)
+fn main() {
+    var v0 = zero;
+    var v1 = init;
+}
diff --git a/test/tint/var/initialization/private/array/i32.wgsl.expected.glsl b/test/tint/var/initialization/private/array/i32.wgsl.expected.glsl
new file mode 100644
index 0000000..956d196
--- /dev/null
+++ b/test/tint/var/initialization/private/array/i32.wgsl.expected.glsl
@@ -0,0 +1,14 @@
+#version 310 es
+
+int zero[3] = int[3](0, 0, 0);
+int init[3] = int[3](1, 2, 3);
+void tint_symbol() {
+  int v0[3] = zero;
+  int v1[3] = init;
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  tint_symbol();
+  return;
+}
diff --git a/test/tint/var/initialization/private/array/i32.wgsl.expected.hlsl b/test/tint/var/initialization/private/array/i32.wgsl.expected.hlsl
new file mode 100644
index 0000000..e4cc4bc
--- /dev/null
+++ b/test/tint/var/initialization/private/array/i32.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+static int zero[3] = (int[3])0;
+static int init[3] = {1, 2, 3};
+
+[numthreads(1, 1, 1)]
+void main() {
+  int v0[3] = zero;
+  int v1[3] = init;
+  return;
+}
diff --git a/test/tint/var/initialization/private/array/i32.wgsl.expected.msl b/test/tint/var/initialization/private/array/i32.wgsl.expected.msl
new file mode 100644
index 0000000..60dac04
--- /dev/null
+++ b/test/tint/var/initialization/private/array/i32.wgsl.expected.msl
@@ -0,0 +1,24 @@
+#include <metal_stdlib>
+
+using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
+kernel void tint_symbol() {
+  thread tint_array<int, 3> tint_symbol_1 = {};
+  thread tint_array<int, 3> tint_symbol_2 = tint_array<int, 3>{1, 2, 3};
+  tint_array<int, 3> v0 = tint_symbol_1;
+  tint_array<int, 3> v1 = tint_symbol_2;
+  return;
+}
+
diff --git a/test/tint/var/initialization/private/array/i32.wgsl.expected.spvasm b/test/tint/var/initialization/private/array/i32.wgsl.expected.spvasm
new file mode 100644
index 0000000..0ca3cd6
--- /dev/null
+++ b/test/tint/var/initialization/private/array/i32.wgsl.expected.spvasm
@@ -0,0 +1,40 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 22
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpName %zero "zero"
+               OpName %init "init"
+               OpName %main "main"
+               OpName %v0 "v0"
+               OpName %v1 "v1"
+               OpDecorate %_arr_int_uint_3 ArrayStride 4
+        %int = OpTypeInt 32 1
+       %uint = OpTypeInt 32 0
+     %uint_3 = OpConstant %uint 3
+%_arr_int_uint_3 = OpTypeArray %int %uint_3
+%_ptr_Private__arr_int_uint_3 = OpTypePointer Private %_arr_int_uint_3
+          %7 = OpConstantNull %_arr_int_uint_3
+       %zero = OpVariable %_ptr_Private__arr_int_uint_3 Private %7
+      %int_1 = OpConstant %int 1
+      %int_2 = OpConstant %int 2
+      %int_3 = OpConstant %int 3
+         %11 = OpConstantComposite %_arr_int_uint_3 %int_1 %int_2 %int_3
+       %init = OpVariable %_ptr_Private__arr_int_uint_3 Private %11
+       %void = OpTypeVoid
+         %13 = OpTypeFunction %void
+%_ptr_Function__arr_int_uint_3 = OpTypePointer Function %_arr_int_uint_3
+       %main = OpFunction %void None %13
+         %16 = OpLabel
+         %v0 = OpVariable %_ptr_Function__arr_int_uint_3 Function %7
+         %v1 = OpVariable %_ptr_Function__arr_int_uint_3 Function %7
+         %17 = OpLoad %_arr_int_uint_3 %zero
+               OpStore %v0 %17
+         %20 = OpLoad %_arr_int_uint_3 %init
+               OpStore %v1 %20
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/var/initialization/private/array/i32.wgsl.expected.wgsl b/test/tint/var/initialization/private/array/i32.wgsl.expected.wgsl
new file mode 100644
index 0000000..59f9448
--- /dev/null
+++ b/test/tint/var/initialization/private/array/i32.wgsl.expected.wgsl
@@ -0,0 +1,9 @@
+var<private> zero : array<i32, 3>;
+
+var<private> init : array<i32, 3> = array<i32, 3>(1, 2, 3);
+
+@compute @workgroup_size(1)
+fn main() {
+  var v0 = zero;
+  var v1 = init;
+}
diff --git a/test/tint/var/initialization/workgroup/array.wgsl b/test/tint/var/initialization/workgroup/array.wgsl
deleted file mode 100644
index f3b6972..0000000
--- a/test/tint/var/initialization/workgroup/array.wgsl
+++ /dev/null
@@ -1,6 +0,0 @@
-var<workgroup> v : array<i32, 3>;
-
-@compute @workgroup_size(1)
-fn main() {
-    _ = v;
-}
diff --git a/test/tint/var/initialization/workgroup/array.wgsl.expected.msl b/test/tint/var/initialization/workgroup/array.wgsl.expected.msl
deleted file mode 100644
index 9157d88..0000000
--- a/test/tint/var/initialization/workgroup/array.wgsl.expected.msl
+++ /dev/null
@@ -1,21 +0,0 @@
-#include <metal_stdlib>
-
-using namespace metal;
-struct tint_array_wrapper {
-  int arr[3];
-};
-
-void tint_symbol_inner(uint local_invocation_index, threadgroup tint_array_wrapper* const tint_symbol_1) {
-  for(uint idx = local_invocation_index; (idx < 3u); idx = (idx + 1u)) {
-    uint const i = idx;
-    (*(tint_symbol_1)).arr[i] = 0;
-  }
-  threadgroup_barrier(mem_flags::mem_threadgroup);
-}
-
-kernel void tint_symbol(uint local_invocation_index [[thread_index_in_threadgroup]]) {
-  threadgroup tint_array_wrapper tint_symbol_2;
-  tint_symbol_inner(local_invocation_index, &(tint_symbol_2));
-  return;
-}
-
diff --git a/test/tint/var/initialization/workgroup/array.wgsl.expected.wgsl b/test/tint/var/initialization/workgroup/array.wgsl.expected.wgsl
deleted file mode 100644
index d1e084b..0000000
--- a/test/tint/var/initialization/workgroup/array.wgsl.expected.wgsl
+++ /dev/null
@@ -1,6 +0,0 @@
-var<workgroup> v : array<i32, 3>;
-
-@compute @workgroup_size(1)
-fn main() {
-  _ = v;
-}
diff --git a/test/tint/var/initialization/workgroup/array/array_i32.wgsl b/test/tint/var/initialization/workgroup/array/array_i32.wgsl
new file mode 100644
index 0000000..d7ba62c
--- /dev/null
+++ b/test/tint/var/initialization/workgroup/array/array_i32.wgsl
@@ -0,0 +1,6 @@
+var<workgroup> zero : array<array<i32, 3>, 2>;
+
+@compute @workgroup_size(1)
+fn main() {
+    var v = zero;
+}
diff --git a/test/tint/var/initialization/workgroup/array/array_i32.wgsl.expected.glsl b/test/tint/var/initialization/workgroup/array/array_i32.wgsl.expected.glsl
new file mode 100644
index 0000000..a3ba5e2
--- /dev/null
+++ b/test/tint/var/initialization/workgroup/array/array_i32.wgsl.expected.glsl
@@ -0,0 +1,20 @@
+#version 310 es
+
+shared int zero[2][3];
+void tint_symbol(uint local_invocation_index) {
+  {
+    for(uint idx = local_invocation_index; (idx < 6u); idx = (idx + 1u)) {
+      uint i = (idx / 3u);
+      uint i_1 = (idx % 3u);
+      zero[i][i_1] = 0;
+    }
+  }
+  barrier();
+  int v[2][3] = zero;
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  tint_symbol(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/var/initialization/workgroup/array.wgsl.expected.hlsl b/test/tint/var/initialization/workgroup/array/array_i32.wgsl.expected.hlsl
similarity index 60%
copy from test/tint/var/initialization/workgroup/array.wgsl.expected.hlsl
copy to test/tint/var/initialization/workgroup/array/array_i32.wgsl.expected.hlsl
index 21c4cab..0bcec72 100644
--- a/test/tint/var/initialization/workgroup/array.wgsl.expected.hlsl
+++ b/test/tint/var/initialization/workgroup/array/array_i32.wgsl.expected.hlsl
@@ -1,4 +1,4 @@
-groupshared int v[3];
+groupshared int zero[2][3];
 
 struct tint_symbol_1 {
   uint local_invocation_index : SV_GroupIndex;
@@ -6,12 +6,14 @@
 
 void main_inner(uint local_invocation_index) {
   {
-    [loop] for(uint idx = local_invocation_index; (idx < 3u); idx = (idx + 1u)) {
-      const uint i = idx;
-      v[i] = 0;
+    [loop] for(uint idx = local_invocation_index; (idx < 6u); idx = (idx + 1u)) {
+      const uint i = (idx / 3u);
+      const uint i_1 = (idx % 3u);
+      zero[i][i_1] = 0;
     }
   }
   GroupMemoryBarrierWithGroupSync();
+  int v[2][3] = zero;
 }
 
 [numthreads(1, 1, 1)]
diff --git a/test/tint/var/initialization/workgroup/array/array_i32.wgsl.expected.msl b/test/tint/var/initialization/workgroup/array/array_i32.wgsl.expected.msl
new file mode 100644
index 0000000..81c7619
--- /dev/null
+++ b/test/tint/var/initialization/workgroup/array/array_i32.wgsl.expected.msl
@@ -0,0 +1,32 @@
+#include <metal_stdlib>
+
+using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
+void tint_symbol_inner(uint local_invocation_index, threadgroup tint_array<tint_array<int, 3>, 2>* const tint_symbol_1) {
+  for(uint idx = local_invocation_index; (idx < 6u); idx = (idx + 1u)) {
+    uint const i = (idx / 3u);
+    uint const i_1 = (idx % 3u);
+    (*(tint_symbol_1))[i][i_1] = 0;
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  tint_array<tint_array<int, 3>, 2> v = *(tint_symbol_1);
+}
+
+kernel void tint_symbol(uint local_invocation_index [[thread_index_in_threadgroup]]) {
+  threadgroup tint_array<tint_array<int, 3>, 2> tint_symbol_2;
+  tint_symbol_inner(local_invocation_index, &(tint_symbol_2));
+  return;
+}
+
diff --git a/test/tint/var/initialization/workgroup/array/array_i32.wgsl.expected.spvasm b/test/tint/var/initialization/workgroup/array/array_i32.wgsl.expected.spvasm
new file mode 100644
index 0000000..ff32eed
--- /dev/null
+++ b/test/tint/var/initialization/workgroup/array/array_i32.wgsl.expected.spvasm
@@ -0,0 +1,85 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 51
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main" %local_invocation_index_1
+               OpExecutionMode %main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %zero "zero"
+               OpName %main_inner "main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %idx "idx"
+               OpName %v "v"
+               OpName %main "main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+               OpDecorate %_arr_int_uint_3 ArrayStride 4
+               OpDecorate %_arr__arr_int_uint_3_uint_2 ArrayStride 12
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+     %uint_3 = OpConstant %uint 3
+%_arr_int_uint_3 = OpTypeArray %int %uint_3
+     %uint_2 = OpConstant %uint 2
+%_arr__arr_int_uint_3_uint_2 = OpTypeArray %_arr_int_uint_3 %uint_2
+%_ptr_Workgroup__arr__arr_int_uint_3_uint_2 = OpTypePointer Workgroup %_arr__arr_int_uint_3_uint_2
+       %zero = OpVariable %_ptr_Workgroup__arr__arr_int_uint_3_uint_2 Workgroup
+       %void = OpTypeVoid
+         %11 = OpTypeFunction %void %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %18 = OpConstantNull %uint
+     %uint_6 = OpConstant %uint 6
+       %bool = OpTypeBool
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+         %36 = OpConstantNull %int
+     %uint_1 = OpConstant %uint 1
+   %uint_264 = OpConstant %uint 264
+%_ptr_Function__arr__arr_int_uint_3_uint_2 = OpTypePointer Function %_arr__arr_int_uint_3_uint_2
+         %45 = OpConstantNull %_arr__arr_int_uint_3_uint_2
+         %46 = OpTypeFunction %void
+ %main_inner = OpFunction %void None %11
+%local_invocation_index = OpFunctionParameter %uint
+         %15 = OpLabel
+        %idx = OpVariable %_ptr_Function_uint Function %18
+          %v = OpVariable %_ptr_Function__arr__arr_int_uint_3_uint_2 Function %45
+               OpStore %idx %local_invocation_index
+               OpBranch %19
+         %19 = OpLabel
+               OpLoopMerge %20 %21 None
+               OpBranch %22
+         %22 = OpLabel
+         %24 = OpLoad %uint %idx
+         %26 = OpULessThan %bool %24 %uint_6
+         %23 = OpLogicalNot %bool %26
+               OpSelectionMerge %28 None
+               OpBranchConditional %23 %29 %28
+         %29 = OpLabel
+               OpBranch %20
+         %28 = OpLabel
+         %30 = OpLoad %uint %idx
+         %31 = OpUDiv %uint %30 %uint_3
+         %32 = OpLoad %uint %idx
+         %33 = OpUMod %uint %32 %uint_3
+         %35 = OpAccessChain %_ptr_Workgroup_int %zero %31 %33
+               OpStore %35 %36
+               OpBranch %21
+         %21 = OpLabel
+         %37 = OpLoad %uint %idx
+         %39 = OpIAdd %uint %37 %uint_1
+               OpStore %idx %39
+               OpBranch %19
+         %20 = OpLabel
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %42 = OpLoad %_arr__arr_int_uint_3_uint_2 %zero
+               OpStore %v %42
+               OpReturn
+               OpFunctionEnd
+       %main = OpFunction %void None %46
+         %48 = OpLabel
+         %50 = OpLoad %uint %local_invocation_index_1
+         %49 = OpFunctionCall %void %main_inner %50
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/var/initialization/workgroup/array/array_i32.wgsl.expected.wgsl b/test/tint/var/initialization/workgroup/array/array_i32.wgsl.expected.wgsl
new file mode 100644
index 0000000..f3d6c3c
--- /dev/null
+++ b/test/tint/var/initialization/workgroup/array/array_i32.wgsl.expected.wgsl
@@ -0,0 +1,6 @@
+var<workgroup> zero : array<array<i32, 3>, 2>;
+
+@compute @workgroup_size(1)
+fn main() {
+  var v = zero;
+}
diff --git a/test/tint/var/initialization/workgroup/array/i32.wgsl b/test/tint/var/initialization/workgroup/array/i32.wgsl
new file mode 100644
index 0000000..2b910b3
--- /dev/null
+++ b/test/tint/var/initialization/workgroup/array/i32.wgsl
@@ -0,0 +1,6 @@
+var<workgroup> zero : array<i32, 3>;
+
+@compute @workgroup_size(1)
+fn main() {
+    var v = zero;
+}
diff --git a/test/tint/var/initialization/workgroup/array.wgsl.expected.glsl b/test/tint/var/initialization/workgroup/array/i32.wgsl.expected.glsl
similarity index 84%
rename from test/tint/var/initialization/workgroup/array.wgsl.expected.glsl
rename to test/tint/var/initialization/workgroup/array/i32.wgsl.expected.glsl
index 0cb7390..b2191b1 100644
--- a/test/tint/var/initialization/workgroup/array.wgsl.expected.glsl
+++ b/test/tint/var/initialization/workgroup/array/i32.wgsl.expected.glsl
@@ -1,14 +1,15 @@
 #version 310 es
 
-shared int v[3];
+shared int zero[3];
 void tint_symbol(uint local_invocation_index) {
   {
     for(uint idx = local_invocation_index; (idx < 3u); idx = (idx + 1u)) {
       uint i = idx;
-      v[i] = 0;
+      zero[i] = 0;
     }
   }
   barrier();
+  int v[3] = zero;
 }
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
diff --git a/test/tint/var/initialization/workgroup/array.wgsl.expected.hlsl b/test/tint/var/initialization/workgroup/array/i32.wgsl.expected.hlsl
similarity index 86%
rename from test/tint/var/initialization/workgroup/array.wgsl.expected.hlsl
rename to test/tint/var/initialization/workgroup/array/i32.wgsl.expected.hlsl
index 21c4cab..15e697a 100644
--- a/test/tint/var/initialization/workgroup/array.wgsl.expected.hlsl
+++ b/test/tint/var/initialization/workgroup/array/i32.wgsl.expected.hlsl
@@ -1,4 +1,4 @@
-groupshared int v[3];
+groupshared int zero[3];
 
 struct tint_symbol_1 {
   uint local_invocation_index : SV_GroupIndex;
@@ -8,10 +8,11 @@
   {
     [loop] for(uint idx = local_invocation_index; (idx < 3u); idx = (idx + 1u)) {
       const uint i = idx;
-      v[i] = 0;
+      zero[i] = 0;
     }
   }
   GroupMemoryBarrierWithGroupSync();
+  int v[3] = zero;
 }
 
 [numthreads(1, 1, 1)]
diff --git a/test/tint/var/initialization/workgroup/array/i32.wgsl.expected.msl b/test/tint/var/initialization/workgroup/array/i32.wgsl.expected.msl
new file mode 100644
index 0000000..5b98f4a
--- /dev/null
+++ b/test/tint/var/initialization/workgroup/array/i32.wgsl.expected.msl
@@ -0,0 +1,31 @@
+#include <metal_stdlib>
+
+using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+    const constant T& operator[](size_t i) const constant { return elements[i]; }
+    device T& operator[](size_t i) device { return elements[i]; }
+    const device T& operator[](size_t i) const device { return elements[i]; }
+    thread T& operator[](size_t i) thread { return elements[i]; }
+    const thread T& operator[](size_t i) const thread { return elements[i]; }
+    threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+    const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+    T elements[N];
+};
+
+void tint_symbol_inner(uint local_invocation_index, threadgroup tint_array<int, 3>* const tint_symbol_1) {
+  for(uint idx = local_invocation_index; (idx < 3u); idx = (idx + 1u)) {
+    uint const i = idx;
+    (*(tint_symbol_1))[i] = 0;
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  tint_array<int, 3> v = *(tint_symbol_1);
+}
+
+kernel void tint_symbol(uint local_invocation_index [[thread_index_in_threadgroup]]) {
+  threadgroup tint_array<int, 3> tint_symbol_2;
+  tint_symbol_inner(local_invocation_index, &(tint_symbol_2));
+  return;
+}
+
diff --git a/test/tint/var/initialization/workgroup/array.wgsl.expected.spvasm b/test/tint/var/initialization/workgroup/array/i32.wgsl.expected.spvasm
similarity index 79%
rename from test/tint/var/initialization/workgroup/array.wgsl.expected.spvasm
rename to test/tint/var/initialization/workgroup/array/i32.wgsl.expected.spvasm
index 97682d3..59c3e27 100644
--- a/test/tint/var/initialization/workgroup/array.wgsl.expected.spvasm
+++ b/test/tint/var/initialization/workgroup/array/i32.wgsl.expected.spvasm
@@ -1,17 +1,18 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 0
-; Bound: 42
+; Bound: 46
 ; Schema: 0
                OpCapability Shader
                OpMemoryModel Logical GLSL450
                OpEntryPoint GLCompute %main "main" %local_invocation_index_1
                OpExecutionMode %main LocalSize 1 1 1
                OpName %local_invocation_index_1 "local_invocation_index_1"
-               OpName %v "v"
+               OpName %zero "zero"
                OpName %main_inner "main_inner"
                OpName %local_invocation_index "local_invocation_index"
                OpName %idx "idx"
+               OpName %v "v"
                OpName %main "main"
                OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
                OpDecorate %_arr_int_uint_3 ArrayStride 4
@@ -22,7 +23,7 @@
      %uint_3 = OpConstant %uint 3
 %_arr_int_uint_3 = OpTypeArray %int %uint_3
 %_ptr_Workgroup__arr_int_uint_3 = OpTypePointer Workgroup %_arr_int_uint_3
-          %v = OpVariable %_ptr_Workgroup__arr_int_uint_3 Workgroup
+       %zero = OpVariable %_ptr_Workgroup__arr_int_uint_3 Workgroup
        %void = OpTypeVoid
           %9 = OpTypeFunction %void %uint
 %_ptr_Function_uint = OpTypePointer Function %uint
@@ -33,11 +34,14 @@
      %uint_1 = OpConstant %uint 1
      %uint_2 = OpConstant %uint 2
    %uint_264 = OpConstant %uint 264
-         %37 = OpTypeFunction %void
+%_ptr_Function__arr_int_uint_3 = OpTypePointer Function %_arr_int_uint_3
+         %40 = OpConstantNull %_arr_int_uint_3
+         %41 = OpTypeFunction %void
  %main_inner = OpFunction %void None %9
 %local_invocation_index = OpFunctionParameter %uint
          %13 = OpLabel
         %idx = OpVariable %_ptr_Function_uint Function %16
+          %v = OpVariable %_ptr_Function__arr_int_uint_3 Function %40
                OpStore %idx %local_invocation_index
                OpBranch %17
          %17 = OpLabel
@@ -53,7 +57,7 @@
                OpBranch %18
          %25 = OpLabel
          %27 = OpLoad %uint %idx
-         %29 = OpAccessChain %_ptr_Workgroup_int %v %27
+         %29 = OpAccessChain %_ptr_Workgroup_int %zero %27
                OpStore %29 %30
                OpBranch %19
          %19 = OpLabel
@@ -63,11 +67,13 @@
                OpBranch %17
          %18 = OpLabel
                OpControlBarrier %uint_2 %uint_2 %uint_264
+         %37 = OpLoad %_arr_int_uint_3 %zero
+               OpStore %v %37
                OpReturn
                OpFunctionEnd
-       %main = OpFunction %void None %37
-         %39 = OpLabel
-         %41 = OpLoad %uint %local_invocation_index_1
-         %40 = OpFunctionCall %void %main_inner %41
+       %main = OpFunction %void None %41
+         %43 = OpLabel
+         %45 = OpLoad %uint %local_invocation_index_1
+         %44 = OpFunctionCall %void %main_inner %45
                OpReturn
                OpFunctionEnd
diff --git a/test/tint/var/initialization/workgroup/array/i32.wgsl.expected.wgsl b/test/tint/var/initialization/workgroup/array/i32.wgsl.expected.wgsl
new file mode 100644
index 0000000..8f7cdf6
--- /dev/null
+++ b/test/tint/var/initialization/workgroup/array/i32.wgsl.expected.wgsl
@@ -0,0 +1,6 @@
+var<workgroup> zero : array<i32, 3>;
+
+@compute @workgroup_size(1)
+fn main() {
+  var v = zero;
+}